Skip to content

Commit

Permalink
Extended the wafv2_web_acl module with custom_response_bodies argument (
Browse files Browse the repository at this point in the history
ansible-collections#721)

Extended the wafv2_web_acl module with custom_response_bodies argument

SUMMARY
Extended the wafv2_web_acl module to also take the custom_response_bodies argument, improved docs and extended tests
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
wafv2_web_acl
ADDITIONAL INFORMATION
Also touched docs of aws_waf_web_acl to make it easier to find the WAF v2 modules as I had trouble finding that at first.

Reviewed-by: Markus Bergholz <git@osuv.de>
Reviewed-by: Stefan Horning <None>
Reviewed-by: Mark Chappell <None>
Reviewed-by: Alina Buzachis <None>
  • Loading branch information
stefanhorning authored Feb 10, 2022
1 parent 239136b commit c91acf6
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 177 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/721-wafv2_web_acl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- wafv2_web_acl - Extended the wafv2_web_acl module to also take the ``custom_response_bodies`` argument (https://github.com/ansible-collections/community.aws/pull/721).
- wafv2_web_acl - Documentation updates wafv2_web_acl and aws_waf_web_acl (https://github.com/ansible-collections/community.aws/pull/721).
6 changes: 3 additions & 3 deletions plugins/modules/aws_waf_web_acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

DOCUMENTATION = r'''
module: aws_waf_web_acl
short_description: Create and delete WAF Web ACLs.
short_description: Create and delete WAF Web ACLs
version_added: 1.0.0
description:
- Read the AWS documentation for WAF
U(https://aws.amazon.com/documentation/waf/).
- Module for WAF classic, for WAF v2 use the I(wafv2_*) modules.
- Read the AWS documentation for WAF U(https://docs.aws.amazon.com/waf/latest/developerguide/classic-waf-chapter.html).
author:
- Mike Mochan (@mmochan)
Expand Down
144 changes: 116 additions & 28 deletions plugins/modules/wafv2_web_acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
version_added: 1.5.0
author:
- "Markus Bergholz (@markuman)"
short_description: wafv2_web_acl
short_description: Create and delete WAF Web ACLs
description:
- Create, modify or delete a wafv2 web acl.
- Create, modify or delete AWS WAF v2 web ACLs (not for classic WAF).
- See docs at U(https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html)
options:
state:
description:
Expand All @@ -28,9 +29,9 @@
type: str
scope:
description:
- Scope of wafv2 web acl.
- Geographical scope of the web acl.
required: true
choices: ["CLOUDFRONT","REGIONAL"]
choices: ["CLOUDFRONT", "REGIONAL"]
type: str
description:
description:
Expand All @@ -39,7 +40,7 @@
default_action:
description:
- Default action of the wafv2 web acl.
choices: ["Block","Allow"]
choices: ["Block", "Allow"]
type: str
sampled_requests:
description:
Expand Down Expand Up @@ -87,6 +88,14 @@
description:
- Rule configuration.
type: dict
custom_response_bodies:
description:
- A map of custom response keys and content bodies. Define response bodies here and reference them in the rules by providing
- the key of the body dictionary element.
- Each element must have a unique dict key and in the dict two keys for I(content_type) and I(content).
- Requires botocore >= 1.21.0
type: dict
version_added: 3.1.0
purge_rules:
description:
- When set to C(no), keep the existing load balancer rules in place. Will modify and add, but will not delete.
Expand All @@ -100,16 +109,15 @@
'''

EXAMPLES = '''
- name: create web acl
- name: Create test web acl
community.aws.wafv2_web_acl:
name: test05
state: present
description: hallo eins
scope: REGIONAL
default_action: Allow
sampled_requests: no
cloudwatch_metrics: yes
metric_name: blub
metric_name: test05-acl-metric
rules:
- name: zwei
priority: 0
Expand Down Expand Up @@ -191,10 +199,56 @@
text_transformations:
- type: LOWERCASE
priority: 0
purge_rules: yes
tags:
A: B
C: D
register: out
state: present
- name: Create IP filtering web ACL
community.aws.wafv2_web_acl:
name: ip-filtering-traffic
description: ACL that filters web traffic based on rate limits and whitelists some IPs
scope: REGIONAL
default_action: Allow
sampled_requests: yes
cloudwatch_metrics: yes
metric_name: ip-filtering-traffic
rules:
- name: whitelist-own-IPs
priority: 0
action:
allow: {}
statement:
ip_set_reference_statement:
arn: 'arn:aws:wafv2:us-east-1:520789123123:regional/ipset/own-public-ips/1c4bdfc4-0f77-3b23-5222-123123123'
visibility_config:
sampled_requests_enabled: yes
cloud_watch_metrics_enabled: yes
metric_name: waf-acl-rule-whitelist-own-IPs
- name: rate-limit-per-IP
priority: 1
action:
block:
custom_response:
response_code: 429
custom_response_body_key: too_many_requests
statement:
rate_based_statement:
limit: 5000
aggregate_key_type: IP
visibility_config:
sampled_requests_enabled: yes
cloud_watch_metrics_enabled: yes
metric_name: waf-acl-rule-rate-limit-per-IP
purge_rules: yes
custom_response_bodies:
too_many_requests:
content_type: APPLICATION_JSON
content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }'
region: us-east-1
state: present
'''

RETURN = """
Expand All @@ -218,6 +272,12 @@
sample: test02
returned: Always, as long as the web acl exists
type: str
default_action:
description: Default action of ACL
returned: Always, as long as the web acl exists
sample:
allow: {}
type: dict
rules:
description: Current rules of the web acl
returned: Always, as long as the web acl exists
Expand All @@ -235,6 +295,14 @@
cloud_watch_metrics_enabled: true
metric_name: admin_protect
sampled_requests_enabled: true
custom_response_bodies:
description: Custom response body configurations to be used in rules
type: dict
sample:
too_many_requests:
content_type: APPLICATION_JSON
content: '{ message: "Your request has been blocked due to too many HTTP requests coming from your IP" }'
returned: Always, as long as the web acl exists
visibility_config:
description: Visibility config of the web acl
returned: Always, as long as the web acl exists
Expand Down Expand Up @@ -267,22 +335,27 @@ def __init__(self, wafv2, name, scope, fail_json_aws):
self.fail_json_aws = fail_json_aws
self.existing_acl, self.id, self.locktoken = self.get_web_acl()

def update(self, default_action, description, rules, sampled_requests, cloudwatch_metrics, metric_name):
def update(self, default_action, description, rules, sampled_requests, cloudwatch_metrics, metric_name, custom_response_bodies):
req_obj = {
'Name': self.name,
'Scope': self.scope,
'Id': self.id,
'DefaultAction': default_action,
'Description': description,
'Rules': rules,
'VisibilityConfig': {
'SampledRequestsEnabled': sampled_requests,
'CloudWatchMetricsEnabled': cloudwatch_metrics,
'MetricName': metric_name
},
'LockToken': self.locktoken
}

if custom_response_bodies:
req_obj['CustomResponseBodies'] = custom_response_bodies

try:
response = self.wafv2.update_web_acl(
Name=self.name,
Scope=self.scope,
Id=self.id,
DefaultAction=default_action,
Description=description,
Rules=rules,
VisibilityConfig={
'SampledRequestsEnabled': sampled_requests,
'CloudWatchMetricsEnabled': cloudwatch_metrics,
'MetricName': metric_name
},
LockToken=self.locktoken
)
response = self.wafv2.update_web_acl(**req_obj)
except (BotoCoreError, ClientError) as e:
self.fail_json_aws(e, msg="Failed to update wafv2 web acl.")
return response
Expand Down Expand Up @@ -331,7 +404,7 @@ def get_web_acl(self):
def list(self):
return wafv2_list_web_acls(self.wafv2, self.scope, self.fail_json_aws)

def create(self, default_action, rules, sampled_requests, cloudwatch_metrics, metric_name, tags, description):
def create(self, default_action, rules, sampled_requests, cloudwatch_metrics, metric_name, tags, description, custom_response_bodies):
req_obj = {
'Name': self.name,
'Scope': self.scope,
Expand All @@ -343,6 +416,9 @@ def create(self, default_action, rules, sampled_requests, cloudwatch_metrics, me
'MetricName': metric_name
}
}

if custom_response_bodies:
req_obj['CustomResponseBodies'] = custom_response_bodies
if description:
req_obj['Description'] = description
if tags:
Expand Down Expand Up @@ -370,6 +446,7 @@ def main():
cloudwatch_metrics=dict(type='bool', default=True),
metric_name=dict(type='str'),
tags=dict(type='dict'),
custom_response_bodies=dict(type='dict'),
purge_rules=dict(default=True, type='bool')
)

Expand All @@ -392,6 +469,14 @@ def main():
purge_rules = module.params.get("purge_rules")
check_mode = module.check_mode

custom_response_bodies = module.params.get("custom_response_bodies")
if custom_response_bodies:
module.require_botocore_at_least('1.21.0', reason='to set custom response bodies')
custom_response_bodies = {}

for custom_name, body in module.params.get("custom_response_bodies").items():
custom_response_bodies[custom_name] = snake_dict_to_camel_dict(body, capitalize_first=True)

if default_action == 'Block':
default_action = {'Block': {}}
elif default_action == 'Allow':
Expand Down Expand Up @@ -422,7 +507,8 @@ def main():
rules,
sampled_requests,
cloudwatch_metrics,
metric_name
metric_name,
custom_response_bodies
)

else:
Expand All @@ -438,7 +524,8 @@ def main():
cloudwatch_metrics,
metric_name,
tags,
description
description,
custom_response_bodies
)

elif state == 'absent':
Expand All @@ -453,7 +540,8 @@ def main():
rules,
sampled_requests,
cloudwatch_metrics,
metric_name
metric_name,
custom_response_bodies
)
else:
change = True
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/targets/wafv2/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dependencies:
- setup_remote_tmp_dir
- role: setup_botocore_pip
vars:
boto3_version: "1.18.0"
botocore_version: "1.21.0"
1 change: 0 additions & 1 deletion tests/integration/targets/wafv2/tasks/alb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,5 @@

- assert:
that:
- alb.changed
- alb.listeners|length == 1
- alb.listeners[0].rules|length == 1
Loading

0 comments on commit c91acf6

Please sign in to comment.