Skip to main content

WAF protection

The Static files scope can attach an existing AWS WAF WebACL to its CloudFront distribution, adding an extra security layer in front of your assets. The feature is opt-in and adds no IAM overhead unless enabled.

When to use it

Attach a WebACL when you need:

  • Managed AWS rules (common rule set, IP reputation lists, bot control)
  • Rate-based rules per IP
  • Custom request filtering or geo restrictions
  • WAF logging to S3, CloudWatch, or Firehose

If you only need basic HTTPS and edge caching, leave the WAF off.

Limits

The integration is intentionally narrow to keep the configuration simple. The following are out of scope today:

LimitDetail
WAF versionOnly AWS WAFv2 is supported. WAF Classic (aws_waf_*) is not.
AccountSame-account only. The WebACL must live in the same AWS account that the agent authenticates against.
ScopeThe WebACL must have scope=CLOUDFRONT. AWS confines those WebACLs to us-east-1, so the WebACL must live in us-east-1.
LifecycleThe scope attaches an existing WebACL by name. It does not create, update, or delete WebACL resources.

If you need any of the above, manage the WebACL outside the scope and reference it by name once it exists.

Configuration

Add a security block to the attributes of your scope-configurations provider:

attributes = {
cloud_provider = "aws"
provider = { ... }
network = { ... }
distribution = { aws_distribution = "cloudfront" }

security = {
aws_security = "waf"
aws_web_acl_name = "my-web-acl"
}
}
FieldValuesDescription
security.aws_security"none" (default) or "waf"Choose whether to attach a WebACL. "none" skips the WAF integration entirely.
security.aws_web_acl_namestringName of an existing WAFv2 WebACL with scope=CLOUDFRONT in us-east-1. Required when aws_security="waf".

The same fields are also available in the nullplatform UI under the Security tab of the scope configuration.

IAM permissions

When aws_security="waf", the agent needs two additional permissions on top of the ones documented in AWS setup:

{
"Sid": "WAFv2WebACLLookup",
"Effect": "Allow",
"Action": [
"wafv2:ListWebACLs",
"wafv2:GetWebACL"
],
"Resource": "arn:aws:wafv2:us-east-1:YOUR_ACCOUNT_ID:global/webacl/*/*"
}

ListWebACLs is called by the scope's setup script to validate the WebACL name before running OpenTofu. GetWebACL is called by the OpenTofu data source that resolves the name to an ARN.

When aws_security="none", neither action is invoked, so the policy above is only needed in clusters that actually use the WAF feature.

Operational behavior

Attaching or detaching a WebACL is always an in-place update of the CloudFront distribution. The distribution ID stays stable across the change, no DNS or edge propagation reset is required, and there is no downtime.

  • Attach: setting aws_security="waf" with a valid name on an existing scope triggers a distribution update that sets WebACLId to the WebACL ARN. The scope is redeployed normally.
  • Detach: setting aws_security="none" (or clearing the name) on the next deployment sets WebACLId back to empty. The distribution keeps the same ID.
  • Swap: changing aws_web_acl_name to a different existing WebACL re-points WebACLId to the new ARN, also in-place.
note

The configuration change in the provider does not by itself update CloudFront. You need to trigger a deployment of the scope (initial, blue-green, or any redeploy) for the agent to apply the new value.

Validation and errors

Before running OpenTofu, the scope's setup script validates that the WebACL exists by calling wafv2:ListWebACLs with scope=CLOUDFRONT in us-east-1. If the name is misspelled, lives in a different account, or has the wrong scope, the deployment fails fast with a message that points to the most common causes:

  • Name misspelled
  • WebACL exists but with scope=REGIONAL instead of CLOUDFRONT
  • WebACL lives in a different AWS account
  • Agent role lacks wafv2:ListWebACLs

Fix the underlying issue and redeploy. No partial state is left behind because the failure happens before the OpenTofu apply.