From c8e179fbf1568612bdd5fb41720a25dd192b8caa Mon Sep 17 00:00:00 2001 From: mjmayer Date: Thu, 16 May 2019 17:36:14 -0700 Subject: [PATCH] Aws waf region (#48953) * Add waiter for AWSRegional * Add support for WAF Regional * Add support for regional waf web acl * Remove set_trace, pep formatting * Add paginator for regional_waf * Change name of param for waf_regional This is more in line with how AWS refers to the service. Additional changes made to how client is called. Used ternary to reduce if statements * Change parameter name to waf_regional * Add support for removal waf regional condition * Change parameter from cloudfront to waf_regional * Added state: absent waf rule * Remove set_trace * Add integration tests for waf regional * WIP: adding region parameter to tests * Add support for waf facts module * Add region to waf regional integration tests * Update security policy for waf regional testing * Add type to documentation for waf_regional param --- .../testing_policies/security-policy.json | 6 + lib/ansible/module_utils/aws/waf.py | 25 +- lib/ansible/module_utils/aws/waiters.py | 6 + .../modules/cloud/amazon/aws_waf_condition.py | 17 +- .../modules/cloud/amazon/aws_waf_facts.py | 16 +- .../modules/cloud/amazon/aws_waf_rule.py | 39 +- .../modules/cloud/amazon/aws_waf_web_acl.py | 48 +- .../targets/aws_waf_web_acl/tasks/main.yml | 629 ++++++++++++++++++ 8 files changed, 758 insertions(+), 28 deletions(-) diff --git a/hacking/aws_config/testing_policies/security-policy.json b/hacking/aws_config/testing_policies/security-policy.json index b1760a6bec9..93bac5dd397 100644 --- a/hacking/aws_config/testing_policies/security-policy.json +++ b/hacking/aws_config/testing_policies/security-policy.json @@ -90,6 +90,12 @@ "Resource": [ "arn:aws:logs:{{aws_region}}:{{aws_account}}:log-group:ansible-testing*" ] + }, + { + "Sid": "AllowWAFRegionalusage", + "Action": "waf-regional:*", + "Effect": "Allow", + "Resource": "*" } ] } diff --git a/lib/ansible/module_utils/aws/waf.py b/lib/ansible/module_utils/aws/waf.py index 39de2202277..f160a8b5ac9 100644 --- a/lib/ansible/module_utils/aws/waf.py +++ b/lib/ansible/module_utils/aws/waf.py @@ -162,15 +162,38 @@ def list_rules_with_backoff(client): return paginator.paginate().build_full_result()['Rules'] +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def list_regional_rules_with_backoff(client): + resp = client.list_rules() + rules = [] + while resp: + rules += resp['Rules'] + resp = client.list_rules(NextMarker=resp['NextMarker']) if 'NextMarker' in resp else None + return rules + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) def list_web_acls_with_backoff(client): paginator = client.get_paginator('list_web_acls') return paginator.paginate().build_full_result()['WebACLs'] +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def list_regional_web_acls_with_backoff(client): + resp = client.list_web_acls() + acls = [] + while resp: + acls += resp['WebACLs'] + resp = client.list_web_acls(NextMarker=resp['NextMarker']) if 'NextMarker' in resp else None + return acls + + def list_web_acls(client, module): try: - return list_web_acls_with_backoff(client) + if client.__class__.__name__ == 'WAF': + return list_web_acls_with_backoff(client) + elif client.__class__.__name__ == 'WAFRegional': + return list_regional_web_acls_with_backoff(client) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't obtain web acls") diff --git a/lib/ansible/module_utils/aws/waiters.py b/lib/ansible/module_utils/aws/waiters.py index 5b7da179387..f62750e51db 100644 --- a/lib/ansible/module_utils/aws/waiters.py +++ b/lib/ansible/module_utils/aws/waiters.py @@ -305,6 +305,12 @@ waiters_by_name = { core_waiter.NormalizedOperationMethod( waf.get_change_token_status )), + ('WAFRegional', 'change_token_in_sync'): lambda waf: core_waiter.Waiter( + 'change_token_in_sync', + waf_model('ChangeTokenInSync'), + core_waiter.NormalizedOperationMethod( + waf.get_change_token_status + )), ('EKS', 'cluster_active'): lambda eks: core_waiter.Waiter( 'cluster_active', eks_model('ClusterActive'), diff --git a/lib/ansible/modules/cloud/amazon/aws_waf_condition.py b/lib/ansible/modules/cloud/amazon/aws_waf_condition.py index 90b4c870088..2da808b5274 100644 --- a/lib/ansible/modules/cloud/amazon/aws_waf_condition.py +++ b/lib/ansible/modules/cloud/amazon/aws_waf_condition.py @@ -57,6 +57,12 @@ options: - Whether to remove existing filters from a condition if not passed in I(filters). default: False type: bool + waf_regional: + description: Whether to use waf_regional module. Defaults to false. + default: false + required: no + type: bool + version_added: 2.9 state: description: Whether the condition should be C(present) or C(absent). choices: @@ -335,7 +341,7 @@ from ansible.module_utils.aws.core import AnsibleAWSModule from ansible.module_utils.ec2 import boto3_conn, get_aws_connection_info, ec2_argument_spec from ansible.module_utils.ec2 import camel_dict_to_snake_dict, AWSRetry, compare_policies from ansible.module_utils.aws.waf import run_func_with_change_token_backoff, MATCH_LOOKUP -from ansible.module_utils.aws.waf import get_rule_with_backoff, list_rules_with_backoff +from ansible.module_utils.aws.waf import get_rule_with_backoff, list_rules_with_backoff, list_regional_rules_with_backoff class Condition(object): @@ -519,7 +525,10 @@ class Condition(object): def find_condition_in_rules(self, condition_set_id): rules_in_use = [] try: - all_rules = list_rules_with_backoff(self.client) + if self.client.__class__.__name__ == 'WAF': + all_rules = list_rules_with_backoff(self.client) + elif self.client.__class__.__name__ == 'WAFRegional': + all_rules = list_regional_rules_with_backoff(self.client) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg='Could not list rules') for rule in all_rules: @@ -636,6 +645,7 @@ def main(): type=dict(required=True, choices=['byte', 'geo', 'ip', 'regex', 'size', 'sql', 'xss']), filters=dict(type='list'), purge_filters=dict(type='bool', default=False), + waf_regional=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent']), ), ) @@ -644,7 +654,8 @@ def main(): state = module.params.get('state') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) - client = boto3_conn(module, conn_type='client', resource='waf', region=region, endpoint=ec2_url, **aws_connect_kwargs) + resource = 'waf' if not module.params['waf_regional'] else 'waf-regional' + client = boto3_conn(module, conn_type='client', resource=resource, region=region, endpoint=ec2_url, **aws_connect_kwargs) condition = Condition(client, module) diff --git a/lib/ansible/modules/cloud/amazon/aws_waf_facts.py b/lib/ansible/modules/cloud/amazon/aws_waf_facts.py index 0e870629b16..37aaecc0d5d 100644 --- a/lib/ansible/modules/cloud/amazon/aws_waf_facts.py +++ b/lib/ansible/modules/cloud/amazon/aws_waf_facts.py @@ -17,6 +17,12 @@ options: name: description: - The name of a Web Application Firewall + waf_regional: + description: Wether to use waf_regional module. Defaults to true + default: false + required: no + type: bool + version_added: "2.9" author: - Mike Mochan (@mmochan) @@ -33,6 +39,11 @@ EXAMPLES = ''' - name: obtain all facts for a single WAF aws_waf_facts: name: test_waf + +- name: obtain all facts for a single WAF Regional + aws_waf_facts: + name: test_waf + waf_regional: true ''' RETURN = ''' @@ -113,13 +124,14 @@ def main(): argument_spec.update( dict( name=dict(required=False), + waf_regional=dict(type='bool', default=False), ) ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) - client = boto3_conn(module, conn_type='client', resource='waf', region=region, endpoint=ec2_url, **aws_connect_kwargs) - + resource = 'waf' if not module.params['waf_regional'] else 'waf-regional' + client = boto3_conn(module, conn_type='client', resource=resource, region=region, endpoint=ec2_url, **aws_connect_kwargs) web_acls = list_web_acls(client, module) name = module.params['name'] if name: diff --git a/lib/ansible/modules/cloud/amazon/aws_waf_rule.py b/lib/ansible/modules/cloud/amazon/aws_waf_rule.py index 53a0dde9458..1866853c8c1 100644 --- a/lib/ansible/modules/cloud/amazon/aws_waf_rule.py +++ b/lib/ansible/modules/cloud/amazon/aws_waf_rule.py @@ -47,8 +47,14 @@ options: purge_conditions: description: - Whether or not to remove conditions that are not passed when updating `conditions`. - default: False + default: false type: bool + waf_regional: + description: Whether to use waf_regional module. Defaults to false + default: false + required: no + type: bool + version_added: "2.9" ''' EXAMPLES = ''' @@ -127,8 +133,8 @@ except ImportError: from ansible.module_utils.aws.core import AnsibleAWSModule from ansible.module_utils.ec2 import boto3_conn, get_aws_connection_info, ec2_argument_spec from ansible.module_utils.ec2 import camel_dict_to_snake_dict -from ansible.module_utils.aws.waf import run_func_with_change_token_backoff, list_rules_with_backoff, MATCH_LOOKUP -from ansible.module_utils.aws.waf import get_web_acl_with_backoff, list_web_acls_with_backoff +from ansible.module_utils.aws.waf import run_func_with_change_token_backoff, list_rules_with_backoff, list_regional_rules_with_backoff, MATCH_LOOKUP +from ansible.module_utils.aws.waf import get_web_acl_with_backoff, list_web_acls_with_backoff, list_regional_web_acls_with_backoff def get_rule_by_name(client, module, name): @@ -145,8 +151,21 @@ def get_rule(client, module, rule_id): def list_rules(client, module): + if client.__class__.__name__ == 'WAF': + try: + return list_rules_with_backoff(client) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Could not list WAF rules') + elif client.__class__.__name__ == 'WAFRegional': + try: + return list_regional_rules_with_backoff(client) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Could not list WAF Regional rules') + + +def list_regional_rules(client, module): try: - return list_rules_with_backoff(client) + return list_regional_rules_with_backoff(client) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not list WAF rules') @@ -260,7 +279,10 @@ def ensure_rule_present(client, module): def find_rule_in_web_acls(client, module, rule_id): web_acls_in_use = [] try: - all_web_acls = list_web_acls_with_backoff(client) + if client.__class__.__name__ == 'WAF': + all_web_acls = list_web_acls_with_backoff(client) + elif client.__class__.__name__ == 'WAFRegional': + all_web_acls = list_regional_web_acls_with_backoff(client) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not list Web ACLs') for web_acl in all_web_acls: @@ -297,15 +319,16 @@ def main(): metric_name=dict(), state=dict(default='present', choices=['present', 'absent']), conditions=dict(type='list'), - purge_conditions=dict(type='bool', default=False) + purge_conditions=dict(type='bool', default=False), + waf_regional=dict(type='bool', default=False), ), ) module = AnsibleAWSModule(argument_spec=argument_spec) state = module.params.get('state') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) - client = boto3_conn(module, conn_type='client', resource='waf', region=region, endpoint=ec2_url, **aws_connect_kwargs) - + resource = 'waf' if not module.params['waf_regional'] else 'waf-regional' + client = boto3_conn(module, conn_type='client', resource=resource, region=region, endpoint=ec2_url, **aws_connect_kwargs) if state == 'present': (changed, results) = ensure_rule_present(client, module) else: diff --git a/lib/ansible/modules/cloud/amazon/aws_waf_web_acl.py b/lib/ansible/modules/cloud/amazon/aws_waf_web_acl.py index 607c7bd7be6..f664a7760c7 100644 --- a/lib/ansible/modules/cloud/amazon/aws_waf_web_acl.py +++ b/lib/ansible/modules/cloud/amazon/aws_waf_web_acl.py @@ -49,12 +49,17 @@ options: - Each rule must contain I(name), I(action), I(priority) keys. - Priorities must be unique, but not necessarily consecutive. Lower numbered priorities are evalauted first. - The I(type) key can be passed as C(rate_based), it defaults to C(regular) - purge_rules: description: - Whether to remove rules that aren't passed with C(rules). default: False type: bool + waf_regional: + description: Whether to use waf_regional module. Defaults to false. + default: false + required: no + type: bool + version_added: "2.9" ''' EXAMPLES = ''' @@ -140,7 +145,8 @@ import re from ansible.module_utils.aws.core import AnsibleAWSModule from ansible.module_utils.aws.waiters import get_waiter from ansible.module_utils.ec2 import boto3_conn, get_aws_connection_info, ec2_argument_spec, camel_dict_to_snake_dict -from ansible.module_utils.aws.waf import list_rules_with_backoff, list_web_acls_with_backoff, run_func_with_change_token_backoff +from ansible.module_utils.aws.waf import list_rules_with_backoff, list_web_acls_with_backoff, list_regional_web_acls_with_backoff, \ + run_func_with_change_token_backoff, list_regional_rules_with_backoff def get_web_acl_by_name(client, module, name): @@ -152,11 +158,18 @@ def get_web_acl_by_name(client, module, name): def create_rule_lookup(client, module): - try: - rules = list_rules_with_backoff(client) - return dict((rule['Name'], rule) for rule in rules) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Could not list rules') + if client.__class__.__name__ == 'WAF': + try: + rules = list_rules_with_backoff(client) + return dict((rule['Name'], rule) for rule in rules) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Could not list rules') + elif client.__class__.__name__ == 'WAFRegional': + try: + rules = list_regional_rules_with_backoff(client) + return dict((rule['Name'], rule) for rule in rules) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Could not list regional rules') def get_web_acl(client, module, web_acl_id): @@ -167,10 +180,16 @@ def get_web_acl(client, module, web_acl_id): def list_web_acls(client, module,): - try: - return list_web_acls_with_backoff(client) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Could not get Web ACLs') + if client.__class__.__name__ == 'WAF': + try: + return list_web_acls_with_backoff(client) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Could not get Web ACLs') + elif client.__class__.__name__ == 'WAFRegional': + try: + return list_regional_web_acls_with_backoff(client) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Could not get Web ACLs') def find_and_update_web_acl(client, module, web_acl_id): @@ -296,7 +315,8 @@ def main(): metric_name=dict(), state=dict(default='present', choices=['present', 'absent']), rules=dict(type='list'), - purge_rules=dict(type='bool', default=False) + purge_rules=dict(type='bool', default=False), + waf_regional=dict(type='bool', default=False), ), ) module = AnsibleAWSModule(argument_spec=argument_spec, @@ -304,8 +324,8 @@ def main(): state = module.params.get('state') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) - client = boto3_conn(module, conn_type='client', resource='waf', region=region, endpoint=ec2_url, **aws_connect_kwargs) - + resource = 'waf' if not module.params['waf_regional'] else 'waf-regional' + client = boto3_conn(module, conn_type='client', resource=resource, region=region, endpoint=ec2_url, **aws_connect_kwargs) if state == 'present': (changed, results) = ensure_web_acl_present(client, module) else: diff --git a/test/integration/targets/aws_waf_web_acl/tasks/main.yml b/test/integration/targets/aws_waf_web_acl/tasks/main.yml index e1dac5fca75..6b58e0b3929 100644 --- a/test/integration/targets/aws_waf_web_acl/tasks/main.yml +++ b/test/integration/targets/aws_waf_web_acl/tasks/main.yml @@ -233,6 +233,257 @@ recreate_waf_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id != create_waf_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id + - name: create WAF Regional IP condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "10.0.0.0/8" + type: ip + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_ip_condition + + - name: add an IP address to WAF Regional condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "10.0.0.0/8" + - ip_address: "192.168.0.0/24" + type: ip + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: add_ip_address_to_waf_regional_condition + + - name: check expected WAF Regional filter length + assert: + that: + - add_ip_address_to_waf_regional_condition.condition.ip_set_descriptors|length == 2 + + - name: add an IP address to WAF Regional condition (rely on purge_filters defaulting to false) + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "192.168.10.0/24" + type: ip + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: add_ip_address_to_waf_regional_condition_no_purge + + - name: check WAF Regional filter length has increased + assert: + that: + - add_ip_address_to_waf_regional_condition_no_purge.condition.ip_set_descriptors|length == 3 + - add_ip_address_to_waf_regional_condition_no_purge.changed + + - name: add an IP address to WAF Regional condition (set purge_filters) + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + filters: + - ip_address: "192.168.20.0/24" + purge_filters: yes + type: ip + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: add_ip_address_to_waf_regional_condition_purge + + - name: check WAF Regional filter length has reduced + assert: + that: + - add_ip_address_to_waf_regional_condition_purge.condition.ip_set_descriptors|length == 1 + - add_ip_address_to_waf_regional_condition_purge.changed + + - name: create WAF Regional byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + filters: + - field_to_match: header + position: STARTS_WITH + target_string: Hello + header: Content-type + type: byte + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_byte_condition + + - name: recreate WAF Regional byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + filters: + - field_to_match: header + position: STARTS_WITH + target_string: Hello + header: Content-type + type: byte + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: recreate_waf_regional_byte_condition + + - name: assert that no change was made + assert: + that: + - not recreate_waf_regional_byte_condition.changed + + - name: create WAF Regional geo condition + aws_waf_condition: + name: "{{ resource_prefix }}_geo_condition" + filters: + - country: US + - country: AU + - country: AT + type: geo + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_geo_condition + + - name: create WAF Regional size condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + filters: + - field_to_match: query_string + size: 300 + comparison: GT + type: size + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_size_condition + + - name: create WAF Regional sql condition + aws_waf_condition: + name: "{{ resource_prefix }}_sql_condition" + filters: + - field_to_match: query_string + transformation: url_decode + type: sql + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_sql_condition + + - name: create WAF Regional xss condition + aws_waf_condition: + name: "{{ resource_prefix }}_xss_condition" + filters: + - field_to_match: query_string + transformation: url_decode + type: xss + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_xss_condition + + - name: create WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_regex_condition + + - name: create a second WAF Regional regex condition with the same regex + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + filters: + - field_to_match: header + header: cookie + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_second_waf_regional_regex_condition + + - name: check that the pattern is shared + assert: + that: + - > + create_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id == + create_second_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id + - create_second_waf_regional_regex_condition.changed + + + - name: delete first WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: delete_waf_regional_regex_condition + + - name: delete second WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + filters: + - field_to_match: header + header: cookie + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: delete_second_waf_regional_regex_condition + + - name: create WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + filters: + - field_to_match: query_string + regex_pattern: + name: greetings + regex_strings: + - '[hH]ello' + - '^Hi there' + - '.*Good Day to You' + type: regex + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: recreate_waf_regional_regex_condition + + - name: check that a new pattern is created (because the first pattern should have been deleted once unused) + assert: + that: + - > + recreate_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id != + create_waf_regional_regex_condition.condition.regex_match_tuples[0].regex_pattern_set_id + ################################################## # aws_waf_rule tests ################################################## @@ -345,6 +596,124 @@ - remove_in_use_condition.failed - "'Condition {{ resource_prefix }}_size_condition is in use' in remove_in_use_condition.msg" + - name: create WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_regex_condition" + type: regex + negated: no + - name: "{{ resource_prefix }}_geo_condition" + type: geo + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_aws_waf_regional_rule + + - name: check WAF Regional rule + assert: + that: + - create_aws_waf_regional_rule.changed + - create_aws_waf_regional_rule.rule.predicates|length == 3 + + - name: recreate WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_regex_condition" + type: regex + negated: no + - name: "{{ resource_prefix }}_geo_condition" + type: geo + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_aws_waf_regional_rule + + - name: check WAF Regional rule did not change + assert: + that: + - not create_aws_waf_regional_rule.changed + - create_aws_waf_regional_rule.rule.predicates|length == 3 + + - name: add further WAF Regional rules relying on purge_conditions defaulting to false + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_sql_condition" + type: sql + negated: no + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: add_conditions_to_aws_waf_regional_rule + + - name: check WAF Regional rule added rules + assert: + that: + - add_conditions_to_aws_waf_regional_rule.changed + - add_conditions_to_aws_waf_regional_rule.rule.predicates|length == 6 + + - name: remove some rules through purging conditions + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + - name: "{{ resource_prefix }}_byte_condition" + type: byte + negated: no + - name: "{{ resource_prefix }}_size_condition" + type: size + negated: no + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: add_and_remove_waf_regional_rule_conditions + + - name: check WAF Regional rules were updated as expected + assert: + that: + - add_and_remove_waf_regional_rule_conditions.changed + - add_and_remove_waf_regional_rule_conditions.rule.predicates|length == 4 + + - name: attempt to remove an WAF Regional in use condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + type: size + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + register: remove_in_use_condition + + - name: check failure was sensible + assert: + that: + - remove_in_use_condition.failed + - "'Condition {{ resource_prefix }}_size_condition is in use' in remove_in_use_condition.msg" + ################################################## # aws_waf_web_acl tests ################################################## @@ -477,6 +846,156 @@ state: absent <<: *aws_connection_info + - name: create WAF Regional web ACL + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 1 + action: block + default_action: block + purge_rules: yes + state: present + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: create_waf_regional_web_acl + + - name: recreate WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 1 + action: block + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: recreate_waf_regional_web_acl + + - name: check WAF Regional web acl was not changed + assert: + that: + - not recreate_waf_regional_web_acl.changed + - recreate_waf_regional_web_acl.web_acl.rules|length == 1 + + - name: create a second WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule_2" + conditions: + - name: "{{ resource_prefix }}_ip_condition" + type: ip + negated: yes + - name: "{{ resource_prefix }}_sql_condition" + type: sql + negated: no + - name: "{{ resource_prefix }}_xss_condition" + type: xss + negated: no + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + + - name: add a new rule to the WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule_2" + priority: 2 + action: allow + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: waf_regional_web_acl_add_rule + + - name: check that rule was added to the WAF Regional web acl + assert: + that: + - waf_regional_web_acl_add_rule.changed + - waf_regional_web_acl_add_rule.web_acl.rules|length == 2 + + - name: use purge rules to remove the WAF Regional first rule + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule_2" + priority: 2 + action: allow + purge_rules: yes + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: waf_regional_web_acl_add_rule + + - name: check that rule was removed from the WAF Regional web acl + assert: + that: + - waf_regional_web_acl_add_rule.changed + - waf_regional_web_acl_add_rule.web_acl.rules|length == 1 + + - name: swap two WAF Regional rules of same priority + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + rules: + - name: "{{ resource_prefix }}_rule" + priority: 2 + action: allow + purge_rules: yes + default_action: block + state: present + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: waf_regional_web_acl_swap_rule + + - name: attempt to delete the WAF Regional inuse first rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + register: remove_waf_regional_inuse_rule + + - name: check that removing WAF Regional in-use rule fails + assert: + that: + - remove_waf_regional_inuse_rule.failed + + - name: delete the WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + register: delete_waf_regional_web_acl + + - name: check that WAF Regional web acl was deleted + assert: + that: + - delete_waf_regional_web_acl.changed + - not delete_waf_regional_web_acl.web_acl + + - name: delete the no longer in use WAF Regional first rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + + ################################################## + # TEARDOWN + ################################################## + always: - debug: msg: "****** TEARDOWN STARTS HERE ******" @@ -568,3 +1087,113 @@ state: absent <<: *aws_connection_info ignore_errors: yes + + - name: delete the WAF Regional web acl + aws_waf_web_acl: + name: "{{ resource_prefix }}_web_acl" + state: absent + purge_rules: yes + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove second WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule_2" + state: absent + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional rule + aws_waf_rule: + name: "{{ resource_prefix }}_rule" + state: absent + purge_conditions: yes + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional XSS condition + aws_waf_condition: + name: "{{ resource_prefix }}_xss_condition" + type: xss + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional SQL condition + aws_waf_condition: + name: "{{ resource_prefix }}_sql_condition" + type: sql + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional size condition + aws_waf_condition: + name: "{{ resource_prefix }}_size_condition" + type: size + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional geo condition + aws_waf_condition: + name: "{{ resource_prefix }}_geo_condition" + type: geo + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional byte condition + aws_waf_condition: + name: "{{ resource_prefix }}_byte_condition" + type: byte + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional ip address condition + aws_waf_condition: + name: "{{ resource_prefix }}_ip_condition" + type: ip + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove WAF Regional regex part 2 condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition_part_2" + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes + + - name: remove first WAF Regional regex condition + aws_waf_condition: + name: "{{ resource_prefix }}_regex_condition" + type: regex + state: absent + region: "{{ aws_region }}" + waf_regional: true + <<: *aws_connection_info + ignore_errors: yes