From 66292da32952f49f9eba9fb7800284540e6c6efe Mon Sep 17 00:00:00 2001 From: Gerlof Fokkema Date: Tue, 11 Jun 2024 11:34:48 +0200 Subject: [PATCH] Firewalld: Add functionality to set forwarding. Fixes #529. --- changelogs/fragments/548_add_foward.yml | 3 + plugins/modules/firewalld.py | 87 ++++++++++++++++++- .../firewalld/tasks/source_test_cases.yml | 5 +- .../firewalld/tasks/zone_test_cases.yml | 46 ++++++++++ 4 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 changelogs/fragments/548_add_foward.yml diff --git a/changelogs/fragments/548_add_foward.yml b/changelogs/fragments/548_add_foward.yml new file mode 100644 index 0000000000..2d41ccd7b4 --- /dev/null +++ b/changelogs/fragments/548_add_foward.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - firewalld - add functionality to set forwarding (https://github.com/ansible-collections/ansible.posix/pull/548). diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py index 146990f46b..e7b3edfb02 100644 --- a/plugins/modules/firewalld.py +++ b/plugins/modules/firewalld.py @@ -108,6 +108,11 @@ - The amount of time in seconds the rule should be in effect for when non-permanent. type: int default: 0 + forward: + description: + - The forward setting you would like to enable/disable to/from zones within firewalld. + - This option only is supported by firewalld v0.9.0 or later. + type: str masquerade: description: - The masquerade setting you would like to enable/disable to/from zones within firewalld. @@ -138,8 +143,8 @@ - This module needs C(python-firewall) or C(python3-firewall) on managed nodes. It is usually provided as a subset with C(firewalld) from the OS distributor for the OS default Python interpreter. requirements: -- firewalld >= 0.2.11 -- python-firewall >= 0.2.11 +- firewalld >= 0.9.0 +- python-firewall >= 0.9.0 author: - Adam Miller (@maxamillion) ''' @@ -198,6 +203,12 @@ permanent: true state: enabled +- ansible.posix.firewalld: + forward: true + state: enabled + permanent: true + zone: internal + - ansible.posix.firewalld: masquerade: true state: enabled @@ -405,6 +416,49 @@ def set_disabled_permanent(self, protocol, timeout): 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 = "Added forward to zone %s" % self.zone + self.disabled_msg = "Removed forward from 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.queryForward() 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 MasqueradeTransaction(FirewallTransaction): """ MasqueradeTransaction @@ -821,6 +875,7 @@ def main(): state=dict(type='str', required=True, choices=['absent', 'disabled', 'enabled', 'present']), timeout=dict(type='int', default=0), interface=dict(type='str'), + forward=dict(type='str'), masquerade=dict(type='str'), offline=dict(type='bool', default=False), target=dict(type='str', choices=['default', 'ACCEPT', 'DROP', '%%REJECT%%']), @@ -833,7 +888,7 @@ def main(): ), mutually_exclusive=[ ['icmp_block', 'icmp_block_inversion', 'service', 'protocol', 'port', 'port_forward', 'rich_rule', - 'interface', 'masquerade', 'source', 'target'] + 'interface', 'forward', 'masquerade', 'source', 'target'] ], ) @@ -842,6 +897,7 @@ def main(): immediate = module.params['immediate'] timeout = module.params['timeout'] interface = module.params['interface'] + forward = module.params['forward'] masquerade = module.params['masquerade'] offline = module.params['offline'] @@ -905,7 +961,7 @@ def main(): modification = False if any([icmp_block, icmp_block_inversion, service, protocol, port, port_forward, rich_rule, - interface, masquerade, source, target]): + interface, forward, masquerade, source, target]): modification = True if modification and desired_state in ['absent', 'present'] and target is None: module.fail_json( @@ -1072,6 +1128,29 @@ def main(): changed, transaction_msgs = transaction.run() msgs = msgs + transaction_msgs + if forward is not None: + # Type of forward will be changed to boolean in a future release. + forward_status = False + try: + forward_status = boolean(forward, False) + except TypeError: + module.warn('The value of the forward option is "%s". ' + '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.' % forward) + + expected_state = 'enabled' if (desired_state == 'enabled') == forward_status else 'disabled' + transaction = ForwardTransaction( + module, + action_args=(), + zone=zone, + desired_state=expected_state, + permanent=permanent, + immediate=immediate, + ) + + changed, transaction_msgs = transaction.run() + msgs = msgs + transaction_msgs + if masquerade is not None: # Type of masquerade will be changed to boolean in a future release. masquerade_status = True diff --git a/tests/integration/targets/firewalld/tasks/source_test_cases.yml b/tests/integration/targets/firewalld/tasks/source_test_cases.yml index a2552d55f5..8b76521d94 100644 --- a/tests/integration/targets/firewalld/tasks/source_test_cases.yml +++ b/tests/integration/targets/firewalld/tasks/source_test_cases.yml @@ -83,5 +83,6 @@ ansible.builtin.assert: that: - result is not changed - - "result.msg == - 'parameters are mutually exclusive: icmp_block|icmp_block_inversion|service|protocol|port|port_forward|rich_rule|interface|masquerade|source|target'" + - > + result.msg == 'parameters are mutually exclusive: + icmp_block|icmp_block_inversion|service|protocol|port|port_forward|rich_rule|interface|forward|masquerade|source|target' diff --git a/tests/integration/targets/firewalld/tasks/zone_test_cases.yml b/tests/integration/targets/firewalld/tasks/zone_test_cases.yml index 753dd183ea..f8ab078edf 100644 --- a/tests/integration/targets/firewalld/tasks/zone_test_cases.yml +++ b/tests/integration/targets/firewalld/tasks/zone_test_cases.yml @@ -23,6 +23,52 @@ that: - result is not changed +- name: Enable zone forwarding + ansible.posix.firewalld: + zone: custom + forward: true + permanent: true + state: enabled + register: result + +- name: Assert zone forwarding is enabled + ansible.builtin.debug: + var: result is changed + +- name: Enable zone forwarding (verify not changed) + ansible.posix.firewalld: + zone: custom + forward: true + permanent: true + state: enabled + register: result + +- name: Assert zone forwarding is enabled (verify not changed) + ansible.builtin.debug: + var: result is not changed + +- name: Disable zone forwarding + ansible.posix.firewalld: + zone: custom + forward: false + permanent: true + state: enabled + +- name: Assert zone forwarding is disabled + ansible.builtin.debug: + var: result is changed + +- name: Disable zone forwarding (verify not changed) + ansible.posix.firewalld: + zone: custom + forward: false + permanent: true + state: enabled + +- name: Assert zone forwarding is disabled (verify not changed) + ansible.builtin.debug: + var: result is not changed + - name: Firewalld remove zone custom ansible.posix.firewalld: zone: custom