WAF Module
A Terraform module for creating and managing AWS WAFv2 Web ACLs. This module wraps the cloudposse/waf/aws module and provides a simplified interface for common WAF configurations.
Features
- Create regional or CloudFront WAF Web ACLs
- Configure managed rule groups with customizable actions
- Support for various rule types (byte match, rate-based, geo match, etc.)
- Automatic CloudWatch logging
- Header-based rule bypass for authorized traffic
Header-Based Rule Bypass
The module supports bypassing specific WAF rules when requests contain a designated header with a secret value. This is useful for allowing trusted services (e.g., load testing tools, internal services) to bypass certain rules.
How It Works
- The module generates a random secret and stores it in AWS Parameter Store
- WAF rules specified in
rules_to_bypassare configured with scope-down statements - Requests containing the bypass header with the correct secret value skip those rules
- Requests without the header (or with incorrect values) are evaluated normally
Usage Example
module "waf" {
source = "./modules/wafv2"
name = "my-application-waf"
create = true
bypass_config = {
header_name = "X-WAF-Bypass"
parameter_store_name = "/myapp/waf/bypass-secret"
rules_to_bypass = [
"AWS-AWSManagedRulesAnonymousIpList",
"AWS-AWSManagedRulesAmazonIpReputationList"
]
}
}
Retrieving the Bypass Secret
Services that need to bypass WAF rules must retrieve the secret from Parameter Store. Grant IAM permissions to the parameter ARN output:
# IAM policy for retrieving the bypass secret
data "aws_iam_policy_document" "waf_bypass" {
statement {
actions = ["ssm:GetParameter"]
resources = [module.waf.bypass_parameter_arn]
}
}
Then retrieve the secret using the AWS SDK or CLI:
aws ssm get-parameter --name "/myapp/waf/bypass-secret" --with-decryption --query "Parameter.Value" --output text
Include the secret in requests as a header:
Modules
| Name | Source | Version |
|---|---|---|
| bypass_secret | ./modules/random_password | n/a |
| waf | ./modules/wafv2-cloudposse-fork | n/a |
Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| additional_managed_rule_groups | Extra managed rule groups to enable on top of the built-in set, for use cases the defaults do not cover (for example WordPress, PHP or Linux workloads). Opt-in: leaving this empty keeps the web ACL exactly as before. Each entry supports: name - Managed rule group name, e.g. "AWSManagedRulesWordPressRuleSet". vendor_name - Defaults to "AWS". priority - Optional. Auto-assigned from 8 upwards, in list order, when omitted. Keep any byte_match/rate_based/regex rule priorities clear of that range. version - Optional pinned rule group version. Defaults to the latest. The resulting rules are named "AWS- exclude_rules andrule_action_override exactly like the built-in rule groups.Note: most AWS managed rule groups are free, but some (BotControl, ATP, ACFP) carry an extra charge. See: https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html Example: additional_managed_rule_groups = [ { name = "AWSManagedRulesWordPressRuleSet" }, { name = "AWSManagedRulesPHPRuleSet" }, ] |
list(object({ |
[] |
no |
| association_arns | List of ARNs of the resources to associate with the WAF | list(string) |
[] |
no |
| bypass_config | Configuration for header-based rule bypass. When configured, specific WAF rules can be bypassed if a request contains the designated header with a secret value stored in Parameter Store. | object({ |
null |
no |
| byte_match_statement_rules | Byte Match Statement Rules | list(any) |
[] |
no |
| cloudwatch_metrics_enabled | Whether the associated resource sends metrics to CloudWatch | bool |
true |
no |
| create | Create WAF | bool |
n/a | yes |
| create_log_group | Create CloudWatch Log Group. Only applies when log_destination_type is 'cloudwatch'. | bool |
true |
no |
| create_s3_log_bucket | Create S3 bucket for WAF logs. Only applies when log_destination_type is 's3'. | bool |
true |
no |
| default_action | Default action for the WAF, either allow or block | string |
"allow" |
no |
| exclude_rules | List of rules to exclude | list(string) |
[] |
no |
| geo_allowlist_statement_rules | Geo Allowlist Statement Rules | list(any) |
[] |
no |
| geo_match_statement_rules | Geo Match Statement Rules | list(any) |
[] |
no |
| ip_set_reference_statement_rules | IP Set Reference Statement Rules | list(any) |
[] |
no |
| log_destination_configs | The Amazon Kinesis Data Firehose, CloudWatch Log log group, or S3 bucket Amazon Resource Names (ARNs) that you want to associate with the web ACL. Only used when log_destination_type is 'none'. | list(string) |
[] |
no |
| log_destination_type | The type of log destination. Valid values are 'cloudwatch', 's3', or 'none'. When set to 'none', no logging resources are created and log_destination_configs can be used for external destinations. | string |
"cloudwatch" |
no |
| log_group_retention_in_days | The number of days to retain logs in the CloudWatch Log Group. Ignored if create_log_group is false. | number |
0 |
no |
| name | Name of the WAF | string |
n/a | yes |
| nested_statement_rules | Nested Statement Rules for complex AND/OR/NOT logic. See https://registry.terraform.io/modules/cloudposse/waf/aws/latest#nested_statement_rules | list(any) |
[] |
no |
| rate_based_statement_rules | Rate Based Statement Rules | list(any) |
[] |
no |
| regex_match_statement_rules | Regex Match Statement Rules | list(any) |
[] |
no |
| regex_pattern_set_reference_statement_rules | Regex Pattern Set Reference Statement Rules | list(any) |
[] |
no |
| rule_action_override | Map of rule action overrides | map(list(string)) |
{} |
no |
| sampled_requests_enabled | Whether AWS WAF should store a sampling of the web requests that match the rules | bool |
false |
no |
| scope | Specifies whether this is for an AWS CloudFront distribution or for a regional application. Valid values are CLOUDFRONT and REGIONAL. | string |
"REGIONAL" |
no |
| size_constraint_statement_rules | Size Constraint Statement Rules | list(any) |
[] |
no |
| sqli_match_statement_rules | SQLi Match Statement Rules | list(any) |
[] |
no |
| xss_match_statement_rules | XSS Match Statement Rules | list(any) |
[] |
no |
Outputs
| Name | Description |
|---|---|
| arn | The ARN of the WAF WebACL. |
| bypass_parameter_arn | ARN of the SSM parameter storing the bypass secret. Use this for IAM policy configuration. |
| bypass_parameter_name | Name of the SSM parameter storing the bypass secret. |
| bypass_secret_value | The bypass secret value. Use this to pass to E2E test config. |
| capacity | The web ACL capacity units (WCUs) currently being used by this web ACL. |
| id | The ID of the WAF WebACL. |
| log_group_arn | The ARN of the CloudWatch Log Group for WAF logs. |
| logging_config_id | The ARN of the WAFv2 Web ACL logging configuration. |
| s3_log_bucket_arn | The ARN of the S3 bucket for WAF logs. |
| s3_log_bucket_name | The name of the S3 bucket for WAF logs. |