Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: extend support for rate based statement rules #81

Closed
wants to merge 11 commits into from
103 changes: 100 additions & 3 deletions rules.tf
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,14 @@ resource "aws_wafv2_web_acl" "default" {
}
dynamic "block" {
for_each = rule.value.action == "block" ? [1] : []
content {}
content {
dynamic "custom_response" {
for_each = rule.value.custom_response_code != null ? [rule.value.custom_response_code] : []
content {
response_code = custom_response.value
}
}
}
}
dynamic "count" {
for_each = rule.value.action == "count" ? [1] : []
Expand All @@ -731,8 +738,98 @@ resource "aws_wafv2_web_acl" "default" {
for_each = lookup(rule.value, "statement", null) != null ? [rule.value.statement] : []

content {
aggregate_key_type = lookup(rate_based_statement.value, "aggregate_key_type", "IP")
limit = rate_based_statement.value.limit
aggregate_key_type = lookup(rate_based_statement.value, "aggregate_key_type", "IP")
limit = rate_based_statement.value.limit
evaluation_window_sec = lookup(rate_based_statement.value, "evaluation_window_sec", 300)

dynamic "scope_down_statement" {
for_each = lookup(rate_based_statement.value, "scope_down_statement", null) != null ? [rate_based_statement.value.scope_down_statement] : []

content {
dynamic "byte_match_statement" {
for_each = lookup(scope_down_statement.value, "byte_match_statement", null) != null ? [scope_down_statement.value.byte_match_statement] : []
content {
search_string = byte_match_statement.value.search_string
positional_constraint = byte_match_statement.value.positional_constraint
field_to_match {
dynamic "uri_path" {
for_each = lookup(byte_match_statement.value, "uri_path", null) != null ? [1] : []
content {}
}
dynamic "single_header" {
for_each = lookup(byte_match_statement.value, "single_header", null) != null ? [byte_match_statement.value.single_header] : []
content {
name = single_header.value
}
}
dynamic "method" {
for_each = lookup(byte_match_statement.value, "method", null) != null ? [1] : []
content {}
}
}
dynamic "text_transformation" {
for_each = lookup(byte_match_statement.value, "text_transformation", [
{
priority = 0
type = "NONE"
}
])

content {
priority = text_transformation.value.priority
type = text_transformation.value.type
}
}
}
}
dynamic "and_statement" {
for_each = length(lookup(scope_down_statement.value, "and", [])) > 0 ? [scope_down_statement.value.and] : []
content {
dynamic "statement" {
for_each = and_statement.value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GuusDeGraeve the PR looks good, thank you.

Some questions about this code

 for_each = length(lookup(scope_down_statement.value, "and", [])) > 0 ? [scope_down_statement.value.and] : []
                  content {
                    dynamic "statement" {
                      for_each = and_statement.value

Looks like scope_down_statement.value.and should be a list b/c you check its length.
But then you put it into another list in [scope_down_statement.value.and].
Should it be just ? scope_down_statement.value.and : [] ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @aknysh, yes that is correct, the reason is because the add statement is a list of multiple statements. Which we loop over within the next dynamic block dynamic "statement" {.

I know it looks quite confusing when reading the code, but it is definitely on purpose.

content {
dynamic "byte_match_statement" {
for_each = lookup(statement.value, "byte_match_statement", null) != null ? [statement.value.byte_match_statement] : []
content {
search_string = byte_match_statement.value.search_string
positional_constraint = byte_match_statement.value.positional_constraint
field_to_match {
dynamic "uri_path" {
for_each = lookup(byte_match_statement.value, "uri_path", null) != null ? [1] : []
content {}
}
dynamic "single_header" {
for_each = lookup(byte_match_statement.value, "single_header", null) != null ? [byte_match_statement.value.single_header] : []
content {
name = single_header.value
}
}
dynamic "method" {
for_each = lookup(byte_match_statement.value, "method", null) != null ? [1] : []
content {}
}
}
dynamic "text_transformation" {
for_each = lookup(byte_match_statement.value, "text_transformation", [
{
priority = 0
type = "NONE"
}
])

content {
priority = text_transformation.value.priority
type = text_transformation.value.type
}
}
}
}
}
}
}
}
}
}

dynamic "forwarded_ip_config" {
for_each = lookup(rate_based_statement.value, "forwarded_ip_config", null) != null ? [rate_based_statement.value.forwarded_ip_config] : []
Expand Down
5 changes: 3 additions & 2 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,9 @@ variable "rate_based_statement_rules" {
immunity_time = number
})
}), null)
rule_label = optional(list(string), null)
statement = any
rule_label = optional(list(string), null)
statement = any
custom_response_code = optional(number, null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be added to the description

visibility_config = optional(object({
cloudwatch_metrics_enabled = optional(bool)
metric_name = string
Expand Down