From 1e7d82af6d1f929b736bde949b163609c998e184 Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Sat, 16 Jan 2021 17:01:10 -0600 Subject: [PATCH] firewalld: Add support for firewalld port forwarding Fixes: ansible-collections/ansible.posix#100 --- docs/ansible.posix.firewalld_module.rst | 24 +++- plugins/modules/firewalld.py | 116 ++++++++++++++++++ .../tasks/port_forward_test_cases.yml | 77 ++++++++++++ 3 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 tests/integration/targets/firewalld/tasks/port_forward_test_cases.yml diff --git a/docs/ansible.posix.firewalld_module.rst b/docs/ansible.posix.firewalld_module.rst index 49a4b69333..4a750ad447 100644 --- a/docs/ansible.posix.firewalld_module.rst +++ b/docs/ansible.posix.firewalld_module.rst @@ -173,6 +173,21 @@ Parameters
Must be in the form PORT/PROTOCOL or PORT-PORT/PROTOCOL for port ranges.
+ + +
+ port_forward + +
+ string +
+ + + + +
Port and protocol to forward using firewalld.
+ +
@@ -360,9 +375,13 @@ Examples permanent: yes icmp_block: echo-request - - name: Redirect port 443 to 8443 with Rich Rule + - name: Redirect port 443 to 8443 + become: yes ansible.posix.firewalld: - rich_rule: rule family=ipv4 forward-port port=443 protocol=tcp to-port=8443 + port_forward: + - port: 443 + proto: tcp + toport: 8443 zone: public permanent: yes immediate: yes @@ -370,7 +389,6 @@ Examples - Status ------ diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py index 241fe6204b..c87dd3690b 100644 --- a/plugins/modules/firewalld.py +++ b/plugins/modules/firewalld.py @@ -24,6 +24,32 @@ - Name of a port or port range to add/remove to/from firewalld. - Must be in the form PORT/PROTOCOL or PORT-PORT/PROTOCOL for port ranges. type: str + port_forward: + description: + - Port and protocol to forward using firewalld. + type: list + elements: dict + suboptions: + port: + type: str + required: true + description: + - Source port to forward from + proto: + type: str + required: true + description: + - protocol to forward + choices: [udp, tcp] + toport: + type: str + required: true + description: + - destination port + toaddr: + type: str + description: + - Optional address to forward to rich_rule: description: - Rich rule to add/remove to/from firewalld. @@ -678,6 +704,55 @@ def set_disabled_permanent(self): zone_obj.remove() +class ForwardPortTransaction(FirewallTransaction): + """ + ForwardPortTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(ForwardPortTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + def get_enabled_immediate(self, port, proto, toport, toaddr, timeout): + forward_port = [port, proto, toport, toaddr] + if self.fw_offline: + fw_zone, fw_settings = self.get_fw_zone_settings() + forward_list = fw_settings.getForwardPorts() + else: + forward_list = self.fw.getForwardPorts(self.zone) + + if forward_port in forward_list: + return True + else: + return False + + def get_enabled_permanent(self, port, proto, toport, toaddr, timeout): + forward_port = (port, proto, toport, toaddr) + fw_zone, fw_settings = self.get_fw_zone_settings() + + if forward_port in fw_settings.getForwardPorts(): + return True + else: + return False + + def set_enabled_immediate(self, port, proto, toport, toaddr, timeout): + self.fw.addForwardPort(self.zone, port, proto, toport, toaddr, timeout) + + def set_enabled_permanent(self, port, proto, toport, toaddr, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.addForwardPort(port, proto, toport, toaddr) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self, port, proto, toport, toaddr, timeout): + self.fw.removeForwardPort(self.zone, port, proto, toport, toaddr) + + def set_disabled_permanent(self, port, proto, toport, toaddr, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.removeForwardPort(port, proto, toport, toaddr) + self.update_fw_settings(fw_zone, fw_settings) + + def main(): module = AnsibleModule( @@ -686,6 +761,7 @@ def main(): icmp_block_inversion=dict(type='str'), service=dict(type='str'), port=dict(type='str'), + port_forward=dict(type='list', elements='dict'), rich_rule=dict(type='str'), zone=dict(type='str'), immediate=dict(type='bool', default=False), @@ -745,6 +821,21 @@ def main(): else: port = None + port_forward_toaddr = '' + port_forward = None + if module.params['port_forward'] is not None: + if len(module.params['port_forward']) > 1: + module.fail_json(msg='Only one port forward supported at a time') + port_forward = module.params['port_forward'][0] + if 'port' not in port_forward: + module.fail_json(msg='port must be specified for port forward') + if 'proto' not in port_forward: + module.fail_json(msg='proto udp/tcp must be specified for port forward') + if 'toport' not in port_forward: + module.fail_json(msg='toport must be specified for port forward') + if 'toaddr' in port_forward: + port_forward_toaddr = port_forward['toaddr'] + modification_count = 0 if icmp_block is not None: modification_count += 1 @@ -754,6 +845,8 @@ def main(): modification_count += 1 if port is not None: modification_count += 1 + if port_forward is not None: + modification_count += 1 if rich_rule is not None: modification_count += 1 if interface is not None: @@ -856,6 +949,29 @@ def main(): ) ) + if port_forward is not None: + transaction = ForwardPortTransaction( + module, + action_args=(str(port_forward['port']), port_forward['proto'], + str(port_forward['toport']), port_forward_toaddr, timeout), + zone=zone, + desired_state=desired_state, + permanent=permanent, + immediate=immediate + ) + + changed, transaction_msgs = transaction.run() + msgs = msgs + transaction_msgs + if changed is True: + msgs.append( + "Changed port_forward %s to %s" % ( + "port=%s:proto=%s:toport=%s:toaddr=%s" % ( + port_forward['port'], port_forward['proto'], + port_forward['toport'], port_forward_toaddr + ), desired_state + ) + ) + if rich_rule is not None: transaction = RichRuleTransaction( diff --git a/tests/integration/targets/firewalld/tasks/port_forward_test_cases.yml b/tests/integration/targets/firewalld/tasks/port_forward_test_cases.yml new file mode 100644 index 0000000000..c2a982d91c --- /dev/null +++ b/tests/integration/targets/firewalld/tasks/port_forward_test_cases.yml @@ -0,0 +1,77 @@ +# Test playbook for the firewalld module - port operations +# (c) 2017, Adam Miller + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +- name: firewalld port forward test permanent enabled + firewalld: + port_forward: + - port: 8080 + proto: tcp + toport: 8081 + permanent: true + state: enabled + register: result + +- name: assert firewalld port test permanent enabled worked + assert: + that: + - result is changed + +- name: firewalld port test permanent enabled rerun (verify not changed) + firewalld: + port_forward: + - port: 8080 + proto: tcp + toport: 8081 + permanent: true + state: enabled + register: result + +- name: assert firewalld port test permanent enabled rerun worked (verify not changed) + assert: + that: + - result is not changed + +- name: firewalld port test permanent disabled + firewalld: + port_forward: + - port: 8080 + proto: tcp + toport: 8081 + permanent: true + state: disabled + register: result + +- name: assert firewalld port test permanent disabled worked + assert: + that: + - result is changed + +- name: firewalld port test permanent disabled rerun (verify not changed) + firewalld: + port_forward: + - port: 8080 + proto: tcp + toport: 8081 + permanent: true + state: disabled + register: result + +- name: assert firewalld port test permanent disabled rerun worked (verify not changed) + assert: + that: + - result is not changed