Repository: ansible-collections/community.aws

Issue:
Support "Drop invalid header fields" in elb_application_lb
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
Add support for "drop invalid header fields" when manipulating an ALB.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
`elb_application_lb`

##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->
If you want to enable dropping invalid headers on your ALB you should:

- create the ALB
- by default, dropping invalid headers is disabled
- edit the ALB settings on AWS UI and enable "Drop invalid header fields"

This feature would solve the need to manually edit the ALB after its creation.

<!--- Paste example playbooks or commands between quotes below -->
```yaml

```

<!--- HINT: You can also paste gist.github.com links for larger files -->



Discussion:
Hi @boutetnico, thank you for raising this. Would you be willing to implement this feature? If you need any help, please feel free to reach out to us on IRC #ansible-aws. Thank you.
cc @jillr @s-hertel @tremble @wimnat
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @markuman
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I can work on adding this
Also going to add support for the other routing options:

- http_desync_mitigation_mode
- http_drop_invalid_header_fields (this issue)
- http_x_amzn_tls_version_and_cipher_suite
- http_xff_client_port_enabled

Resolving patch:
diff --git a/plugins/modules/elb_application_lb.py b/plugins/modules/elb_application_lb.py
--- a/plugins/modules/elb_application_lb.py
+++ b/plugins/modules/elb_application_lb.py
@@ -49,13 +49,38 @@
   deletion_protection:
     description:
       - Indicates whether deletion protection for the ALB is enabled.
-      - Defaults to C(false).
+      - Defaults to C(False).
     type: bool
   http2:
     description:
       - Indicates whether to enable HTTP2 routing.
-      - Defaults to C(false).
+      - Defaults to C(True).
     type: bool
+  http_desync_mitigation_mode:
+    description:
+      - Determines how the load balancer handles requests that might pose a security risk to an application.
+      - Defaults to C('defensive')
+    type: str
+    choices: ['monitor', 'defensive', 'strictest']
+    version_added: 3.2.0
+  http_drop_invalid_header_fields:
+    description:
+      - Indicates whether HTTP headers with invalid header fields are removed by the load balancer C(True) or routed to targets C(False).
+      - Defaults to C(False).
+    type: bool
+    version_added: 3.2.0
+  http_x_amzn_tls_version_and_cipher_suite:
+    description:
+      - Indicates whether the two headers are added to the client request before sending it to the target.
+      - Defaults to C(False).
+    type: bool
+    version_added: 3.2.0
+  http_xff_client_port:
+    description:
+      - Indicates whether the X-Forwarded-For header should preserve the source port that the client used to connect to the load balancer.
+      - Defaults to C(False).
+    type: bool
+    version_added: 3.2.0
   idle_timeout:
     description:
       - The number of seconds to wait before an idle connection is closed.
@@ -183,6 +208,12 @@
       - Sets the type of IP addresses used by the subnets of the specified Application Load Balancer.
     choices: [ 'ipv4', 'dualstack' ]
     type: str
+  waf_fail_open:
+    description:
+      - Indicates whether to allow a AWS WAF-enabled load balancer to route requests to targets if it is unable to forward the request to AWS WAF.
+      - Defaults to C(False).
+    type: bool
+    version_added: 3.2.0
 extends_documentation_fragment:
 - amazon.aws.aws
 - amazon.aws.ec2
@@ -525,6 +556,13 @@ def create_or_update_alb(alb_obj):
                 alb_obj.module.exit_json(changed=True, msg='Would have updated ALB if not in check mode.')
             alb_obj.modify_security_groups()
 
+        # ALB attributes
+        if not alb_obj.compare_elb_attributes():
+            if alb_obj.module.check_mode:
+                alb_obj.module.exit_json(changed=True, msg='Would have updated ALB if not in check mode.')
+            alb_obj.update_elb_attributes()
+            alb_obj.modify_elb_attributes()
+
         # Tags - only need to play with tags if tags parameter has been set to something
         if alb_obj.tags is not None:
 
@@ -549,10 +587,6 @@ def create_or_update_alb(alb_obj):
             alb_obj.module.exit_json(changed=True, msg='Would have created ALB if not in check mode.')
         alb_obj.create_elb()
 
