Skip to content

Commit

Permalink
wafv2: add support for captcha in rule actions
Browse files Browse the repository at this point in the history
We can now configure rules to run a CAPTCHA check against web requests and, as needed, send a CAPTCHA challenge to the client
  • Loading branch information
Yuri committed Nov 13, 2021
1 parent 1338345 commit 8fb1370
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .changelog/21766.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_wafv2_web_acl: Add `captcha` argument in rule actions
```
53 changes: 53 additions & 0 deletions internal/service/wafv2/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,19 @@ func wafv2CountConfigSchema() *schema.Schema {
}
}

func wafv2CaptchaConfigSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"custom_request_handling": wafv2CustomRequestHandlingSchema(),
},
},
}
}

func wafv2BlockConfigSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Expand Down Expand Up @@ -574,6 +587,10 @@ func expandWafv2RuleAction(l []interface{}) *wafv2.RuleAction {
action.Count = expandWafv2CountAction(v.([]interface{}))
}

if v, ok := m["captcha"]; ok && len(v.([]interface{})) > 0 {
action.Captcha = expandWafv2CaptchaAction(v.([]interface{}))
}

return action
}

Expand Down Expand Up @@ -615,6 +632,25 @@ func expandWafv2CountAction(l []interface{}) *wafv2.CountAction {
return action
}

func expandWafv2CaptchaAction(l []interface{}) *wafv2.CaptchaAction {
action := &wafv2.CaptchaAction{}

if len(l) == 0 || l[0] == nil {
return action
}

m, ok := l[0].(map[string]interface{})
if !ok {
return action
}

if v, ok := m["custom_request_handling"].([]interface{}); ok && len(v) > 0 {
action.CustomRequestHandling = expandWafv2CustomRequestHandling(v)
}

return action
}

func expandWafv2BlockAction(l []interface{}) *wafv2.BlockAction {
action := &wafv2.BlockAction{}

Expand Down Expand Up @@ -1094,6 +1130,10 @@ func flattenWafv2RuleAction(a *wafv2.RuleAction) interface{} {
m["count"] = flattenWafv2Count(a.Count)
}

if a.Captcha != nil {
m["captcha"] = flattenWafv2Captcha(a.Captcha)
}

return []interface{}{m}
}

Expand Down Expand Up @@ -1137,6 +1177,19 @@ func flattenWafv2Count(a *wafv2.CountAction) []interface{} {
return []interface{}{m}
}

func flattenWafv2Captcha(a *wafv2.CaptchaAction) []interface{} {
if a == nil {
return []interface{}{}
}
m := map[string]interface{}{}

if a.CustomRequestHandling != nil {
m["custom_request_handling"] = flattenWafv2CustomRequestHandling(a.CustomRequestHandling)
}

return []interface{}{m}
}

func flattenWafv2CustomRequestHandling(c *wafv2.CustomRequestHandling) []interface{} {
if c == nil {
return []interface{}{}
Expand Down
7 changes: 4 additions & 3 deletions internal/service/wafv2/web_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ func ResourceWebACL() *schema.Resource {
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allow": wafv2AllowConfigSchema(),
"block": wafv2BlockConfigSchema(),
"count": wafv2CountConfigSchema(),
"allow": wafv2AllowConfigSchema(),
"block": wafv2BlockConfigSchema(),
"count": wafv2CountConfigSchema(),
"captcha": wafv2CaptchaConfigSchema(),
},
},
},
Expand Down
85 changes: 85 additions & 0 deletions internal/service/wafv2/web_acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,38 @@ func TestAccWAFV2WebACL_Custom_requestHandling(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "visibility_config.0.sampled_requests_enabled", "false"),
),
},
{
Config: testAccWebACLConfig_CustomRequestHandling_captcha(webACLName, "x-hdr1", "x-hdr2"),
Check: resource.ComposeTestCheckFunc(
testAccCheckWebACLExists(resourceName, &v),
acctest.MatchResourceAttrRegionalARN(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"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{
"name": "rule-1",
"action.#": "1",
"action.0.allow.#": "0",
"action.0.block.#": "0",
"action.0.count.#": "0",
"action.0.captcha.#": "1",
"action.0.captcha.0.custom_request_handling.#": "1",
"action.0.captcha.0.custom_request_handling.0.insert_header.#": "2",
"action.0.captcha.0.custom_request_handling.0.insert_header.0.name": "x-hdr1",
"action.0.captcha.0.custom_request_handling.0.insert_header.0.value": "test-value-1",
"action.0.captcha.0.custom_request_handling.0.insert_header.1.name": "x-hdr2",
"action.0.captcha.0.custom_request_handling.0.insert_header.1.value": "test-value-2",
"priority": "1",
}),
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,
Expand Down Expand Up @@ -1707,6 +1739,59 @@ resource "aws_wafv2_web_acl" "test" {
`, name, firstHeader, secondHeader)
}

func testAccWebACLConfig_CustomRequestHandling_captcha(name, firstHeader string, secondHeader string) string {
return fmt.Sprintf(`
resource "aws_wafv2_web_acl" "test" {
name = %[1]q
description = %[1]q
scope = "REGIONAL"
default_action {
allow {}
}
rule {
name = "rule-1"
priority = 1
action {
captcha {
custom_request_handling {
insert_header {
name = %[2]q
value = "test-value-1"
}
insert_header {
name = %[3]q
value = "test-value-2"
}
}
}
}
statement {
geo_match_statement {
country_codes = ["US", "CA"]
}
}
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, firstHeader, secondHeader)
}

func testAccWebACLConfig_CustomRequestHandling_allow(name, firstHeader string, secondHeader string) string {
return fmt.Sprintf(`
resource "aws_wafv2_web_acl" "test" {
Expand Down
9 changes: 8 additions & 1 deletion website/docs/r/wafv2_web_acl.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,12 @@ Each `rule` supports the following arguments:

The `action` block supports the following arguments:

~> **NOTE:** One of `allow`, `block`, or `count`, is required when specifying an `action`.
~> **NOTE:** One of `allow`, `block`, `count` or `captcha`, is required when specifying an `action`.

* `allow` - (Optional) Instructs AWS WAF to allow the web request. See [Allow](#action) below for details.
* `block` - (Optional) Instructs AWS WAF to block the web request. See [Block](#block) below for details.
* `count` - (Optional) Instructs AWS WAF to count the web request and allow it. See [Count](#count) below for details.
* `captcha` - (Optional) Instructs AWS WAF to run a CAPTCHA check against the web request. See [Captcha](#captcha) below for details.

### Override Action

Expand Down Expand Up @@ -328,6 +329,12 @@ The `count` block supports the following arguments:

* `custom_request_handling` - (Optional) Defines custom handling for the web request. See [Custom Request Handling](#custom-request-handling) below for details.

### Captcha

The `captcha` block supports the following arguments:

* `custom_request_handling` - (Optional) Defines custom handling for the web request. See [Custom Request Handling](#custom-request-handling) below for details.

### Custom Request Handling

The `custom_request_handling` block supports the following arguments:
Expand Down

0 comments on commit 8fb1370

Please sign in to comment.