Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new feature to firewalld module allowing the default zone to be set. #405

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion plugins/modules/firewalld.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@
description:
- The masquerade setting you would like to enable/disable to/from zones within firewalld.
type: str
default:
description:
- Indicates that the targeted zone should be set as firewalld's default zone.
- This change must always be both immediate (when firewalld is running) and permanent.
type: bool
offline:
description:
- Whether to run this module even when firewalld is offline.
Expand Down Expand Up @@ -213,6 +218,13 @@
permanent: yes
immediate: yes
state: enabled

- name: Set the default zone to 'trusted'
ansible.builtin.firewalld:
zone: trusted
permanent: true
default: true
state: enabled
'''

from ansible.module_utils.basic import AnsibleModule
Expand All @@ -222,6 +234,7 @@
try:
from firewall.client import Rich_Rule
from firewall.client import FirewallClientZoneSettings
from firewall.config import FALLBACK_ZONE
except ImportError:
# The import errors are handled via FirewallTransaction, don't need to
# duplicate that here
Expand Down Expand Up @@ -696,6 +709,47 @@ def set_disabled_permanent(self):
zone_obj.remove()


class DefaultZoneTransaction(FirewallTransaction):
"""
DefaultZoneTransaction
"""

def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False):
super(DefaultZoneTransaction, self).__init__(
module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate
)
self.upstream_default_zone = FALLBACK_ZONE
self.enabled_msg = "Updated default zone to %s" % self.zone
self.disabled_msg = "Reverted default zone from %s to upstream default %s" % (self.zone, self.upstream_default_zone)
if (not permanent) or not (fw_offline or immediate):
self.module.fail_json(msg="Default zone changes must be permanent and when daemon is online must also be immediate")

def get_enabled_immediate(self):
return self.fw.getDefaultZone() == self.zone

def get_enabled_permanent(self):
default_zone = self.fw.get_default_zone() if fw_offline else self.fw.getDefaultZone()
return self.zone == default_zone

def set_enabled_immediate(self):
pass # permanent default zone change will also apply immediately to a running daemon

def set_enabled_permanent(self):
if fw_offline:
self.fw.set_default_zone(self.zone)
else:
self.fw.setDefaultZone(self.zone)

def set_disabled_immediate(self):
pass # permanent default zone change will also apply immediately to a running daemon

def set_disabled_permanent(self):
if fw_offline:
self.fw.set_default_zone(self.upstream_default_zone)
else:
self.fw.setDefaultZone(self.upstream_default_zone)


class ForwardPortTransaction(FirewallTransaction):
"""
ForwardPortTransaction
Expand Down Expand Up @@ -751,18 +805,20 @@ def main():
timeout=dict(type='int', default=0),
interface=dict(type='str'),
masquerade=dict(type='str'),
default=dict(type='bool'),
offline=dict(type='bool'),
target=dict(type='str', choices=['default', 'ACCEPT', 'DROP', '%%REJECT%%']),
),
supports_check_mode=True,
required_by=dict(
interface=('zone',),
target=('zone',),
default=('zone',),
source=('permanent',),
),
mutually_exclusive=[
['icmp_block', 'icmp_block_inversion', 'service', 'port', 'port_forward', 'rich_rule',
'interface', 'masquerade', 'source', 'target']
'interface', 'masquerade', 'source', 'target', 'default']
],
)

Expand All @@ -772,6 +828,7 @@ def main():
timeout = module.params['timeout']
interface = module.params['interface']
masquerade = module.params['masquerade']
default = module.params['default']

# Sanity checks
FirewallTransaction.sanity_check(module)
Expand Down Expand Up @@ -1008,6 +1065,20 @@ def main():
changed, transaction_msgs = transaction.run()
msgs = msgs + transaction_msgs

if default is not None:
expected_state = 'enabled' if (desired_state == 'enabled') == default else 'disabled'
transaction = DefaultZoneTransaction(
module,
action_args=(),
zone=zone,
desired_state=expected_state,
permanent=permanent,
immediate=immediate,
)

changed, transaction_msgs = transaction.run()
msgs = msgs + transaction_msgs

''' If there are no changes within the zone we are operating on the zone itself '''
if not modification and desired_state in ['absent', 'present']:

Expand Down
2 changes: 2 additions & 0 deletions tests/integration/targets/firewalld/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
state: stopped

- import_tasks: run_all_tests.yml
# FIXME currently fails when the daemon is running AND executed inside a container
- import_tasks: zone_default_test_cases.yml
when: check_output.rc == 0

when:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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|source|target|default'"
115 changes: 115 additions & 0 deletions tests/integration/targets/firewalld/tasks/zone_default_test_cases.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Test playbook for the firewalld module - zone default operations
# (c) 2022, Gregory Furlong <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

- name: Set default zone to trusted (default - true / state - enabled)
block:
- name: Update default zone to trusted
ansible.posix.firewalld:
zone: trusted
default: True
permanent: True
state: enabled
register: result

- name: Default zone is trusted
ansible.builtin.assert:
that:
- result is changed

- name: Update default zone to trusted (verify not changed)
ansible.posix.firewalld:
zone: trusted
default: True
permanent: True
state: enabled
register: result

- name: Default zone is trusted (verify not changed)
ansible.builtin.assert:
that:
- result is not changed

- name: Revert default zone to upstream default (default - false / state - enabled)
block:
- name: Revert default zone to upstream default
ansible.posix.firewalld:
zone: trusted
default: False
permanent: True
state: enabled
register: result

- name: Default zone is reverted
ansible.builtin.assert:
that:
- result is changed

- name: Revert default zone to upstream default (verify not changed)
ansible.posix.firewalld:
zone: trusted
default: False
permanent: True
state: enabled
register: result

- name: Default zone is reverted (verify not changed)
ansible.builtin.assert:
that:
- result is not changed

- name: Set default zone to trusted (default - false / state - disabled)
block:
- name: Update default zone to trusted
ansible.posix.firewalld:
zone: trusted
default: False
permanent: True
state: disabled
register: result

- name: Default zone is trusted
ansible.builtin.assert:
that:
- result is changed

- name: Update default zone to trusted (verify not changed)
ansible.posix.firewalld:
zone: trusted
default: False
permanent: True
state: disabled
register: result

- name: Default zone is trusted (verify not changed)
ansible.builtin.assert:
that:
- result is not changed

- name: Revert default zone to upstream default (default - true / state - disabled)
block:
- name: Revert default zone to upstream default
ansible.posix.firewalld:
zone: trusted
default: True
permanent: True
state: disabled
register: result

- name: Default zone is reverted
ansible.builtin.assert:
that:
- result is changed

- name: Revert default zone to upstream default (verify not changed)
ansible.posix.firewalld:
zone: trusted
default: True
permanent: True
state: disabled
register: result

- name: Default zone is reverted (verify not changed)
ansible.builtin.assert:
that:
- result is not changed