From 5a38230dbf9308a140053311aa0927cda3cf4983 Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sat, 22 Jan 2022 18:10:15 +0100 Subject: [PATCH 1/8] Add support for firewalld intra zone forwarding --- plugins/modules/firewalld.py | 73 +++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py index 39a3b18..33277e4 100644 --- a/plugins/modules/firewalld.py +++ b/plugins/modules/firewalld.py @@ -106,6 +106,10 @@ options: description: - The masquerade setting you would like to enable/disable to/from zones within firewalld. type: str + forward: + description: + - Whether intra zone forwarding should be enabled/disabled for a zone in firewalld. + type: bool offline: description: - Whether to run this module even when firewalld is offline. @@ -183,6 +187,12 @@ EXAMPLES = r''' permanent: yes zone: dmz +- ansible.posix.firewalld: + forward: yes + state: enabled + permanent: yes + zone: dmz + - ansible.posix.firewalld: zone: custom state: present @@ -386,6 +396,49 @@ class MasqueradeTransaction(FirewallTransaction): self.update_fw_settings(fw_zone, fw_settings) +class ForwardTransaction(FirewallTransaction): + """ + ForwardTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(ForwardTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + self.enabled_msg = "Enabled intra zone forwarding on zone %s" % self.zone + self.disabled_msg = "Disabled intra zone forwarding on zone %s" % self.zone + + def get_enabled_immediate(self): + if self.fw.queryForward(self.zone) is True: + return True + else: + return False + + def get_enabled_permanent(self): + fw_zone, fw_settings = self.get_fw_zone_settings() + if fw_settings.getForward() is True: + return True + else: + return False + + def set_enabled_immediate(self): + self.fw.addForward(self.zone) + + def set_enabled_permanent(self): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.setForward(True) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self): + self.fw.removeForward(self.zone) + + def set_disabled_permanent(self): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.setForward(False) + self.update_fw_settings(fw_zone, fw_settings) + + class PortTransaction(FirewallTransaction): """ PortTransaction @@ -751,6 +804,7 @@ def main(): timeout=dict(type='int', default=0), interface=dict(type='str'), masquerade=dict(type='str'), + forward=dict(type='bool'), offline=dict(type='bool'), target=dict(type='str', choices=['default', 'ACCEPT', 'DROP', '%%REJECT%%']), ), @@ -762,7 +816,7 @@ def main(): ), mutually_exclusive=[ ['icmp_block', 'icmp_block_inversion', 'service', 'port', 'port_forward', 'rich_rule', - 'interface', 'masquerade', 'source', 'target'] + 'interface', 'masquerade', 'forward', 'source', 'target'] ], ) @@ -772,6 +826,7 @@ def main(): timeout = module.params['timeout'] interface = module.params['interface'] masquerade = module.params['masquerade'] + forward = module.params['forward'] # Sanity checks FirewallTransaction.sanity_check(module) @@ -822,7 +877,7 @@ def main(): modification = False if any([icmp_block, icmp_block_inversion, service, port, port_forward, rich_rule, - interface, masquerade, source, target]): + interface, masquerade, forward, source, target]): modification = True if modification and desired_state in ['absent', 'present'] and target is None: module.fail_json( @@ -994,6 +1049,20 @@ def main(): 'The type of the option will be changed from string to boolean in a future release. ' 'To avoid unexpected behavior, please change the value to boolean.' % masquerade) + if forward is not None: + + transaction = ForwardTransaction( + module, + action_args=(), + zone=zone, + desired_state=desired_state, + permanent=permanent, + immediate=immediate, + ) + + changed, transaction_msgs = transaction.run() + msgs = msgs + transaction_msgs + if target is not None: transaction = ZoneTargetTransaction( From 4fefc2482a7ac8815969decb0c7ec78b95d15e70 Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sat, 22 Jan 2022 18:14:36 +0100 Subject: [PATCH 2/8] Add support for firewalld intra zone forwarding - documentation --- docs/ansible.posix.firewalld_module.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/ansible.posix.firewalld_module.rst b/docs/ansible.posix.firewalld_module.rst index a041534..0b5a9aa 100644 --- a/docs/ansible.posix.firewalld_module.rst +++ b/docs/ansible.posix.firewalld_module.rst @@ -117,6 +117,21 @@ Parameters
The masquerade setting you would like to enable/disable to/from zones within firewalld.
+ + +
+ forward + +
+ string +
+ + + + +
Whether intra zone forwarding should be enabled/disabled for a zone in firewalld.
+ +
@@ -454,6 +469,12 @@ Examples permanent: yes zone: dmz + - ansible.posix.firewalld: + forward: yes + state: enabled + permanent: yes + zone: dmz + - ansible.posix.firewalld: zone: custom state: present From 11439a0e6b2b3f7610a8aeeef121995f53ce52b1 Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sat, 22 Jan 2022 18:18:52 +0100 Subject: [PATCH 3/8] Add support for firewalld intra zone forwarding - tests --- .../firewalld/tasks/forward_test_cases.yml | 51 +++++++++++++++++++ .../targets/firewalld/tasks/run_all_tests.yml | 3 ++ 2 files changed, 54 insertions(+) create mode 100644 tests/integration/targets/firewalld/tasks/forward_test_cases.yml diff --git a/tests/integration/targets/firewalld/tasks/forward_test_cases.yml b/tests/integration/targets/firewalld/tasks/forward_test_cases.yml new file mode 100644 index 0000000..00b8939 --- /dev/null +++ b/tests/integration/targets/firewalld/tasks/forward_test_cases.yml @@ -0,0 +1,51 @@ +# Test playbook for the firewalld module - forward operations +# (c) 2017, Adam Miller +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: firewalld forward test permanent enabled + firewalld: + forward: yes + permanent: true + state: enabled + register: result + +- name: assert firewalld forward test permanent enabled worked + assert: + that: + - result is changed + +- name: firewalld forward test permanent enabled rerun (verify not changed) + firewalld: + forward: yes + permanent: true + state: enabled + register: result + +- name: assert firewalld forward test permanent enabled rerun worked (verify not changed) + assert: + that: + - result is not changed + +- name: firewalld forward test permanent disabled + firewalld: + forward: no + permanent: true + state: disabled + register: result + +- name: assert firewalld forward test permanent disabled worked + assert: + that: + - result is changed + +- name: firewalld forward test permanent disabled rerun (verify not changed) + firewalld: + forward: no + permanent: true + state: disabled + register: result + +- name: assert firewalld forward test permanent disabled rerun worked (verify not changed) + assert: + that: + - result is not changed diff --git a/tests/integration/targets/firewalld/tasks/run_all_tests.yml b/tests/integration/targets/firewalld/tasks/run_all_tests.yml index 4270e89..2a86bb7 100644 --- a/tests/integration/targets/firewalld/tasks/run_all_tests.yml +++ b/tests/integration/targets/firewalld/tasks/run_all_tests.yml @@ -21,3 +21,6 @@ # firewalld port forwarding operation test cases - include_tasks: port_forward_test_cases.yml + +# firewalld zone forwarding operation test cases +- include_tasks: forward_test_cases.yml From 7e40959a471dda83b24da582cab5aae1357e70f9 Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sat, 29 Jan 2022 16:33:28 +0100 Subject: [PATCH 4/8] Ensure firewalld version is at least 0.9.0 --- plugins/modules/firewalld.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py index 33277e4..ee2799e 100644 --- a/plugins/modules/firewalld.py +++ b/plugins/modules/firewalld.py @@ -109,6 +109,7 @@ options: forward: description: - Whether intra zone forwarding should be enabled/disabled for a zone in firewalld. + This parameter supports on python-firewall 0.9.0 or later. type: bool offline: description: @@ -191,7 +192,7 @@ EXAMPLES = r''' forward: yes state: enabled permanent: yes - zone: dmz + zone: custom - ansible.posix.firewalld: zone: custom @@ -228,10 +229,12 @@ EXAMPLES = r''' from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.parsing.convert_bool import boolean from ansible_collections.ansible.posix.plugins.module_utils.firewalld import FirewallTransaction, fw_offline +from ansible_collections.ansible.posix.plugins.module_utils.version import StrictVersion try: from firewall.client import Rich_Rule from firewall.client import FirewallClientZoneSettings + from firewall.config import VERSION as FIREWALLD_VERSION except ImportError: # The import errors are handled via FirewallTransaction, don't need to # duplicate that here @@ -1051,6 +1054,9 @@ def main(): if forward is not None: + if StrictVersion(FIREWALLD_VERSION) < StrictVersion('0.9.0'): + module.fail_json(msg=f'Intra zone forwarding requires firewalld>=0.9.0. Current version is {FIREWALLD_VERSION}.') + transaction = ForwardTransaction( module, action_args=(), From e8198bce1c61dc3acc9477260a9671131323d2ce Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sat, 29 Jan 2022 19:12:34 +0100 Subject: [PATCH 5/8] Fixed failing parameter exclusivity test --- tests/integration/targets/firewalld/tasks/source_test_cases.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/firewalld/tasks/source_test_cases.yml b/tests/integration/targets/firewalld/tasks/source_test_cases.yml index 172a47e..9766075 100644 --- a/tests/integration/targets/firewalld/tasks/source_test_cases.yml +++ b/tests/integration/targets/firewalld/tasks/source_test_cases.yml @@ -82,4 +82,4 @@ assert: that: - result is not changed - - "result.msg == 'parameters are mutually exclusive: icmp_block|icmp_block_inversion|service|port|port_forward|rich_rule|interface|masquerade|source|target'" + - "result.msg == 'parameters are mutually exclusive: icmp_block|icmp_block_inversion|service|port|port_forward|rich_rule|interface|masquerade|forward|source|target'" From 6635b8391137ab84e1f9199f880b8282fab74625 Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sun, 30 Jan 2022 12:22:57 +0100 Subject: [PATCH 6/8] Integration test: ensure forwarding start disabled Integration test: verify error message if firewalld<0.9.0 Added changelog fragment --- .../320_firewalld_intra_zone_forwarding.yml | 4 + .../firewalld/tasks/forward_test_cases.yml | 115 ++++++++++++------ 2 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 changelogs/fragments/320_firewalld_intra_zone_forwarding.yml diff --git a/changelogs/fragments/320_firewalld_intra_zone_forwarding.yml b/changelogs/fragments/320_firewalld_intra_zone_forwarding.yml new file mode 100644 index 0000000..b707262 --- /dev/null +++ b/changelogs/fragments/320_firewalld_intra_zone_forwarding.yml @@ -0,0 +1,4 @@ +--- +minor_changes: +- firewalld - Added parameter ``forward`` to support enabling/disabling intra-zone + forwarding. diff --git a/tests/integration/targets/firewalld/tasks/forward_test_cases.yml b/tests/integration/targets/firewalld/tasks/forward_test_cases.yml index 00b8939..c8b8d62 100644 --- a/tests/integration/targets/firewalld/tasks/forward_test_cases.yml +++ b/tests/integration/targets/firewalld/tasks/forward_test_cases.yml @@ -2,50 +2,85 @@ # (c) 2017, Adam Miller # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -- name: firewalld forward test permanent enabled - firewalld: - forward: yes - permanent: true - state: enabled - register: result +- name: query firewalld version + package_facts: -- name: assert firewalld forward test permanent enabled worked - assert: - that: - - result is changed +- name: run tests if intra zone forwarding is supported + block: -- name: firewalld forward test permanent enabled rerun (verify not changed) - firewalld: - forward: yes - permanent: true - state: enabled - register: result + # Starting with firewalld 1.0.0 intra-zone forwarding is enabled by default. + # Ensure it is disabled before starting our tests. + - name: ensure forwarding starts disabled + firewalld: + forward: yes + permanent: true + state: disabled -- name: assert firewalld forward test permanent enabled rerun worked (verify not changed) - assert: - that: - - result is not changed + - name: firewalld forward test permanent enabled + firewalld: + forward: yes + permanent: true + state: enabled + register: result -- name: firewalld forward test permanent disabled - firewalld: - forward: no - permanent: true - state: disabled - register: result + - name: assert firewalld forward test permanent enabled worked + assert: + that: + - result is changed -- name: assert firewalld forward test permanent disabled worked - assert: - that: - - result is changed + - name: firewalld forward test permanent enabled rerun (verify not changed) + firewalld: + forward: yes + permanent: true + state: enabled + register: result -- name: firewalld forward test permanent disabled rerun (verify not changed) - firewalld: - forward: no - permanent: true - state: disabled - register: result + - name: assert firewalld forward test permanent enabled rerun worked (verify not changed) + assert: + that: + - result is not changed -- name: assert firewalld forward test permanent disabled rerun worked (verify not changed) - assert: - that: - - result is not changed + - name: firewalld forward test permanent disabled + firewalld: + forward: no + permanent: true + state: disabled + register: result + + - name: assert firewalld forward test permanent disabled worked + assert: + that: + - result is changed + + - name: firewalld forward test permanent disabled rerun (verify not changed) + firewalld: + forward: no + permanent: true + state: disabled + register: result + + - name: assert firewalld forward test permanent disabled rerun worked (verify not changed) + assert: + that: + - result is not changed + + when: ansible_facts.packages.firewalld[0].version is version('0.9.0', '>=') + +- name: run tests if intra zone forwarding is not supported + block: + + - name: try to enable intra zone forwarding + firewalld: + forward: yes + permanent: yes + state: enabled + ignore_errors: yes + register: result + + - name: assert unsupported firewalld version + assert: + that: + - result is failed + - "'Intra zone forwarding requires firewalld>=0.9.0. Current version is' in result.msg" + + when: ansible_facts.packages.firewalld[0].version is version('0.9.0', '<') \ No newline at end of file From d4c3aef803650d99f70138da23491c348f877e17 Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sun, 30 Jan 2022 12:40:10 +0100 Subject: [PATCH 7/8] Replaced f-string with .format for Python2 compatibility --- plugins/modules/firewalld.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py index ee2799e..c4d000c 100644 --- a/plugins/modules/firewalld.py +++ b/plugins/modules/firewalld.py @@ -1055,7 +1055,7 @@ def main(): if forward is not None: if StrictVersion(FIREWALLD_VERSION) < StrictVersion('0.9.0'): - module.fail_json(msg=f'Intra zone forwarding requires firewalld>=0.9.0. Current version is {FIREWALLD_VERSION}.') + module.fail_json(msg='Intra zone forwarding requires firewalld>=0.9.0. Current version is {0}.'.format(FIREWALLD_VERSION)) transaction = ForwardTransaction( module, From b3b0db918927c99aa73447a076a7464383e09c8d Mon Sep 17 00:00:00 2001 From: Evert Hessel Date: Sun, 6 Feb 2022 22:10:52 +0100 Subject: [PATCH 8/8] Use get_zone_config_dict when firewalld >= 0.9.0 so forward setting is passed correctly --- plugins/module_utils/firewalld.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/module_utils/firewalld.py b/plugins/module_utils/firewalld.py index c79a126..394f10e 100644 --- a/plugins/module_utils/firewalld.py +++ b/plugins/module_utils/firewalld.py @@ -126,9 +126,16 @@ class FirewallTransaction(object): def get_fw_zone_settings(self): if self.fw_offline: fw_zone = self.fw.config.get_zone(self.zone) - fw_settings = FirewallClientZoneSettings( - list(self.fw.config.get_zone_config(fw_zone)) - ) + + # If firewalld version is 0.9.0 or higher retrieve the configuration + # using the get_zone_config_dict call, otherwise the returned value + # for the 'forward' field is always zero. + if LooseVersion(FW_VERSION) >= LooseVersion("0.9.0"): + fw_settings = FirewallClientZoneSettings(self.fw.config.get_zone_config_dict(fw_zone)) + else: + fw_settings = FirewallClientZoneSettings( + list(self.fw.config.get_zone_config(fw_zone)) + ) else: fw_zone = self.fw.config().getZoneByName(self.zone) fw_settings = fw_zone.getSettings()