-    # ALB attributes
-    alb_obj.update_elb_attributes()
-    alb_obj.modify_elb_attributes()
-
     # Listeners
     listeners_obj = ELBListeners(alb_obj.connection, alb_obj.module, alb_obj.elb['LoadBalancerArn'])
     listeners_to_add, listeners_to_modify, listeners_to_delete = listeners_obj.compare_listeners()
@@ -683,6 +717,10 @@ def main():
         access_logs_s3_prefix=dict(type='str'),
         deletion_protection=dict(type='bool'),
         http2=dict(type='bool'),
+        http_desync_mitigation_mode=dict(type='str', choices=['monitor', 'defensive', 'strictest']),
+        http_drop_invalid_header_fields=dict(type='bool'),
+        http_x_amzn_tls_version_and_cipher_suite=dict(type='bool'),
+        http_xff_client_port=dict(type='bool'),
         idle_timeout=dict(type='int'),
         listeners=dict(type='list',
                        elements='dict',
@@ -703,6 +741,7 @@ def main():
         scheme=dict(default='internet-facing', choices=['internet-facing', 'internal']),
         state=dict(choices=['present', 'absent'], default='present'),
         tags=dict(type='dict'),
+        waf_fail_open=dict(type='bool'),
         wait_timeout=dict(type='int'),
         wait=dict(default=False, type='bool'),
         purge_rules=dict(default=True, type='bool'),


Test patch:
diff --git a/tests/integration/targets/elb_application_lb/tasks/main.yml b/tests/integration/targets/elb_application_lb/tasks/main.yml
--- a/tests/integration/targets/elb_application_lb/tasks/main.yml
+++ b/tests/integration/targets/elb_application_lb/tasks/main.yml
@@ -241,6 +241,12 @@
           - alb is changed
           - alb.ip_address_type == 'dualstack'
           - alb.listeners[0].rules | length == 1
+          - alb.routing_http2_enabled | bool
+          - alb.routing_http_desync_mitigation_mode == 'defensive'
+          - not alb.routing_http_drop_invalid_header_fields_enabled | bool
+          - not alb.routing_http_x_amzn_tls_version_and_cipher_suite_enabled | bool
+          - not alb.routing_http_xff_client_port_enabled | bool
+          - not alb.waf_fail_open_enabled | bool
 
     - name: Create an ALB with ip address type (idempotence) - check_mode
       elb_application_lb:
@@ -282,6 +288,132 @@
         that:
           - alb is not changed
           - alb.ip_address_type == 'dualstack'
+          - alb.routing_http2_enabled | bool
+          - alb.routing_http_desync_mitigation_mode == 'defensive'
+          - not alb.routing_http_drop_invalid_header_fields_enabled | bool
+          - not alb.routing_http_x_amzn_tls_version_and_cipher_suite_enabled | bool
+          - not alb.routing_http_xff_client_port_enabled | bool
+          - not alb.waf_fail_open_enabled | bool
+
+    # ------------------------------------------------------------------------------------------
+
+    - name: Update an ALB with different attributes - check_mode
+      elb_application_lb:
+        name: "{{ alb_name }}"
+        subnets: "{{ public_subnets }}"
+        security_groups: "{{ sec_group.group_id }}"
+        state: present
+        listeners:
+          - Protocol: HTTP
+            Port: 80
+            DefaultActions:
+              - Type: forward
+                TargetGroupName: "{{ tg_name }}"
+        ip_address_type: 'dualstack'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
+      register: alb
+      check_mode: yes
+
+    - assert:
+        that:
+          - alb is changed
+          - alb.msg is match('Would have updated ALB if not in check mode.')
+
+    - name: Update an ALB with different attributes
+      elb_application_lb:
+        name: "{{ alb_name }}"
+        subnets: "{{ public_subnets }}"
+        security_groups: "{{ sec_group.group_id }}"
+        state: present
+        listeners:
+          - Protocol: HTTP
+            Port: 80
+            DefaultActions:
+              - Type: forward
+                TargetGroupName: "{{ tg_name }}"
+        ip_address_type: 'dualstack'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
+      register: alb
+
+    - assert:
+        that:
+          - alb is changed
+          - alb.ip_address_type == 'dualstack'
+          - not alb.routing_http2_enabled | bool
+          - alb.routing_http_desync_mitigation_mode == 'monitor'
+          - alb.routing_http_drop_invalid_header_fields_enabled | bool
+          - alb.routing_http_x_amzn_tls_version_and_cipher_suite_enabled | bool
+          - alb.routing_http_xff_client_port_enabled | bool
+          - alb.waf_fail_open_enabled | bool
+
+    - name: Update an ALB with different attributes (idempotence) - check_mode
+      elb_application_lb:
+        name: "{{ alb_name }}"
+        subnets: "{{ public_subnets }}"
+        security_groups: "{{ sec_group.group_id }}"
+        state: present
+        listeners:
+          - Protocol: HTTP
+            Port: 80
+            DefaultActions:
+              - Type: forward
+                TargetGroupName: "{{ tg_name }}"
+        ip_address_type: 'dualstack'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
+      register: alb
+      check_mode: yes
+
+    - assert:
+        that:
+          - alb is not changed
+          - alb.msg is match('IN CHECK MODE - no changes to make to ALB specified.')
+
+    - name: Update an ALB with different attributes (idempotence)
+      elb_application_lb:
+        name: "{{ alb_name }}"
+        subnets: "{{ public_subnets }}"
+        security_groups: "{{ sec_group.group_id }}"
+        state: present
+        listeners:
+          - Protocol: HTTP
+            Port: 80
+            DefaultActions:
+              - Type: forward
+                TargetGroupName: "{{ tg_name }}"
+        ip_address_type: 'dualstack'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
+      register: alb
+
+    - assert:
+        that:
+          - alb is not changed
+          - alb.ip_address_type == 'dualstack'
+          - not alb.routing_http2_enabled | bool
+          - alb.routing_http_desync_mitigation_mode == 'monitor'
+          - alb.routing_http_drop_invalid_header_fields_enabled | bool
+          - alb.routing_http_x_amzn_tls_version_and_cipher_suite_enabled | bool
+          - alb.routing_http_xff_client_port_enabled | bool
+          - alb.waf_fail_open_enabled | bool
 
     # ------------------------------------------------------------------------------------------
 
@@ -298,6 +430,12 @@
               - Type: forward
                 TargetGroupName: "{{ tg_name }}"
         ip_address_type: 'ipv4'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
       register: alb
       check_mode: yes
 
@@ -319,12 +457,24 @@
               - Type: forward
                 TargetGroupName: "{{ tg_name }}"
         ip_address_type: 'ipv4'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
       register: alb
 
     - assert:
         that:
           - alb is changed
           - alb.ip_address_type == 'ipv4'
+          - not alb.routing_http2_enabled | bool
+          - alb.routing_http_desync_mitigation_mode == 'monitor'
+          - alb.routing_http_drop_invalid_header_fields_enabled | bool
+          - alb.routing_http_x_amzn_tls_version_and_cipher_suite_enabled | bool
+          - alb.routing_http_xff_client_port_enabled | bool
+          - alb.waf_fail_open_enabled | bool
 
     - name: Update an ALB with different ip address type (idempotence) - check_mode
       elb_application_lb:
@@ -339,6 +489,12 @@
               - Type: forward
                 TargetGroupName: "{{ tg_name }}"
         ip_address_type: 'ipv4'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
       register: alb
       check_mode: yes
 
@@ -360,12 +516,24 @@
               - Type: forward
                 TargetGroupName: "{{ tg_name }}"
         ip_address_type: 'ipv4'
+        http2: no
+        http_desync_mitigation_mode: monitor
+        http_drop_invalid_header_fields: yes
+        http_x_amzn_tls_version_and_cipher_suite: yes
+        http_xff_client_port: yes
+        waf_fail_open: yes
       register: alb
 
     - assert:
         that:
           - alb is not changed
           - alb.ip_address_type == 'ipv4'
+          - not alb.routing_http2_enabled | bool
+          - alb.routing_http_desync_mitigation_mode == 'monitor'
+          - alb.routing_http_drop_invalid_header_fields_enabled | bool
+          - alb.routing_http_x_amzn_tls_version_and_cipher_suite_enabled | bool
+          - alb.routing_http_xff_client_port_enabled | bool
+          - alb.waf_fail_open_enabled | bool
 
     # ------------------------------------------------------------------------------------------