umotif-public / terraform-aws-waf-webaclv2

Terraform module to configure WAF V2 Web ACL with managed rules for Application Load Balancer
https://registry.terraform.io/modules/umotif-public/waf-webaclv2/aws
Other
137 stars 124 forks source link

not_statement issue #33

Closed 80kk closed 3 years ago

80kk commented 3 years ago

Hi, I am trying to add not_statement rule blocking any IP address not being added to white list:

` { name = "whitelisted-ip-set" priority = "60" action = "allow"

  ip_set_reference_statement = {
    arn = module.waf.whitelisted_ip_set
  }

  visibility_config = {
    cloudwatch_metrics_enabled = false
    sampled_requests_enabled   = true
  }
},
{
  name     = "blacklisted-ip-set"
  priority = "50"
  action   = "block"

  ip_set_reference_statement = {
    arn = module.waf.blacklisted_ip_set
  }

  visibility_config = {
    cloudwatch_metrics_enabled = false
    metric_name                = join("", [var.tags.name, "-ip-set-block-metrics"])
    sampled_requests_enabled   = true
  }
},
{
  name     = "Allow-only-from-IP"
  priority = 70
  action   = "block"

  not_statement = {
      ip_set_reference_statement = {
        arn = module.waf.whitelisted_ip_set
      }
  }

  visibility_config = {
    cloudwatch_metrics_enabled = false
    metric_name                = join("", [var.tags.name, "-allow-only-from"])
    sampled_requests_enabled   = false
  }
}

` but I am getting the following error:

Error: Error updating WAFv2 WebACL: WAFInvalidParameterException: Error reason: You have used none or multiple values for a field that requires exactly one value., field: STATEMENT, parameter: Statement { RespMetadata: { StatusCode: 400, RequestID: "7a78d73b-9df5-4100-b006-af970e911eb9" }, Field: "STATEMENT", Message_: "Error reason: You have used none or multiple values for a field that requires exactly one value., field: STATEMENT, parameter: Statement", Parameter: "Statement", Reason: "You have used none or multiple values for a field that requires exactly one value." }

How this rule should look?

seanpascual commented 3 years ago

Hello @80kk I think it might be that you need to provide an ARN to the ip_set_reference statement. It looks like you might be passing just the ip_set object and not the ARN. Please have a look at my code below that is working.

My working code looks like (note the way that I'm referring to the ARN for the IP set):

resource "aws_wafv2_ip_set" "custom_ip_set" {
  name = "sean-test-custom-ip-set"

  scope              = "REGIONAL"
  ip_address_version = "IPV4"

  addresses = [
    "192.168.1.1/32",
    "127.0.0.1"
  ]
}

module "waf" {
  source = "git@github.com:umotif-public/terraform-aws-waf-webaclv2.git?ref=3.1.0"

  enabled     = true
  name_prefix = local.name_prefix

  create_alb_association = false

  scope = "REGIONAL"

  allow_default_action = false

  visibility_config = {
    metric_name = "${local.name_prefix}-wafv2-metrics"
  }

  rules = [
    {
      name     = "allow-statuscake"
      priority = "70"
      action   = "block"

      not_statement = {
        ip_set_reference_statement = {
          arn = aws_wafv2_ip_set.custom_ip_set.arn
        }
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "test-waf-setup-waf-ip-set-block-metrics"
        sampled_requests_enabled   = false
      }
    }
  ]
]

Thanks and please let us know if you continue to run into any issues after making that change.

80kk commented 3 years ago

Hello @80kk I think it might be that you need to provide an ARN to the ip_set_reference statement. It looks like you might be passing just the ip_set object and not the ARN. Please have a look at my code below that is working.

This is exactly what I am doing: module.waf.whitelisted_ip_set is an output from: aws_wafv2_ip_set.blacklisted_ip_set.arn:

output "blacklisted_ip_set" {
  value = aws_wafv2_ip_set.blacklisted_ip_set.arn
}

output "whitelisted_ip_set" {
  value = aws_wafv2_ip_set.whitelisted_ip_set.arn
}

and it works fine for:


    {
      name     = "whitelisted-ip-set"
      priority = "60"
      action   = "allow"

      ip_set_reference_statement = {
        arn = module.waf.whitelisted_ip_set
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        sampled_requests_enabled   = true
      }
    },
    {
      name     = "blacklisted-ip-set"
      priority = "50"
      action   = "block"

      ip_set_reference_statement = {
        arn = module.waf.blacklisted_ip_set
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = join("", [var.tags.name, "-ip-set-block-metrics"])
        sampled_requests_enabled   = true
      }
    },

but not for:

    {
      name     = "Allow-only-from"
      priority = 70
      action   = "block"

      not_statement = {
        ip_set_reference_statement = {
          arn = module.waf.whitelisted_ip_set
        }
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = join("", [var.tags.name, "-allow-only-from"])
        sampled_requests_enabled   = false
      }
    }
  ]
seanpascual commented 3 years ago

Hi @80kk would you be able to send through the whole module block please (with any sensitive info redacted)? The rules you posted last work ok on my side, so it's possible there's something else missing.

Also may I confirm what version of terraform and aws_provider you're using please?

Thanks

80kk commented 3 years ago
Terraform v0.13.7
+ provider registry.terraform.io/hashicorp/aws v3.47.0

but 0.12.x is doing the same.

module waf {
  source                      = "../../modules/global/WAFv2"
  name_prefix                 = "WAFv2"
  allow_default_action        = true
  region                      = var.region
  aws_account_id              = var.aws_account_id
  log_destination_configs     = [module.waf.aws_kinesis_firehose_delivery_stream]
  firehose_stream_bucket_name = var.firehose_stream_bucket_name

  redacted_fields = [
    {
      single_header = {
        name = "user-agent"
      }
    }
  ]

  logging_filter = {
    default_behavior = "DROP"

    filter = [
      {
        behavior    = "KEEP"
        requirement = "MEETS_ANY"
        condition = [
          {
            action_condition = {
              action = "ALLOW"
            }
          },
        ]
      },
      {
        behavior    = "KEEP"
        requirement = "MEETS_ALL"
        condition = [
          {
            action_condition = {
              action = "COUNT"
            }
          }
        ]
      }
    ]
  }

  visibility_config = {
    cloudwatch_metrics_enabled     = false
    metric_name                    = "WAFv2-main-metrics"
    sampled_requests_enabled       = true
  }

  rules = [
    {
      name                         = "AWSManagedRulesCommonRuleSet-rule-1"
      priority                     = "10"
      override_action              = "none"

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "AWSManagedRulesCommonRuleSet-metric"
        sampled_requests_enabled   = true
      }

      managed_rule_group_statement = {
        name                       = "AWSManagedRulesCommonRuleSet"
        vendor_name                = "AWS"
        excluded_rule              = [
          "GenericRFI_QUERYARGUMENTS"
        ]
      }
    },
    {
      name                         = "AWSManagedRulesKnownBadInputsRuleSet-rule-2"
      priority                     = "15"
      override_action              = "none"

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "AWSManagedRulesKnownBadInputsRuleSet-metric"
        sampled_requests_enabled   = true
      }

      managed_rule_group_statement = {
        name                       = "AWSManagedRulesKnownBadInputsRuleSet"
        vendor_name                = "AWS"
      }
    },
    {
      name                         = "AWSManagedRulesSQLiRuleSet-rule-3"
      priority                     = "5"
      override_action              = "none"

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "AWSManagedRulesSQLiRuleSet-metric"
        sampled_requests_enabled   = true
      }

      managed_rule_group_statement = {
        name                       = "AWSManagedRulesSQLiRuleSet"
        vendor_name                = "AWS"
      }
    },
    {
      name                         = "AWSManagedRulesLinuxRuleSet-rule-4"
      priority                     = "20"
      override_action              = "none"

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "AWSManagedRulesLinuxRuleSet-metric"
        sampled_requests_enabled   = true
      }

      managed_rule_group_statement = {
        name                       = "AWSManagedRulesLinuxRuleSet"
        vendor_name                = "AWS"
      }
    },
    {
      name                         = "AWSManagedRulesAmazonIpReputationList-rule-5"
      priority                     = "25"
      override_action              = "none"

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "AWSManagedRulesAmazonIpReputationList-metric"
        sampled_requests_enabled   = true
      }

      managed_rule_group_statement = {
        name                       = "AWSManagedRulesAmazonIpReputationList"
        vendor_name                = "AWS"
      }
    },
    {
      name                         = "AWSManagedRulesBotControlRuleSet-rule-5"
      priority                     = "30"
      override_action              = "none"

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "AWSManagedRulesBotControlRuleSet-metric"
        sampled_requests_enabled   = true
      }

      managed_rule_group_statement = {
        name                       = "AWSManagedRulesBotControlRuleSet"
        vendor_name                = "AWS"
        excluded_rule              = [
          "CategoryHttpLibrary",
          "SignalNonBrowserUserAgent"
        ]
      }
    },
    {
      name                 = "ip-rate-limit"
      priority             = "35"
      action               = "count"

      rate_based_statement = {
        limit              = "500"
        aggregate_key_type = "IP"
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        sampled_requests_enabled   = true
      }
    },
    {
      name                = "block_country_codes"
      priority            = "40"
      action              = "block"

      geo_match_statement = {
        country_codes     = ["KP"]
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "block_country_codes-metric"
        sampled_requests_enabled   = true
      }
    },
    {
      name     = "whitelisted-ip-set"
      priority = "60"
      action   = "allow"

      ip_set_reference_statement = {
        arn = module.waf.whitelisted_ip_set
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        sampled_requests_enabled   = true
      }
    },
    {
      name     = "blacklisted-ip-set"
      priority = "50"
      action   = "block"

      ip_set_reference_statement = {
        arn = module.waf.blacklisted_ip_set
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = join("", [var.tags.name, "-ip-set-block-metrics"])
        sampled_requests_enabled   = true
      }
    },
    {
      name     = "Allow-only-from"
      priority = 70
      action   = "block"

      not_statement = {
        ip_set_reference_statement = {
          arn = module.waf.whitelisted_ip_set
        }
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = join("", [var.tags.name, "-allow-only-from"])
        sampled_requests_enabled   = false
      }
    }
  ]

  tags = var.tags
}

module:

resource "aws_wafv2_ip_set" "blacklisted_ip_set" {
  name               = join("", [var.tags.Environment, "-BadBot-blacklisted-ips"])
  description        = "Blacklisted IPs"
  scope              = var.scope
  ip_address_version = "IPV4"
  addresses          = []
}

resource "aws_wafv2_ip_set" "whitelisted_ip_set" {
  name               = join("", [var.tags.Environment, "-BadBot-whitelisted-ips"])
  description        = "Whitelisted IPs"
  scope              = var.scope
  ip_address_version = "IPV4"
  addresses          = []
}

resource "aws_wafv2_web_acl" "main" {
  name  = var.name_prefix
  scope = var.scope

  default_action {
    dynamic "allow" {
      for_each = var.allow_default_action ? [1] : []
      content {}
    }

    dynamic "block" {
      for_each = var.allow_default_action ? [] : [1]
      content {}
    }
  }

  dynamic "rule" {
    for_each = var.rules
    content {
      name     = lookup(rule.value, "name")
      priority = lookup(rule.value, "priority")
... the rest of https://github.com/umotif-public/terraform-aws-waf-webaclv2/blob/main/main.tf
output "web_acl_name" {
  value = join("", aws_wafv2_web_acl.main.*.name)
}

output "web_acl_arn" {
  value = join("", aws_wafv2_web_acl.main.*.arn)
}

output "web_acl_id" {
  value = join("", aws_wafv2_web_acl.main.*.id)
}

output "web_acl_capacity" {
  value = join("", aws_wafv2_web_acl.main.*.capacity)
}

output "blacklisted_ip_set" {
  value = aws_wafv2_ip_set.blacklisted_ip_set.arn
}

output "whitelisted_ip_set" {
  value = aws_wafv2_ip_set.whitelisted_ip_set.arn
}

output "aws_kinesis_firehose_delivery_stream" {
  value = aws_kinesis_firehose_delivery_stream.kinesis_firehose_logs_stream.arn
}
seanpascual commented 3 years ago

Hi @80kk I see that the module has been modified slightly for the IP sets. I have tested on my side using terraform 0.13.7 and the module being referenced directly from github, and it is working correctly.

I would suggest moving your IP sets out of the module, as below, and use the module without edits by sourcing it with source = "git@github.com:umotif-public/terraform-aws-waf-webaclv2.git?ref=3.1.0" as this does work.

resource "aws_wafv2_ip_set" "custom_ip_set" {
  name = "sean-test-custom-ip-set"

  scope              = "REGIONAL"
  ip_address_version = "IPV4"

  addresses = [
    "192.168.1.1/32",
    "127.0.0.1/32"
  ]
}

module "waf" {
  source = "git@github.com:umotif-public/terraform-aws-waf-webaclv2.git?ref=3.1.0"

  enabled     = true
  name_prefix = local.name_prefix

  create_alb_association = false

  scope = "REGIONAL"

  allow_default_action = false

  visibility_config = {
    metric_name = "${local.name_prefix}-wafv2-metrics"
  }

  rules = [
    {
      name     = "block-ips"
      priority = "70"
      action   = "block"

      not_statement = {
        ip_set_reference_statement = {
          arn = aws_wafv2_ip_set.custom_ip_set.arn
        }
      }

      visibility_config = {
        cloudwatch_metrics_enabled = false
        metric_name                = "test-waf-setup-waf-ip-set-block-metrics"
        sampled_requests_enabled   = false
      }
    }
  ]
]

Unfortunately as you're working with a custom edited version of the module I cannot assist further, but if you do find issues with using the module as released please feel free to open a new issue.

Thanks,

Sean