From 146f78e54e6e7c38c6aafbbe24030f186aeee7e8 Mon Sep 17 00:00:00 2001 From: uyggnodoow Date: Thu, 30 Jan 2025 21:03:47 +0900 Subject: [PATCH] feat: Added AWSManagedRules rule sets to WAFv2 + AWSManagedRulesATPRuleSet + AWSManagedRulesACFPRuleSet + AWSManagedRulesBotControlRuleSet + captchaConfig + challengeConfig --- README.md | 8 +++- main.tf | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++- variables.tf | 18 +++++++++ 3 files changed, 135 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6808f63..8a8966e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,10 @@ A Terraform module that creates Web Application Firewall (WAFV2). - GeoMatchStatement - IPSetReferenceStatement - LabelMatchStatement - - ManagedRuleGroupStatement + - ManagedRuleGroupStatemen + - AWSManagedRulesACFPRuleSet + - AWSManagedRulesATPRuleSet + - AWSManagedRulesBotControlRuleSet - NotStatement - OrStatement - RateBasedStatement @@ -58,6 +61,8 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [captcha\_config](#input\_captcha\_config) | (Optional) The amount of time, in seconds, that a CAPTCHA or challenge timestamp is considered valid by AWS WAF. The default setting is 300. | `number` | `300` | no | +| [challenge\_config](#input\_challenge\_config) | (Optional) The amount of time, in seconds, that a CAPTCHA or challenge timestamp is considered valid by AWS WAF. The default setting is 300. | `number` | `300` | no | | [custom\_response\_body](#input\_custom\_response\_body) | (Optional) Defines custom response bodies that can be referenced by custom\_response actions. | `map(any)` | `{}` | no | | [default\_action](#input\_default\_action) | (Required) Action to perform if none of the rules contained in the WebACL match. | `string` | n/a | yes | | [description](#input\_description) | (Optional) Friendly description of the WebACL. | `string` | `null` | no | @@ -71,6 +76,7 @@ No modules. | [rule](#input\_rule) | (Optional) Rule blocks used to identify the web requests that you want to allow, block, or count. | `any` | n/a | yes | | [scope](#input\_scope) | (Required) Specifies whether this is for an AWS CloudFront distribution or for a regional application | `string` | n/a | yes | | [tags](#input\_tags) | (Optional) Map of key-value pairs to associate with the resource. | `map(string)` | `null` | no | +| [token\_domains](#input\_token\_domains) | (Optional) Specifies the domains that AWS WAF should accept in a web request token. This enables the use of tokens across multiple protected websites. When AWS WAF provides a token, it uses the domain of the AWS resource that the web ACL is protecting. If you don't specify a list of token domains, AWS WAF accepts tokens only for the domain of the protected resource. With a token domain list, AWS WAF accepts the resource's host domain plus all domains in the token domain list, including their prefixed subdomains. | `list(string)` | `[]` | no | | [visibility\_config](#input\_visibility\_config) | (Required) Defines and enables Amazon CloudWatch metrics and web request sample collection. | `map(string)` | n/a | yes | ## Outputs diff --git a/main.tf b/main.tf index b248dc8..3f6c83e 100644 --- a/main.tf +++ b/main.tf @@ -85,6 +85,96 @@ resource "aws_wafv2_web_acl" "this" { vendor_name = lookup(managed_rule_group_statement.value, "vendor_name", "AWS") version = lookup(managed_rule_group_statement.value, "version", null) + dynamic "managed_rule_group_configs" { + for_each = lookup(managed_rule_group_statement.value, "managed_rule_group_configs", null) == null ? [] : lookup(managed_rule_group_statement.value, "managed_rule_group_configs") + content { + dynamic "aws_managed_rules_acfp_rule_set" { + for_each = lookup(managed_rule_group_configs.value, "aws_managed_rules_acfp_rule_set", null) == null ? [] : [lookup(managed_rule_group_configs.value, "aws_managed_rules_acfp_rule_set")] + content { + creation_path = lookup(aws_managed_rules_acfp_rule_set.value, "creation_path") + enable_regex_in_path = lookup(aws_managed_rules_acfp_rule_set.value, "enable_regex_in_path", false) + registration_page_path = lookup(aws_managed_rules_acfp_rule_set.value, "registration_page_path") + + dynamic "request_inspection" { + for_each = lookup(aws_managed_rules_acfp_rule_set.value, "request_inspection") == null ? [] : [lookup(aws_managed_rules_acfp_rule_set.value, "request_inspection")] + content { + payload_type = lookup(request_inspection.value, "payload_type", "JSON") + + dynamic "address_fields" { + for_each = lookup(request_inspection.value, "address_fields", null) == null ? [] : [lookup(request_inspection.value, "address_fields")] + + content { + identifiers = lookup(address_fields.value, "identifiers") + } + } + dynamic "email_field" { + for_each = lookup(request_inspection.value, "email_field", null) == null ? [] : [lookup(request_inspection.value, "email_field")] + content { + identifier = lookup(email_field.value, "identifier") + } + } + dynamic "password_field" { + for_each = lookup(request_inspection.value, "password_field", null) == null ? [] : [lookup(request_inspection.value, "password_field")] + content { + identifier = lookup(password_field.value, "identifier") + } + } + dynamic "phone_number_fields" { + for_each = lookup(request_inspection.value, "phone_number_fields", null) == null ? [] : [lookup(request_inspection.value, "phone_number_fields")] + + content { + identifiers = lookup(phone_number_fields.value, "identifiers") + } + } + dynamic "username_field" { + for_each = lookup(request_inspection.value, "username_field", null) == null ? [] : [lookup(request_inspection.value, "username_field")] + content { + identifier = lookup(username_field.value, "identifier") + } + } + } + } + } + } + + dynamic "aws_managed_rules_atp_rule_set" { + for_each = lookup(managed_rule_group_configs.value, "aws_managed_rules_atp_rule_set", null) == null ? [] : [lookup(managed_rule_group_configs.value, "aws_managed_rules_atp_rule_set")] + content { + enable_regex_in_path = lookup(aws_managed_rules_atp_rule_set.value, "enable_regex_in_path", false) + login_path = lookup(aws_managed_rules_atp_rule_set.value, "login_path") + + dynamic "request_inspection" { + for_each = lookup(aws_managed_rules_atp_rule_set.value, "request_inspection") == null ? [] : [lookup(aws_managed_rules_atp_rule_set.value, "request_inspection")] + content { + payload_type = lookup(request_inspection.value, "payload_type", "JSON") + + dynamic "password_field" { + for_each = lookup(request_inspection.value, "password_field", null) == null ? [] : [lookup(request_inspection.value, "password_field")] + content { + identifier = lookup(password_field.value, "identifier") + } + } + dynamic "username_field" { + for_each = lookup(request_inspection.value, "username_field", null) == null ? [] : [lookup(request_inspection.value, "username_field")] + content { + identifier = lookup(username_field.value, "identifier") + } + } + } + } + } + } + + dynamic "aws_managed_rules_bot_control_rule_set" { + for_each = lookup(managed_rule_group_configs.value, "aws_managed_rules_bot_control_rule_set", null) == null ? [] : [lookup(managed_rule_group_configs.value, "aws_managed_rules_bot_control_rule_set")] + content { + enable_machine_learning = lookup(aws_managed_rules_bot_control_rule_set.value, "enable_machine_learning", true) + inspection_level = upper(lookup(aws_managed_rules_bot_control_rule_set.value, "inspection_level", "COMMON")) + } + } + } + } + dynamic "rule_action_override" { for_each = lookup(managed_rule_group_statement.value, "rule_action_override", null) == null ? [] : lookup(managed_rule_group_statement.value, "rule_action_override") content { @@ -113,6 +203,7 @@ resource "aws_wafv2_web_acl" "this" { } } } + dynamic "scope_down_statement" { for_each = lookup(managed_rule_group_statement.value, "scope_down_statement", null) == null ? [] : [lookup(managed_rule_group_statement.value, "scope_down_statement")] content { @@ -12107,6 +12198,20 @@ resource "aws_wafv2_web_acl" "this" { } } + captcha_config { + immunity_time_property { + immunity_time = var.captcha_config + } + } + + challenge_config { + immunity_time_property { + immunity_time = var.challenge_config + } + } + + token_domains = var.token_domains + visibility_config { cloudwatch_metrics_enabled = var.visibility_config.cloudwatch_metrics_enabled metric_name = var.visibility_config.metric_name @@ -12158,30 +12263,34 @@ resource "aws_wafv2_web_acl_logging_configuration" "this" { dynamic "logging_filter" { for_each = var.logging_filter == null ? [] : [var.logging_filter] + content { default_behavior = lookup(logging_filter.value, "default_behavior") dynamic "filter" { for_each = lookup(logging_filter.value, "filter") iterator = filter + content { behavior = lookup(filter.value, "behavior") requirement = lookup(filter.value, "requirement") dynamic "condition" { for_each = lookup(filter.value, "condition") + content { dynamic "action_condition" { for_each = lookup(condition.value, "action_condition", null) == null ? {} : lookup(condition.value, "action_condition") iterator = action_condition + content { action = action_condition.value } } - dynamic "label_name_condition" { for_each = lookup(condition.value, "label_name_condition", null) == null ? {} : lookup(condition.value, "label_name_condition") iterator = label_name_condition + content { label_name = label_name_condition.value } diff --git a/variables.tf b/variables.tf index 3d9e572..df389e0 100644 --- a/variables.tf +++ b/variables.tf @@ -30,6 +30,24 @@ variable "custom_response_body" { default = {} } +variable "captcha_config" { + description = "(Optional) The amount of time, in seconds, that a CAPTCHA or challenge timestamp is considered valid by AWS WAF. The default setting is 300." + type = number + default = 300 +} + +variable "challenge_config" { + description = "(Optional) The amount of time, in seconds, that a CAPTCHA or challenge timestamp is considered valid by AWS WAF. The default setting is 300." + type = number + default = 300 +} + +variable "token_domains" { + description = "(Optional) Specifies the domains that AWS WAF should accept in a web request token. This enables the use of tokens across multiple protected websites. When AWS WAF provides a token, it uses the domain of the AWS resource that the web ACL is protecting. If you don't specify a list of token domains, AWS WAF accepts tokens only for the domain of the protected resource. With a token domain list, AWS WAF accepts the resource's host domain plus all domains in the token domain list, including their prefixed subdomains." + type = list(string) + default = [] +} + variable "rule" { description = "(Optional) Rule blocks used to identify the web requests that you want to allow, block, or count." type = any