From bae0447edc298bf9ea464005360ca9eec34a3a48 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Sun, 16 Aug 2020 12:03:52 -0400 Subject: [PATCH 1/2] add forwarded_ip_config attribute to geo and rate_based statements --- aws/resource_aws_wafv2_rule_group_test.go | 121 ++++++++++++++++-- aws/resource_aws_wafv2_web_acl.go | 18 ++- aws/resource_aws_wafv2_web_acl_test.go | 121 ++++++++++++++++++ aws/wafv2_helper.go | 59 ++++++++- website/docs/r/wafv2_rule_group.html.markdown | 12 ++ website/docs/r/wafv2_web_acl.html.markdown | 15 ++- 6 files changed, 325 insertions(+), 21 deletions(-) diff --git a/aws/resource_aws_wafv2_rule_group_test.go b/aws/resource_aws_wafv2_rule_group_test.go index 0b2f6737e9eb..d2fdde1ad95d 100644 --- a/aws/resource_aws_wafv2_rule_group_test.go +++ b/aws/resource_aws_wafv2_rule_group_test.go @@ -690,11 +690,12 @@ func TestAccAwsWafv2RuleGroup_GeoMatchStatement(t *testing.T) { testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.geo_match_statement.#": "1", - "statement.0.geo_match_statement.0.country_codes.#": "2", - "statement.0.geo_match_statement.0.country_codes.0": "US", - "statement.0.geo_match_statement.0.country_codes.1": "NL", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", + "statement.0.geo_match_statement.0.country_codes.0": "US", + "statement.0.geo_match_statement.0.country_codes.1": "NL", + "statement.0.geo_match_statement.0.forwarded_ip_config.#": "0", }), ), }, @@ -705,12 +706,69 @@ func TestAccAwsWafv2RuleGroup_GeoMatchStatement(t *testing.T) { testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ - "statement.#": "1", - "statement.0.geo_match_statement.#": "1", - "statement.0.geo_match_statement.0.country_codes.#": "3", - "statement.0.geo_match_statement.0.country_codes.0": "ZM", - "statement.0.geo_match_statement.0.country_codes.1": "EE", - "statement.0.geo_match_statement.0.country_codes.2": "MM", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "3", + "statement.0.geo_match_statement.0.country_codes.0": "ZM", + "statement.0.geo_match_statement.0.country_codes.1": "EE", + "statement.0.geo_match_statement.0.country_codes.2": "MM", + "statement.0.geo_match_statement.0.forwarded_ip_config.#": "0", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2RuleGroupImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2RuleGroup_GeoMatchStatement_ForwardedIPConfig(t *testing.T) { + var v wafv2.RuleGroup + ruleGroupName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_rule_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2RuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2RuleGroupConfig_GeoMatchStatement_ForwardedIPConfig(ruleGroupName, "MATCH", "X-Forwarded-For"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", + "statement.0.geo_match_statement.0.country_codes.0": "US", + "statement.0.geo_match_statement.0.country_codes.1": "NL", + "statement.0.geo_match_statement.0.forwarded_ip_config.#": "1", + "statement.0.geo_match_statement.0.forwarded_ip_config.0.fallback_behavior": "MATCH", + "statement.0.geo_match_statement.0.forwarded_ip_config.0.header_name": "X-Forwarded-For", + }), + ), + }, + { + Config: testAccAwsWafv2RuleGroupConfig_GeoMatchStatement_ForwardedIPConfig(ruleGroupName, "NO_MATCH", "Updated"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2RuleGroupExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/rulegroup/.+$`)), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", + "statement.0.geo_match_statement.0.country_codes.0": "US", + "statement.0.geo_match_statement.0.country_codes.1": "NL", + "statement.0.geo_match_statement.0.forwarded_ip_config.#": "1", + "statement.0.geo_match_statement.0.forwarded_ip_config.0.fallback_behavior": "NO_MATCH", + "statement.0.geo_match_statement.0.forwarded_ip_config.0.header_name": "Updated", }), ), }, @@ -2201,6 +2259,47 @@ resource "aws_wafv2_rule_group" "test" { `, name) } +func testAccAwsWafv2RuleGroupConfig_GeoMatchStatement_ForwardedIPConfig(name, fallbackBehavior, headerName string) string { + return fmt.Sprintf(` +resource "aws_wafv2_rule_group" "test" { + capacity = 2 + name = "%s" + scope = "REGIONAL" + + rule { + name = "rule-1" + priority = 1 + + action { + allow {} + } + + statement { + geo_match_statement { + country_codes = ["US", "NL"] + forwarded_ip_config { + fallback_behavior = "%s" + header_name = "%s" + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, fallbackBehavior, headerName) +} + func testAccAwsWafv2RuleGroupConfig_GeoMatchStatement_Update(name string) string { return fmt.Sprintf(` resource "aws_wafv2_rule_group" "test" { diff --git a/aws/resource_aws_wafv2_web_acl.go b/aws/resource_aws_wafv2_web_acl.go index 437f4e669b0b..9cb1fef3fa92 100644 --- a/aws/resource_aws_wafv2_web_acl.go +++ b/aws/resource_aws_wafv2_web_acl.go @@ -401,15 +401,14 @@ func wafv2RateBasedStatementSchema(level int) *schema.Schema { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - // Required field but currently only supports "IP" + // Required field "aggregate_key_type": { - Type: schema.TypeString, - Optional: true, - Default: wafv2.RateBasedStatementAggregateKeyTypeIp, - ValidateFunc: validation.StringInSlice([]string{ - wafv2.RateBasedStatementAggregateKeyTypeIp, - }, false), + Type: schema.TypeString, + Optional: true, + Default: wafv2.RateBasedStatementAggregateKeyTypeIp, + ValidateFunc: validation.StringInSlice(wafv2.RateBasedStatementAggregateKeyType_Values(), false), }, + "forwarded_ip_config": wafv2ForwardedIPConfig(), "limit": { Type: schema.TypeInt, Required: true, @@ -627,6 +626,10 @@ func expandWafv2RateBasedStatement(l []interface{}) *wafv2.RateBasedStatement { Limit: aws.Int64(int64(m["limit"].(int))), } + if v, ok := m["forwarded_ip_config"]; ok { + r.ForwardedIPConfig = expandWafv2ForwardedIPConfig(v.([]interface{})) + } + s := m["scope_down_statement"].([]interface{}) if len(s) > 0 && s[0] != nil { r.ScopeDownStatement = expandWafv2Statement(s[0].(map[string]interface{})) @@ -819,6 +822,7 @@ func flattenWafv2RateBasedStatement(r *wafv2.RateBasedStatement) interface{} { m := map[string]interface{}{ "limit": int(aws.Int64Value(r.Limit)), "aggregate_key_type": aws.StringValue(r.AggregateKeyType), + "forwarded_ip_config": flattenWafv2ForwardedIPConfig(r.ForwardedIPConfig), "scope_down_statement": nil, } diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index 7c6af045b510..485da1f956e4 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -584,6 +584,7 @@ func TestAccAwsWafv2WebACL_RateBasedStatement(t *testing.T) { "statement.#": "1", "statement.0.rate_based_statement.#": "1", "statement.0.rate_based_statement.0.aggregate_key_type": "IP", + "statement.0.rate_based_statement.0.forwarded_ip_config.#": "0", "statement.0.rate_based_statement.0.limit": "50000", "statement.0.rate_based_statement.0.scope_down_statement.#": "0", }), @@ -605,6 +606,7 @@ func TestAccAwsWafv2WebACL_RateBasedStatement(t *testing.T) { "statement.#": "1", "statement.0.rate_based_statement.#": "1", "statement.0.rate_based_statement.0.aggregate_key_type": "IP", + "statement.0.rate_based_statement.0.forwarded_ip_config.#": "0", "statement.0.rate_based_statement.0.limit": "10000", "statement.0.rate_based_statement.0.scope_down_statement.#": "1", "statement.0.rate_based_statement.0.scope_down_statement.0.geo_match_statement.#": "1", @@ -624,6 +626,74 @@ func TestAccAwsWafv2WebACL_RateBasedStatement(t *testing.T) { }) } +func TestAccAwsWafv2WebACL_RateBasedStatement_ForwardedIPConfig(t *testing.T) { + var v wafv2.WebACL + webACLName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_ForwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": "rule-1", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.count.#": "1", + "statement.#": "1", + "statement.0.rate_based_statement.#": "1", + "statement.0.rate_based_statement.0.aggregate_key_type": "FORWARDED_IP", + "statement.0.rate_based_statement.0.forwarded_ip_config.#": "1", + "statement.0.rate_based_statement.0.forwarded_ip_config.0.fallback_behavior": "MATCH", + "statement.0.rate_based_statement.0.forwarded_ip_config.0.header_name": "X-Forwarded-For", + "statement.0.rate_based_statement.0.limit": "50000", + "statement.0.rate_based_statement.0.scope_down_statement.#": "0", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLConfig_RateBasedStatement_ForwardedIPConfig(webACLName, "NO_MATCH", "Updated"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": "rule-1", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "0", + "action.0.count.#": "1", + "statement.#": "1", + "statement.0.rate_based_statement.#": "1", + "statement.0.rate_based_statement.0.aggregate_key_type": "FORWARDED_IP", + "statement.0.rate_based_statement.0.forwarded_ip_config.#": "1", + "statement.0.rate_based_statement.0.forwarded_ip_config.0.fallback_behavior": "NO_MATCH", + "statement.0.rate_based_statement.0.forwarded_ip_config.0.header_name": "Updated", + "statement.0.rate_based_statement.0.limit": "50000", + "statement.0.rate_based_statement.0.scope_down_statement.#": "0", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2WebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + func TestAccAwsWafv2WebACL_RuleGroupReferenceStatement(t *testing.T) { var v wafv2.WebACL var idx int @@ -1252,6 +1322,57 @@ resource "aws_wafv2_web_acl" "test" { `, name) } +func testAccAwsWafv2WebACLConfig_RateBasedStatement_ForwardedIPConfig(name, fallbackBehavior, headerName string) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = "%[1]s" + description = "%[1]s" + scope = "REGIONAL" + + default_action { + block {} + } + + rule { + name = "rule-1" + priority = 1 + + action { + count {} + } + + statement { + rate_based_statement { + aggregate_key_type = "FORWARDED_IP" + forwarded_ip_config { + fallback_behavior = "%s" + header_name = "%s" + } + limit = 50000 + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + tags = { + Tag1 = "Value1" + Tag2 = "Value2" + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, fallbackBehavior, headerName) +} + func testAccAwsWafv2WebACLConfig_RateBasedStatement_Update(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" { diff --git a/aws/wafv2_helper.go b/aws/wafv2_helper.go index 4c85ed08cbbf..81fca68239b8 100644 --- a/aws/wafv2_helper.go +++ b/aws/wafv2_helper.go @@ -143,6 +143,7 @@ func wafv2GeoMatchStatementSchema() *schema.Schema { MinItems: 1, Elem: &schema.Schema{Type: schema.TypeString}, }, + "forwarded_ip_config": wafv2ForwardedIPConfig(), }, }, } @@ -301,6 +302,27 @@ func wafv2FieldToMatchSchema() *schema.Schema { } } +func wafv2ForwardedIPConfig() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fallback_behavior": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(wafv2.FallbackBehavior_Values(), false), + }, + "header_name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + func wafv2TextTransformationSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeSet, @@ -577,6 +599,19 @@ func expandWafv2FieldToMatch(l []interface{}) *wafv2.FieldToMatch { return f } +func expandWafv2ForwardedIPConfig(l []interface{}) *wafv2.ForwardedIPConfig { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + return &wafv2.ForwardedIPConfig{ + FallbackBehavior: aws.String(m["fallback_behavior"].(string)), + HeaderName: aws.String(m["header_name"].(string)), + } +} + func expandWafv2SingleHeader(l []interface{}) *wafv2.SingleHeader { if len(l) == 0 || l[0] == nil { return nil @@ -648,9 +683,15 @@ func expandWafv2GeoMatchStatement(l []interface{}) *wafv2.GeoMatchStatement { m := l[0].(map[string]interface{}) - return &wafv2.GeoMatchStatement{ + statement := &wafv2.GeoMatchStatement{ CountryCodes: expandStringList(m["country_codes"].([]interface{})), } + + if v, ok := m["forwarded_ip_config"]; ok { + statement.ForwardedIPConfig = expandWafv2ForwardedIPConfig(v.([]interface{})) + } + + return statement } func expandWafv2NotStatement(l []interface{}) *wafv2.NotStatement { @@ -908,6 +949,19 @@ func flattenWafv2FieldToMatch(f *wafv2.FieldToMatch) interface{} { return []interface{}{m} } +func flattenWafv2ForwardedIPConfig(f *wafv2.ForwardedIPConfig) interface{} { + if f == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "fallback_behavior": aws.StringValue(f.FallbackBehavior), + "header_name": aws.StringValue(f.HeaderName), + } + + return []interface{}{m} +} + func flattenWafv2SingleHeader(s *wafv2.SingleHeader) interface{} { if s == nil { return []interface{}{} @@ -961,7 +1015,8 @@ func flattenWafv2GeoMatchStatement(g *wafv2.GeoMatchStatement) interface{} { } m := map[string]interface{}{ - "country_codes": flattenStringList(g.CountryCodes), + "country_codes": flattenStringList(g.CountryCodes), + "forwarded_ip_config": flattenWafv2ForwardedIPConfig(g.ForwardedIPConfig), } return []interface{}{m} diff --git a/website/docs/r/wafv2_rule_group.html.markdown b/website/docs/r/wafv2_rule_group.html.markdown index 93b66cba31bb..0ba57029e36b 100644 --- a/website/docs/r/wafv2_rule_group.html.markdown +++ b/website/docs/r/wafv2_rule_group.html.markdown @@ -356,6 +356,7 @@ The `byte_match_statement` block supports the following arguments: The `geo_match_statement` block supports the following arguments: * `country_codes` - (Required) An array of two-character country codes, for example, [ "US", "CN" ], from the alpha-2 country ISO codes of the `ISO 3166` international standard. See the [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_GeoMatchStatement.html) for valid values. +* `forwarded_ip_config` - (Optional) The configuration for inspecting IP addresses in an HTTP header that you specify, instead of using the IP address that's reported by the web request origin. See [Forwarded IP Config](#forwarded-ip-config) below for details. ### IP Set Reference Statement @@ -437,6 +438,17 @@ The `field_to_match` block supports the following arguments: * `single_query_argument` - (Optional) Inspect a single query argument. See [Single Query Argument](#single-query-argument) below for details. * `uri_path` - (Optional) Inspect the request URI path. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. +### Forwarded IP Config + +The configuration for inspecting IP addresses in an HTTP header that you specify, instead of using the IP address that's reported by the web request origin. Commonly, this is the X-Forwarded-For (XFF) header, but you can specify +any header name. If the specified header isn't present in the request, AWS WAFv2 doesn't apply the rule to the web request at all. +AWS WAFv2 only evaluates the first IP address found in the specified HTTP header. + +The `forwarded_ip_config` block supports the following arguments: + +* `fallback_behavior` - (Required) - The match status to assign to the web request if the request doesn't have a valid IP address in the specified position. Valid values include: `MATCH` or `NO_MATCH`. +* `header_name` - (Required) - The name of the HTTP header to use for the IP address. + ### Single Header Inspect a single header. Provide the name of the header to inspect, for example, `User-Agent` or `Referer` (provided as lowercase strings). diff --git a/website/docs/r/wafv2_web_acl.html.markdown b/website/docs/r/wafv2_web_acl.html.markdown index 9a34bea94382..6b2d08baf7ec 100644 --- a/website/docs/r/wafv2_web_acl.html.markdown +++ b/website/docs/r/wafv2_web_acl.html.markdown @@ -351,6 +351,7 @@ The `byte_match_statement` block supports the following arguments: The `geo_match_statement` block supports the following arguments: * `country_codes` - (Required) An array of two-character country codes, for example, [ "US", "CN" ], from the alpha-2 country ISO codes of the `ISO 3166` international standard. See the [documentation](https://docs.aws.amazon.com/waf/latest/APIReference/API_GeoMatchStatement.html) for valid values. +* `forwarded_ip_config` - (Optional) The configuration for inspecting IP addresses in an HTTP header that you specify, instead of using the IP address that's reported by the web request origin. See [Forwarded IP Config](#forwarded-ip-config) below for details. ### IP Set Reference Statement @@ -396,7 +397,8 @@ You can't nest a `rate_based_statement`, for example for use inside a `not_state The `rate_based_statement` block supports the following arguments: -* `aggregate_key_type` - (Optional) Setting that indicates how to aggregate the request counts. Currently the only supported value is `IP` which is set as the default. +* `aggregate_key_type` - (Optional) Setting that indicates how to aggregate the request counts. Valid values include: `FORWARDED_IP` or `IP`. Default: `IP`. +* `forwarded_ip_config` - (Optional) The configuration for inspecting IP addresses in an HTTP header that you specify, instead of using the IP address that's reported by the web request origin. If `aggregate_key_type` is set to `FORWARDED_IP`, this block is required. See [Forwarded IP Config](#forwarded-ip-config) below for details. * `limit` - (Required) The limit on requests per 5-minute period for a single originating IP address. * `scope_down_statement` - (Optional) An optional nested statement that narrows the scope of the rate-based statement to matching web requests. This can be any nestable statement, and you can nest statements at any level below this scope-down statement. See [Statement](#statement) above for details. @@ -471,6 +473,17 @@ The `field_to_match` block supports the following arguments: * `single_query_argument` - (Optional) Inspect a single query argument. See [Single Query Argument](#single-query-argument) below for details. * `uri_path` - (Optional) Inspect the request URI path. This is the part of a web request that identifies a resource, for example, `/images/daily-ad.jpg`. +### Forwarded IP Config + +The configuration for inspecting IP addresses in an HTTP header that you specify, instead of using the IP address that's reported by the web request origin. Commonly, this is the X-Forwarded-For (XFF) header, but you can specify +any header name. If the specified header isn't present in the request, AWS WAFv2 doesn't apply the rule to the web request at all. +AWS WAFv2 only evaluates the first IP address found in the specified HTTP header. + +The `forwarded_ip_config` block supports the following arguments: + +* `fallback_behavior` - (Required) - The match status to assign to the web request if the request doesn't have a valid IP address in the specified position. Valid values include: `MATCH` or `NO_MATCH`. +* `header_name` - (Required) - The name of the HTTP header to use for the IP address. + ### Single Header Inspect a single header. Provide the name of the header to inspect, for example, `User-Agent` or `Referer` (provided as lowercase strings). From da678e4bf3ec4dbacf0caa04002ad5ea912d9382 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Tue, 18 Aug 2020 19:03:00 -0400 Subject: [PATCH 2/2] add webacl geo-match tests --- aws/resource_aws_wafv2_web_acl_test.go | 256 +++++++++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/aws/resource_aws_wafv2_web_acl_test.go b/aws/resource_aws_wafv2_web_acl_test.go index 485da1f956e4..8ec729f11f18 100644 --- a/aws/resource_aws_wafv2_web_acl_test.go +++ b/aws/resource_aws_wafv2_web_acl_test.go @@ -626,6 +626,161 @@ func TestAccAwsWafv2WebACL_RateBasedStatement(t *testing.T) { }) } +func TestAccAwsWafv2WebACL_GeoMatchStatement(t *testing.T) { + var v wafv2.WebACL + webACLName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl.test" + countryCode := fmt.Sprintf("%q", "US") + countryCodes := fmt.Sprintf("%s, %q", countryCode, "CA") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement(webACLName, countryCode), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", "REGIONAL"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": "rule-1", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "1", + "action.0.count.#": "0", + "priority": "1", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "1", + "statement.0.geo_match_statement.0.country_codes.0": "US", + "statement.0.geo_match_statement.0.forwarded_ip_config.#": "0", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": "friendly-rule-metric-name", + "visibility_config.0.sampled_requests_enabled": "false", + }), + resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.cloudwatch_metrics_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.metric_name", "friendly-metric-name"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.sampled_requests_enabled", "false"), + ), + }, + { + Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement(webACLName, countryCodes), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "default_action.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.allow.#", "1"), + resource.TestCheckResourceAttr(resourceName, "default_action.0.block.#", "0"), + resource.TestCheckResourceAttr(resourceName, "scope", "REGIONAL"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "name": "rule-1", + "action.#": "1", + "action.0.allow.#": "0", + "action.0.block.#": "1", + "action.0.count.#": "0", + "priority": "1", + "statement.#": "1", + "statement.0.geo_match_statement.#": "1", + "statement.0.geo_match_statement.0.country_codes.#": "2", + "statement.0.geo_match_statement.0.country_codes.0": "US", + "statement.0.geo_match_statement.0.country_codes.1": "CA", + "statement.0.geo_match_statement.0.forwarded_ip_config.#": "0", + "visibility_config.#": "1", + "visibility_config.0.cloudwatch_metrics_enabled": "false", + "visibility_config.0.metric_name": "friendly-rule-metric-name", + "visibility_config.0.sampled_requests_enabled": "false", + }), + resource.TestCheckResourceAttr(resourceName, "visibility_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.cloudwatch_metrics_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.metric_name", "friendly-metric-name"), + resource.TestCheckResourceAttr(resourceName, "visibility_config.0.sampled_requests_enabled", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2WebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + +func TestAccAwsWafv2WebACL_GeoMatchStatement_ForwardedIPConfig(t *testing.T) { + var v wafv2.WebACL + webACLName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_wafv2_web_acl.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsWafv2WebACLDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement_ForwardedIPConfig(webACLName, "MATCH", "X-Forwarded-For"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.or_statement.#": "1", + "statement.0.or_statement.0.statement.#": "2", + "statement.0.or_statement.0.statement.0.geo_match_statement.#": "1", + "statement.0.or_statement.0.statement.0.geo_match_statement.0.country_codes.#": "1", + "statement.0.or_statement.0.statement.0.geo_match_statement.0.forwarded_ip_config.#": "0", + "statement.0.or_statement.0.statement.1.geo_match_statement.#": "1", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.country_codes.#": "1", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.forwarded_ip_config.#": "1", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.forwarded_ip_config.0.fallback_behavior": "MATCH", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.forwarded_ip_config.0.header_name": "X-Forwarded-For", + }), + ), + }, + { + Config: testAccAwsWafv2WebACLConfig_GeoMatchStatement_ForwardedIPConfig(webACLName, "NO_MATCH", "Updated"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsWafv2WebACLExists(resourceName, &v), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "wafv2", regexp.MustCompile(`regional/webacl/.+$`)), + resource.TestCheckResourceAttr(resourceName, "name", webACLName), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{ + "statement.#": "1", + "statement.0.or_statement.#": "1", + "statement.0.or_statement.0.statement.#": "2", + "statement.0.or_statement.0.statement.0.geo_match_statement.#": "1", + "statement.0.or_statement.0.statement.0.geo_match_statement.0.country_codes.#": "1", + "statement.0.or_statement.0.statement.0.geo_match_statement.0.forwarded_ip_config.#": "0", + "statement.0.or_statement.0.statement.1.geo_match_statement.#": "1", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.country_codes.#": "1", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.forwarded_ip_config.#": "1", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.forwarded_ip_config.0.fallback_behavior": "NO_MATCH", + "statement.0.or_statement.0.statement.1.geo_match_statement.0.forwarded_ip_config.0.header_name": "Updated", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsWafv2WebACLImportStateIdFunc(resourceName), + }, + }, + }) +} + func TestAccAwsWafv2WebACL_RateBasedStatement_ForwardedIPConfig(t *testing.T) { var v wafv2.WebACL webACLName := acctest.RandomWithPrefix("tf-acc-test") @@ -1174,6 +1329,107 @@ resource "aws_wafv2_web_acl" "test" { `, name, ruleName1, priority1, ruleName2, priority2) } +func testAccAwsWafv2WebACLConfig_GeoMatchStatement(name, countryCodes string) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = "%[1]s" + description = "%[1]s" + scope = "REGIONAL" + + default_action { + allow {} + } + + rule { + name = "rule-1" + priority = 1 + + action { + block {} + } + + statement { + geo_match_statement { + country_codes = [%[2]s] + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + tags = { + Tag1 = "Value1" + Tag2 = "Value2" + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, countryCodes) +} + +func testAccAwsWafv2WebACLConfig_GeoMatchStatement_ForwardedIPConfig(name, fallbackBehavior, headerName string) string { + return fmt.Sprintf(` +resource "aws_wafv2_web_acl" "test" { + name = "%[1]s" + description = "%[1]s" + scope = "REGIONAL" + + default_action { + block {} + } + + rule { + name = "rule-1" + priority = 1 + + action { + block {} + } + + statement { + or_statement { + statement { + geo_match_statement { + country_codes = ["US"] + } + } + + statement { + geo_match_statement { + country_codes = ["CA"] + forwarded_ip_config { + fallback_behavior = "%s" + header_name = "%s" + } + } + } + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-rule-metric-name" + sampled_requests_enabled = false + } + } + + visibility_config { + cloudwatch_metrics_enabled = false + metric_name = "friendly-metric-name" + sampled_requests_enabled = false + } +} +`, name, fallbackBehavior, headerName) +} + func testAccAwsWafv2WebACLConfig_ManagedRuleGroupStatement(name string) string { return fmt.Sprintf(` resource "aws_wafv2_web_acl" "test" {