firewalld: Implement zone operations (#32845)
* firewalld: Implement zone operations Zones are removed or added when no other operations are used in conjunction with the keywords 'present' or 'absent'. This leads to a logical and natural syntax of: - firewalld: zone: foo state: present for adding or removing zones. Signed-off-by: Felix Kaechele <felix@kaechele.ca> * firewalld: zone ops: addressed review concerns - Added more documentation on the peculiarities of the zone operations - Output meaningful error messages when trying to use zones incorrectly Signed-off-by: Felix Kaechele <felix@kaechele.ca>
This commit is contained in:
parent
9ff5c15f57
commit
8475171f67
1 changed files with 99 additions and 18 deletions
|
@ -71,9 +71,12 @@ options:
|
|||
version_added: "1.9"
|
||||
state:
|
||||
description:
|
||||
- "Should this port accept(enabled) or reject(disabled) connections."
|
||||
- >
|
||||
Enable or disable a setting.
|
||||
For ports: Should this port accept(enabled) or reject(disabled) connections.
|
||||
The states "present" and "absent" can only be used in zone level operations (i.e. when no other parameters but zone and state are set).
|
||||
required: true
|
||||
choices: [ "enabled", "disabled" ]
|
||||
choices: [ "enabled", "disabled", "present", "absent" ]
|
||||
timeout:
|
||||
description:
|
||||
- "The amount of time the rule should be in effect for when non-permanent."
|
||||
|
@ -88,6 +91,12 @@ options:
|
|||
notes:
|
||||
- Not tested on any Debian based system.
|
||||
- Requires the python2 bindings of firewalld, which may not be installed by default if the distribution switched to python 3
|
||||
- Zone transactions (creating, deleting) can be performed by using only the zone and state parameters "present" or "absent".
|
||||
Note that zone transactions must explicitly be permanent. This is a limitation in firewalld.
|
||||
This also means that you will have to reload firewalld after adding a zone that you wish to perfom immediate actions on.
|
||||
The module will not take care of this for you implicitly because that would undo any previously performed immediate actions which were not
|
||||
permanent. Therefor, if you require immediate access to a newly created zone it is recommended you reload firewalld immediately after the zone
|
||||
creation returns with a changed state and before you perform any other immediate, non-permanent actions on that zone.
|
||||
requirements: [ 'firewalld >= 0.2.11' ]
|
||||
author: "Adam Miller (@maxamillion)"
|
||||
'''
|
||||
|
@ -135,6 +144,11 @@ EXAMPLES = '''
|
|||
state: enabled
|
||||
permanent: true
|
||||
zone: dmz
|
||||
|
||||
- firewalld:
|
||||
zone: custom
|
||||
state: present
|
||||
permanent: true
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
@ -149,6 +163,7 @@ try:
|
|||
|
||||
from firewall.client import Rich_Rule
|
||||
from firewall.client import FirewallClient
|
||||
from firewall.client import FirewallClientZoneSettings
|
||||
fw = None
|
||||
fw_offline = False
|
||||
import_failure = False
|
||||
|
@ -165,7 +180,6 @@ try:
|
|||
# NOTE:
|
||||
# online and offline operations do not share a common firewalld API
|
||||
from firewall.core.fw_test import Firewall_test
|
||||
from firewall.client import FirewallClientZoneSettings
|
||||
fw = Firewall_test()
|
||||
fw.start()
|
||||
|
||||
|
@ -183,18 +197,21 @@ class FirewallTransaction(object):
|
|||
global module
|
||||
|
||||
def __init__(self, fw, action_args=(), zone=None, desired_state=None,
|
||||
permanent=False, immediate=False, fw_offline=False):
|
||||
permanent=False, immediate=False, fw_offline=False,
|
||||
enabled_values=None, disabled_values=None):
|
||||
# type: (firewall.client, tuple, str, bool, bool, bool)
|
||||
"""
|
||||
initializer the transaction
|
||||
|
||||
:fw: firewall client instance
|
||||
:action_args: tuple, args to pass for the action to take place
|
||||
:zone: str, firewall zone
|
||||
:desired_state: str, the desired state (enabled, disabled, etc)
|
||||
:permanent: bool, action should be permanent
|
||||
:immediate: bool, action should take place immediately
|
||||
:fw_offline: bool, action takes place as if the firewall were offline
|
||||
:fw: firewall client instance
|
||||
:action_args: tuple, args to pass for the action to take place
|
||||
:zone: str, firewall zone
|
||||
:desired_state: str, the desired state (enabled, disabled, etc)
|
||||
:permanent: bool, action should be permanent
|
||||
:immediate: bool, action should take place immediately
|
||||
:fw_offline: bool, action takes place as if the firewall were offline
|
||||
:enabled_values: str[], acceptable values for enabling something (default: enabled)
|
||||
:disabled_values: str[], acceptable values for disabling something (default: disabled)
|
||||
"""
|
||||
|
||||
self.fw = fw
|
||||
|
@ -204,6 +221,8 @@ class FirewallTransaction(object):
|
|||
self.permanent = permanent
|
||||
self.immediate = immediate
|
||||
self.fw_offline = fw_offline
|
||||
self.enabled_values = enabled_values or ["enabled"]
|
||||
self.disabled_values = disabled_values or ["disabled"]
|
||||
|
||||
# List of messages that we'll call module.fail_json or module.exit_json
|
||||
# with.
|
||||
|
@ -214,12 +233,6 @@ class FirewallTransaction(object):
|
|||
self.enabled_msg = None
|
||||
self.disabled_msg = None
|
||||
|
||||
# List of acceptable values to enable/diable something
|
||||
# Right now these are only 1 each but in the event we want to add more
|
||||
# later, this will make it easy.
|
||||
self.enabled_values = ["enabled"]
|
||||
self.disabled_values = ["disabled"]
|
||||
|
||||
#####################
|
||||
# exception handling
|
||||
#
|
||||
|
@ -728,6 +741,52 @@ class SourceTransaction(FirewallTransaction):
|
|||
self.update_fw_settings(fw_zone, fw_settings)
|
||||
|
||||
|
||||
class ZoneTransaction(FirewallTransaction):
|
||||
"""
|
||||
ZoneTransaction
|
||||
"""
|
||||
|
||||
def __init__(self, fw, action_args=None, zone=None, desired_state=None,
|
||||
permanent=True, immediate=False, fw_offline=False,
|
||||
enabled_values=None, disabled_values=None):
|
||||
super(ZoneTransaction, self).__init__(
|
||||
fw, action_args=action_args, desired_state=desired_state, zone=zone,
|
||||
permanent=permanent, immediate=immediate, fw_offline=fw_offline,
|
||||
enabled_values=enabled_values or ["present"],
|
||||
disabled_values=disabled_values or ["absent"])
|
||||
|
||||
self.enabled_msg = "Added zone %s" % \
|
||||
(self.zone)
|
||||
|
||||
self.disabled_msg = "Removed zone %s" % \
|
||||
(self.zone)
|
||||
|
||||
self.tx_not_permanent_error_msg = "Zone operations must be permanent. " \
|
||||
"Make sure you didn't set the 'permanent' flag to 'false' or the 'immediate' flag to 'true'."
|
||||
|
||||
def get_enabled_immediate(self):
|
||||
module.fail_json(msg=self.tx_not_permanent_error_msg)
|
||||
|
||||
def get_enabled_permanent(self):
|
||||
if self.zone in fw.config().getZoneNames():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_enabled_immediate(self):
|
||||
module.fail_json(msg=self.tx_not_permanent_error_msg)
|
||||
|
||||
def set_enabled_permanent(self):
|
||||
fw.config().addZone(self.zone, FirewallClientZoneSettings())
|
||||
|
||||
def set_disabled_immediate(self):
|
||||
module.fail_json(msg=self.tx_not_permanent_error_msg)
|
||||
|
||||
def set_disabled_permanent(self):
|
||||
zone_obj = self.fw.config().getZoneByName(self.zone)
|
||||
zone_obj.remove()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
global module
|
||||
|
@ -740,7 +799,7 @@ def main():
|
|||
immediate=dict(type='bool', default=False),
|
||||
source=dict(required=False, default=None),
|
||||
permanent=dict(type='bool', required=False, default=None),
|
||||
state=dict(choices=['enabled', 'disabled'], required=True),
|
||||
state=dict(choices=['enabled', 'disabled', 'present', 'absent'], required=True),
|
||||
timeout=dict(type='int', required=False, default=0),
|
||||
interface=dict(required=False, default=None),
|
||||
masquerade=dict(required=False, default=None),
|
||||
|
@ -825,6 +884,10 @@ def main():
|
|||
module.fail_json(
|
||||
msg='can only operate on port, service, rich_rule, or interface at once'
|
||||
)
|
||||
elif modification_count > 0 and desired_state in ['absent', 'present']:
|
||||
module.fail_json(
|
||||
msg='absent and present state can only be used in zone level operations'
|
||||
)
|
||||
|
||||
if service is not None:
|
||||
|
||||
|
@ -926,6 +989,24 @@ def main():
|
|||
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 modification_count == 0 and desired_state in ['absent', 'present']:
|
||||
|
||||
transaction = ZoneTransaction(
|
||||
fw,
|
||||
action_args=(),
|
||||
zone=zone,
|
||||
desired_state=desired_state,
|
||||
permanent=permanent,
|
||||
immediate=immediate,
|
||||
fw_offline=fw_offline
|
||||
)
|
||||
|
||||
changed, transaction_msgs = transaction.run()
|
||||
msgs = msgs + transaction_msgs
|
||||
if changed is True:
|
||||
msgs.append("Changed zone %s to %s" % (zone, desired_state))
|
||||
|
||||
if fw_offline:
|
||||
msgs.append("(offline operation: only on-disk configs were altered)")
|
||||
|
||||
|
|
Loading…
Reference in a new issue