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"
|
version_added: "1.9"
|
||||||
state:
|
state:
|
||||||
description:
|
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
|
required: true
|
||||||
choices: [ "enabled", "disabled" ]
|
choices: [ "enabled", "disabled", "present", "absent" ]
|
||||||
timeout:
|
timeout:
|
||||||
description:
|
description:
|
||||||
- "The amount of time the rule should be in effect for when non-permanent."
|
- "The amount of time the rule should be in effect for when non-permanent."
|
||||||
|
@ -88,6 +91,12 @@ options:
|
||||||
notes:
|
notes:
|
||||||
- Not tested on any Debian based system.
|
- 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
|
- 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' ]
|
requirements: [ 'firewalld >= 0.2.11' ]
|
||||||
author: "Adam Miller (@maxamillion)"
|
author: "Adam Miller (@maxamillion)"
|
||||||
'''
|
'''
|
||||||
|
@ -135,6 +144,11 @@ EXAMPLES = '''
|
||||||
state: enabled
|
state: enabled
|
||||||
permanent: true
|
permanent: true
|
||||||
zone: dmz
|
zone: dmz
|
||||||
|
|
||||||
|
- firewalld:
|
||||||
|
zone: custom
|
||||||
|
state: present
|
||||||
|
permanent: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
@ -149,6 +163,7 @@ try:
|
||||||
|
|
||||||
from firewall.client import Rich_Rule
|
from firewall.client import Rich_Rule
|
||||||
from firewall.client import FirewallClient
|
from firewall.client import FirewallClient
|
||||||
|
from firewall.client import FirewallClientZoneSettings
|
||||||
fw = None
|
fw = None
|
||||||
fw_offline = False
|
fw_offline = False
|
||||||
import_failure = False
|
import_failure = False
|
||||||
|
@ -165,7 +180,6 @@ try:
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# online and offline operations do not share a common firewalld API
|
# online and offline operations do not share a common firewalld API
|
||||||
from firewall.core.fw_test import Firewall_test
|
from firewall.core.fw_test import Firewall_test
|
||||||
from firewall.client import FirewallClientZoneSettings
|
|
||||||
fw = Firewall_test()
|
fw = Firewall_test()
|
||||||
fw.start()
|
fw.start()
|
||||||
|
|
||||||
|
@ -183,18 +197,21 @@ class FirewallTransaction(object):
|
||||||
global module
|
global module
|
||||||
|
|
||||||
def __init__(self, fw, action_args=(), zone=None, desired_state=None,
|
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)
|
# type: (firewall.client, tuple, str, bool, bool, bool)
|
||||||
"""
|
"""
|
||||||
initializer the transaction
|
initializer the transaction
|
||||||
|
|
||||||
:fw: firewall client instance
|
:fw: firewall client instance
|
||||||
:action_args: tuple, args to pass for the action to take place
|
:action_args: tuple, args to pass for the action to take place
|
||||||
:zone: str, firewall zone
|
:zone: str, firewall zone
|
||||||
:desired_state: str, the desired state (enabled, disabled, etc)
|
:desired_state: str, the desired state (enabled, disabled, etc)
|
||||||
:permanent: bool, action should be permanent
|
:permanent: bool, action should be permanent
|
||||||
:immediate: bool, action should take place immediately
|
:immediate: bool, action should take place immediately
|
||||||
:fw_offline: bool, action takes place as if the firewall were offline
|
: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
|
self.fw = fw
|
||||||
|
@ -204,6 +221,8 @@ class FirewallTransaction(object):
|
||||||
self.permanent = permanent
|
self.permanent = permanent
|
||||||
self.immediate = immediate
|
self.immediate = immediate
|
||||||
self.fw_offline = fw_offline
|
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
|
# List of messages that we'll call module.fail_json or module.exit_json
|
||||||
# with.
|
# with.
|
||||||
|
@ -214,12 +233,6 @@ class FirewallTransaction(object):
|
||||||
self.enabled_msg = None
|
self.enabled_msg = None
|
||||||
self.disabled_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
|
# exception handling
|
||||||
#
|
#
|
||||||
|
@ -728,6 +741,52 @@ class SourceTransaction(FirewallTransaction):
|
||||||
self.update_fw_settings(fw_zone, fw_settings)
|
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():
|
def main():
|
||||||
|
|
||||||
global module
|
global module
|
||||||
|
@ -740,7 +799,7 @@ def main():
|
||||||
immediate=dict(type='bool', default=False),
|
immediate=dict(type='bool', default=False),
|
||||||
source=dict(required=False, default=None),
|
source=dict(required=False, default=None),
|
||||||
permanent=dict(type='bool', 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),
|
timeout=dict(type='int', required=False, default=0),
|
||||||
interface=dict(required=False, default=None),
|
interface=dict(required=False, default=None),
|
||||||
masquerade=dict(required=False, default=None),
|
masquerade=dict(required=False, default=None),
|
||||||
|
@ -825,6 +884,10 @@ def main():
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg='can only operate on port, service, rich_rule, or interface at once'
|
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:
|
if service is not None:
|
||||||
|
|
||||||
|
@ -926,6 +989,24 @@ def main():
|
||||||
changed, transaction_msgs = transaction.run()
|
changed, transaction_msgs = transaction.run()
|
||||||
msgs = msgs + transaction_msgs
|
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:
|
if fw_offline:
|
||||||
msgs.append("(offline operation: only on-disk configs were altered)")
|
msgs.append("(offline operation: only on-disk configs were altered)")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue