From dbcbdee8c0c98dec5d268d90a378368effddae80 Mon Sep 17 00:00:00 2001 From: Olamide Date: Fri, 5 Apr 2024 12:08:59 +0100 Subject: [PATCH] Enable header check for multiple headers --- aws/waf/main.tf | 103 ++++++++++++++++++++++++++++++++++++++----- aws/waf/outputs.tf | 5 +++ aws/waf/variables.tf | 11 +++-- 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/aws/waf/main.tf b/aws/waf/main.tf index fcf54bcc..b22e5ea7 100644 --- a/aws/waf/main.tf +++ b/aws/waf/main.tf @@ -31,21 +31,104 @@ resource "aws_wafv2_web_acl" "main" { block {} } } - statement { - byte_match_statement { - field_to_match { - single_header { - name = lower(rule.value["header_name"]) + dynamic "statement" { + for_each = length(rule.value["header_values"]) == 1 ? rule.value["header_values"] : {} + content { + dynamic "byte_match_statement" { + for_each = length(statement.value["not_statement"]) == false ? [1] : [] + content { + field_to_match { + single_header { + name = lower(statement.value["header_name"]) + } + } + + positional_constraint = "CONTAINS" + + search_string = statement.value["header_value"] + + text_transformation { + priority = 1 + type = "LOWERCASE" + } + } + } + dynamic "not_statement" { + for_each = length(statement.value["not_statement"]) == true ? [1] : [] + content { + statement { + byte_match_statement { + field_to_match { + single_header { + name = lower(statement.value["header_name"]) + } + } + + positional_constraint = "CONTAINS" + + search_string = statement.value["header_value"] + + text_transformation { + priority = 1 + type = "LOWERCASE" + } + } + } } } + } + } + dynamic "statement" { + for_each = length(rule.value["header_values"]) > 1 ? rule.value["header_values"] : {} + content { + and_statement { + dynamic "statement" { + for_each = rule.value["header_values"] + content { + dynamic "byte_match_statement" { + for_each = length(statement.value["not_statement"]) == false ? [1] : [] + content { + field_to_match { + single_header { + name = lower(statement.value["header_name"]) + } + } - positional_constraint = "CONTAINS" + positional_constraint = "CONTAINS" - search_string = rule.value["header_value"] + search_string = statement.value["header_value"] - text_transformation { - priority = 1 - type = "LOWERCASE" + text_transformation { + priority = 1 + type = "LOWERCASE" + } + } + } + dynamic "not_statement" { + for_each = length(statement.value["not_statement"]) == true ? [1] : [] + content { + statement { + byte_match_statement { + field_to_match { + single_header { + name = lower(statement.value["header_name"]) + } + } + + positional_constraint = "CONTAINS" + + search_string = statement.value["header_value"] + + text_transformation { + priority = 1 + type = "LOWERCASE" + } + } + } + } + } + } + } } } } diff --git a/aws/waf/outputs.tf b/aws/waf/outputs.tf index f0a1776f..dd7a7ccb 100644 --- a/aws/waf/outputs.tf +++ b/aws/waf/outputs.tf @@ -2,3 +2,8 @@ output "aws_waf_arn" { description = "The arn for AWS WAF WebACL." value = aws_wafv2_web_acl.main.arn } + +output "waf_logs_sns_topic_arn" { + description = "The arn for the SNS topic to receive the AWS WAF logs" + value = aws_sns_topic.waf_logs_sns_subscription.arn +} diff --git a/aws/waf/variables.tf b/aws/waf/variables.tf index dee871fb..69f11f71 100644 --- a/aws/waf/variables.tf +++ b/aws/waf/variables.tf @@ -46,10 +46,13 @@ variable "rate_limit_rules" { variable "header_match_rules" { description = "Rule statement to inspect and match the header for an incoming request." type = map(object({ - name = string # Name of the header match rule group - priority = number # Relative processing order for header match rule relative to other rules processed by AWS WAF. - header_name = string # This is the name of the header to inspect for all incoming requests. - header_value = string # This is the value to look out for a matching header name for all incoming requests + name = string # Name of the header match rule group + priority = number # Relative processing order for header match rule relative to other rules processed by AWS WAF. + header_values = map(object({ # Header values contains a map of headers to inspect. You can provide multiple headers and values, all headers will be inspected together with `AND` logic. + header_name = string # This is the name of the header to inspect for all incoming requests. + header_value = string # This is the value to look out for a matching header name for all incoming requests + not_statement = optional(bool, false) # This indicates if the result this header match should be negated. The negated result will be joined with other header match results using `AND` logic if more than 1 header is provided. + })) count_override = optional(bool, true) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`. Default value is false. }))