# trussworks/wafv2/aws has a basic WAF with the AWS Managed Ruleset # See https://registry.terraform.io/modules/trussworks/wafv2/aws/latest # # Attempted to add some sane defaults so we can customize as needed # # IMPORTANT NOTES: # An 'Allow' action stops processing immediately. Avoid them! # Goals: # - US IPs only - https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-geo-match.html resource "aws_wafv2_ip_set" "blocked" { name = "blocked_ips" scope = "REGIONAL" ip_address_version = "IPV4" addresses = toset(concat(var.additional_blocked_ips, local.blocked_ips)) } resource "aws_wafv2_ip_set" "allowed" { name = "allowed_ips" scope = "REGIONAL" ip_address_version = "IPV4" addresses = var.allowed_ips } resource "aws_wafv2_rule_group" "xdr_custom_rules" { name = "xdr_custom_rules" scope = "REGIONAL" capacity = 1 # Note, there is visibilty config for the group and for the rule visibility_config { cloudwatch_metrics_enabled = true metric_name = "xdr_custom_rules" sampled_requests_enabled = true } rule { name = "Block_Nonpermitted_Countries" priority = 100 action { block {} } statement { not_statement { statement { geo_match_statement { country_codes = [ "US", "DE", ] } } } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "Block_Nonpermitted_Countries" sampled_requests_enabled = true } } # Add additional custom rules here } module "wafv2" { source = "trussworks/wafv2/aws" version = "= 2.4.0" name = replace(var.fqdns[0], ".", "_") scope = "REGIONAL" alb_arn = var.resource_arn associate_alb = true default_action = "block" # Note: The final action is actually to 'allow', provided the host header is correct filtered_header_rule = { header_types = var.fqdns header_value = "host" priority = 900 action = "allow" } # IP based rules are processed first. # If an IP is blocked, it is blocked no matter what. # If an IP is allowed, it is allowed only if it's not blocked, but no other WAF rules are processed. ip_sets_rule = [ { name = "blocked_ips" action = "block" priority = 10 ip_set_arn = aws_wafv2_ip_set.blocked.arn }, { name = "allowed_ips" action = "allow" priority = 20 ip_set_arn = aws_wafv2_ip_set.allowed.arn } ] # Custom Rules are defined above group_rules = [ { name = aws_wafv2_rule_group.xdr_custom_rules.name arn = aws_wafv2_rule_group.xdr_custom_rules.arn priority = 100 override_action = "none" excluded_rules = [] } ] # A rate-based rule tracks the rate of requests for each originating IP address, and triggers the rule action when the rate exceeds a limit that you specify on the number of requests in any 5-minute time span ip_rate_based_rule = { name = "Rate_Limit" priority = 200 limit = 900 # 900 requests per 5 minutes= 3 requests/second (sustained for 5 minutes) action = "block" } # AWS managed rulesets # Baseline was from trussworks/wafv2/aws, but copied here to be customized for our use and renumbered. managed_rules = [ { "excluded_rules": [ "SizeRestrictions_BODY" # Breaks too many things ], "name": "AWSManagedRulesCommonRuleSet", "override_action": "none", "priority": 510 }, { "excluded_rules": [], "name": "AWSManagedRulesAmazonIpReputationList", "override_action": "none", "priority": 520 }, { "excluded_rules": [], "name": "AWSManagedRulesKnownBadInputsRuleSet", "override_action": "none", "priority": 530 }, { "excluded_rules": [], "name": "AWSManagedRulesSQLiRuleSet", "override_action": "none", "priority": 540 }, { "excluded_rules": [], "name": "AWSManagedRulesLinuxRuleSet", "override_action": "none", "priority": 550 }, { "excluded_rules": [], "name": "AWSManagedRulesUnixRuleSet", "override_action": "none", "priority": 560 } ] tags = var.tags } resource "aws_wafv2_web_acl_logging_configuration" "waf_logs" { log_destination_configs = [ "arn:${var.aws_partition}:firehose:${var.aws_region}:${var.aws_account_id}:deliverystream/aws-waf-logs-splunk" ] resource_arn = module.wafv2.web_acl_id # logging_filter { # default_behavior = "KEEP" # # filter { # behavior = "DROP" # # condition { # action_condition { # action = "COUNT" # } # } # # condition { # label_name_condition { # label_name = "awswaf:111122223333:rulegroup:testRules:LabelNameZ" # } # } # # requirement = "MEETS_ALL" # } # # filter { # behavior = "KEEP" # # condition { # action_condition { # action = "ALLOW" # } # } # # requirement = "MEETS_ANY" # } # } }