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/docs/ansible.posix.firewalld_module.rst b/docs/ansible.posix.firewalld_module.rst
index ea58ff2..aa457bd 100644
--- a/docs/ansible.posix.firewalld_module.rst
+++ b/docs/ansible.posix.firewalld_module.rst
@@ -118,6 +118,21 @@ Parameters
The masquerade setting you would like to enable/disable to/from zones within firewalld.
+
|
@@ -456,6 +471,12 @@ Examples
permanent: yes
zone: dmz
+ - ansible.posix.firewalld:
+ forward: yes
+ state: enabled
+ permanent: yes
+ zone: dmz
+
- ansible.posix.firewalld:
zone: custom
state: present
diff --git a/plugins/module_utils/firewalld.py b/plugins/module_utils/firewalld.py
index 6a76c32..3c7c4d6 100644
--- a/plugins/module_utils/firewalld.py
+++ b/plugins/module_utils/firewalld.py
@@ -127,9 +127,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()
diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py
index 52a2a5a..fe8d32f 100644
--- a/plugins/modules/firewalld.py
+++ b/plugins/modules/firewalld.py
@@ -106,6 +106,11 @@ 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.
+ This parameter supports on python-firewall 0.9.0 or later.
+ type: bool
offline:
description:
- Whether to run this module even when firewalld is offline.
@@ -183,6 +188,12 @@ EXAMPLES = r'''
permanent: true
zone: dmz
+- ansible.posix.firewalld:
+ forward: yes
+ state: enabled
+ permanent: yes
+ zone: custom
+
- ansible.posix.firewalld:
zone: custom
state: present
@@ -218,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
@@ -386,6 +399,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
@@ -759,6 +815,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%%']),
),
@@ -770,7 +827,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']
],
)
@@ -780,6 +837,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)
@@ -830,7 +888,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(
@@ -1002,6 +1060,23 @@ 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:
+
+ if StrictVersion(FIREWALLD_VERSION) < StrictVersion('0.9.0'):
+ module.fail_json(msg='Intra zone forwarding requires firewalld>=0.9.0. Current version is {0}.'.format(FIREWALLD_VERSION))
+
+ 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(
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..c8b8d62
--- /dev/null
+++ b/tests/integration/targets/firewalld/tasks/forward_test_cases.yml
@@ -0,0 +1,86 @@
+# 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: query firewalld version
+ package_facts:
+
+- name: run tests if intra zone forwarding is supported
+ block:
+
+ # 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: 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
+
+ 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
diff --git a/tests/integration/targets/firewalld/tasks/run_all_tests.yml b/tests/integration/targets/firewalld/tasks/run_all_tests.yml
index 5027c1c..27bcac8 100644
--- a/tests/integration/targets/firewalld/tasks/run_all_tests.yml
+++ b/tests/integration/targets/firewalld/tasks/run_all_tests.yml
@@ -24,3 +24,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
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'"
|