From ea952f0825388002dffa01431c1a4e83bcb55f4d Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Mon, 6 Jul 2020 16:59:19 -0500 Subject: [PATCH 1/6] migrate firewalld from community.general Signed-off-by: Adam Miller --- plugins/module_utils/firewalld.py | 316 +++++++ plugins/modules/firewalld.py | 859 ++++++++++++++++++ tests/integration/targets/firewalld/aliases | 6 + .../targets/firewalld/meta/main.yml | 2 + .../targets/firewalld/tasks/main.yml | 56 ++ .../firewalld/tasks/port_test_cases.yml | 65 ++ .../targets/firewalld/tasks/run_all_tests.yml | 35 + .../firewalld/tasks/service_test_cases.yml | 65 ++ .../firewalld/tasks/source_test_cases.yml | 85 ++ tests/sanity/ignore-2.10.txt | 2 + tests/sanity/ignore-2.9.txt | 2 + 11 files changed, 1493 insertions(+) create mode 100644 plugins/module_utils/firewalld.py create mode 100644 plugins/modules/firewalld.py create mode 100644 tests/integration/targets/firewalld/aliases create mode 100644 tests/integration/targets/firewalld/meta/main.yml create mode 100644 tests/integration/targets/firewalld/tasks/main.yml create mode 100644 tests/integration/targets/firewalld/tasks/port_test_cases.yml create mode 100644 tests/integration/targets/firewalld/tasks/run_all_tests.yml create mode 100644 tests/integration/targets/firewalld/tasks/service_test_cases.yml create mode 100644 tests/integration/targets/firewalld/tasks/source_test_cases.yml diff --git a/plugins/module_utils/firewalld.py b/plugins/module_utils/firewalld.py new file mode 100644 index 0000000..b44e031 --- /dev/null +++ b/plugins/module_utils/firewalld.py @@ -0,0 +1,316 @@ +# -*- coding: utf-8 -*- +# +# (c) 2013-2018, Adam Miller (maxamillion@fedoraproject.org) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Imports and info for sanity checking +from distutils.version import LooseVersion + +FW_VERSION = None +fw = None +fw_offline = False +import_failure = True +try: + import firewall.config + FW_VERSION = firewall.config.VERSION + + from firewall.client import FirewallClient + from firewall.client import FirewallClientZoneSettings + from firewall.errors import FirewallError + import_failure = False + + try: + fw = FirewallClient() + fw.getDefaultZone() + + except (AttributeError, FirewallError): + # Firewalld is not currently running, permanent-only operations + fw_offline = True + + # Import other required parts of the firewalld API + # + # NOTE: + # online and offline operations do not share a common firewalld API + try: + from firewall.core.fw_test import Firewall_test + fw = Firewall_test() + except (ModuleNotFoundError): + # In firewalld version 0.7.0 this behavior changed + from firewall.core.fw import Firewall + fw = Firewall(offline=True) + + fw.start() +except ImportError: + pass + + +class FirewallTransaction(object): + """ + FirewallTransaction + + This is the base class for all firewalld transactions we might want to have + """ + + def __init__(self, module, action_args=(), zone=None, desired_state=None, + permanent=False, immediate=False, enabled_values=None, disabled_values=None): + # type: (firewall.client, tuple, str, bool, bool, bool) + """ + initializer the transaction + + :module: AnsibleModule, instance of AnsibleModule + :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 + :enabled_values: str[], acceptable values for enabling something (default: enabled) + :disabled_values: str[], acceptable values for disabling something (default: disabled) + """ + + self.module = module + self.fw = fw + self.action_args = action_args + + if zone: + self.zone = zone + else: + if fw_offline: + self.zone = fw.get_default_zone() + else: + self.zone = fw.getDefaultZone() + + self.desired_state = desired_state + 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. + self.msgs = [] + + # Allow for custom messages to be added for certain subclass transaction + # types + self.enabled_msg = None + self.disabled_msg = None + + ##################### + # exception handling + # + def action_handler(self, action_func, action_func_args): + """ + Function to wrap calls to make actions on firewalld in try/except + logic and emit (hopefully) useful error messages + """ + + try: + return action_func(*action_func_args) + except Exception as e: + + # If there are any commonly known errors that we should provide more + # context for to help the users diagnose what's wrong. Handle that here + if "INVALID_SERVICE" in "%s" % e: + self.msgs.append("Services are defined by port/tcp relationship and named as they are in /etc/services (on most systems)") + + if len(self.msgs) > 0: + self.module.fail_json( + msg='ERROR: Exception caught: %s %s' % (e, ', '.join(self.msgs)) + ) + else: + self.module.fail_json(msg='ERROR: Exception caught: %s' % e) + + 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)) + ) + else: + fw_zone = self.fw.config().getZoneByName(self.zone) + fw_settings = fw_zone.getSettings() + + return (fw_zone, fw_settings) + + def update_fw_settings(self, fw_zone, fw_settings): + if self.fw_offline: + self.fw.config.set_zone_config(fw_zone, fw_settings.settings) + else: + fw_zone.update(fw_settings) + + def get_enabled_immediate(self): + raise NotImplementedError + + def get_enabled_permanent(self): + raise NotImplementedError + + def set_enabled_immediate(self): + raise NotImplementedError + + def set_enabled_permanent(self): + raise NotImplementedError + + def set_disabled_immediate(self): + raise NotImplementedError + + def set_disabled_permanent(self): + raise NotImplementedError + + def run(self): + """ + run + + This function contains the "transaction logic" where as all operations + follow a similar pattern in order to perform their action but simply + call different functions to carry that action out. + """ + + self.changed = False + + if self.immediate and self.permanent: + is_enabled_permanent = self.action_handler( + self.get_enabled_permanent, + self.action_args + ) + is_enabled_immediate = self.action_handler( + self.get_enabled_immediate, + self.action_args + ) + self.msgs.append('Permanent and Non-Permanent(immediate) operation') + + if self.desired_state in self.enabled_values: + if not is_enabled_permanent or not is_enabled_immediate: + if self.module.check_mode: + self.module.exit_json(changed=True) + if not is_enabled_permanent: + self.action_handler( + self.set_enabled_permanent, + self.action_args + ) + self.changed = True + if not is_enabled_immediate: + self.action_handler( + self.set_enabled_immediate, + self.action_args + ) + self.changed = True + if self.changed and self.enabled_msg: + self.msgs.append(self.enabled_msg) + + elif self.desired_state in self.disabled_values: + if is_enabled_permanent or is_enabled_immediate: + if self.module.check_mode: + self.module.exit_json(changed=True) + if is_enabled_permanent: + self.action_handler( + self.set_disabled_permanent, + self.action_args + ) + self.changed = True + if is_enabled_immediate: + self.action_handler( + self.set_disabled_immediate, + self.action_args + ) + self.changed = True + if self.changed and self.disabled_msg: + self.msgs.append(self.disabled_msg) + + elif self.permanent and not self.immediate: + is_enabled = self.action_handler( + self.get_enabled_permanent, + self.action_args + ) + self.msgs.append('Permanent operation') + + if self.desired_state in self.enabled_values: + if not is_enabled: + if self.module.check_mode: + self.module.exit_json(changed=True) + + self.action_handler( + self.set_enabled_permanent, + self.action_args + ) + self.changed = True + if self.changed and self.enabled_msg: + self.msgs.append(self.enabled_msg) + + elif self.desired_state in self.disabled_values: + if is_enabled: + if self.module.check_mode: + self.module.exit_json(changed=True) + + self.action_handler( + self.set_disabled_permanent, + self.action_args + ) + self.changed = True + if self.changed and self.disabled_msg: + self.msgs.append(self.disabled_msg) + + elif self.immediate and not self.permanent: + is_enabled = self.action_handler( + self.get_enabled_immediate, + self.action_args + ) + self.msgs.append('Non-permanent operation') + + if self.desired_state in self.enabled_values: + if not is_enabled: + if self.module.check_mode: + self.module.exit_json(changed=True) + + self.action_handler( + self.set_enabled_immediate, + self.action_args + ) + self.changed = True + if self.changed and self.enabled_msg: + self.msgs.append(self.enabled_msg) + + elif self.desired_state in self.disabled_values: + if is_enabled: + if self.module.check_mode: + self.module.exit_json(changed=True) + + self.action_handler( + self.set_disabled_immediate, + self.action_args + ) + self.changed = True + if self.changed and self.disabled_msg: + self.msgs.append(self.disabled_msg) + + return (self.changed, self.msgs) + + @staticmethod + def sanity_check(module): + """ + Perform sanity checking, version checks, etc + + :module: AnsibleModule instance + """ + + if FW_VERSION and fw_offline: + # Pre-run version checking + if LooseVersion(FW_VERSION) < LooseVersion("0.3.9"): + module.fail_json(msg='unsupported version of firewalld, offline operations require >= 0.3.9 - found: {0}'.format(FW_VERSION)) + elif FW_VERSION and not fw_offline: + # Pre-run version checking + if LooseVersion(FW_VERSION) < LooseVersion("0.2.11"): + module.fail_json(msg='unsupported version of firewalld, requires >= 0.2.11 - found: {0}'.format(FW_VERSION)) + + # Check for firewalld running + try: + if fw.connected is False: + module.fail_json(msg='firewalld service must be running, or try with offline=true') + except AttributeError: + module.fail_json(msg="firewalld connection can't be established,\ + installed version (%s) likely too old. Requires firewalld >= 0.2.11" % FW_VERSION) + + if import_failure: + module.fail_json( + msg='Python Module not found: firewalld and its python module are required for this module, \ + version 0.2.11 or newer required (0.3.9 or newer for offline operations)' + ) diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py new file mode 100644 index 0000000..73666ef --- /dev/null +++ b/plugins/modules/firewalld.py @@ -0,0 +1,859 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2013, Adam Miller +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: firewalld +short_description: Manage arbitrary ports/services with firewalld +description: + - This module allows for addition or deletion of services and ports (either TCP or UDP) in either running or permanent firewalld rules. +options: + service: + description: + - Name of a service to add/remove to/from firewalld. + - The service must be listed in output of firewall-cmd --get-services. + type: str + port: + description: + - 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 + rich_rule: + description: + - Rich rule to add/remove to/from firewalld. + type: str + source: + description: + - The source/network you would like to add/remove to/from firewalld. + type: str + interface: + description: + - The interface you would like to add/remove to/from a zone in firewalld. + type: str + icmp_block: + description: + - The ICMP block you would like to add/remove to/from a zone in firewalld. + type: str + icmp_block_inversion: + description: + - Enable/Disable inversion of ICMP blocks for a zone in firewalld. + type: str + zone: + description: + - The firewalld zone to add/remove to/from. + - Note that the default zone can be configured per system but C(public) is default from upstream. + - Available choices can be extended based on per-system configs, listed here are "out of the box" defaults. + - Possible values include C(block), C(dmz), C(drop), C(external), C(home), C(internal), C(public), C(trusted), C(work). + type: str + permanent: + description: + - Should this configuration be in the running firewalld configuration or persist across reboots. + - As of Ansible 2.3, permanent operations can operate on firewalld configs when it is not running (requires firewalld >= 3.0.9). + - Note that if this is C(no), immediate is assumed C(yes). + type: bool + immediate: + description: + - Should this configuration be applied immediately, if set as permanent. + type: bool + default: no + state: + description: + - Enable or disable a setting. + - 'For ports: Should this port accept (enabled) or reject (disabled) connections.' + - The states C(present) and C(absent) can only be used in zone level operations (i.e. when no other parameters but zone and state are set). + type: str + required: true + choices: [ absent, disabled, enabled, present ] + timeout: + description: + - The amount of time the rule should be in effect for when non-permanent. + type: int + default: 0 + masquerade: + description: + - The masquerade setting you would like to enable/disable to/from zones within firewalld. + type: str + offline: + description: + - Whether to run this module even when firewalld is offline. + type: bool +notes: + - Not tested on any Debian based system. + - Requires the python2 bindings of firewalld, which may not be installed by default. + - For distributions where the python2 firewalld bindings are unavailable (e.g Fedora 28 and later) you will have to set the + ansible_python_interpreter for these hosts to the python3 interpreter path and install the python3 bindings. + - 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 perform 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. Therefore, 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) +''' + +EXAMPLES = r''' +- firewalld: + service: https + permanent: yes + state: enabled + +- firewalld: + port: 8081/tcp + permanent: yes + state: disabled + +- firewalld: + port: 161-162/udp + permanent: yes + state: enabled + +- firewalld: + zone: dmz + service: http + permanent: yes + state: enabled + +- firewalld: + rich_rule: rule service name="ftp" audit limit value="1/m" accept + permanent: yes + state: enabled + +- firewalld: + source: 192.0.2.0/24 + zone: internal + state: enabled + +- firewalld: + zone: trusted + interface: eth2 + permanent: yes + state: enabled + +- firewalld: + masquerade: yes + state: enabled + permanent: yes + zone: dmz + +- firewalld: + zone: custom + state: present + permanent: yes + +- firewalld: + zone: drop + state: enabled + permanent: yes + icmp_block_inversion: yes + +- firewalld: + zone: drop + state: enabled + permanent: yes + icmp_block: echo-request + +- name: Redirect port 443 to 8443 with Rich Rule + firewalld: + rich_rule: rule family=ipv4 forward-port port=443 protocol=tcp to-port=8443 + zone: public + permanent: yes + immediate: yes + state: enabled +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.module_utils.firewalld import FirewallTransaction, fw_offline + +try: + from firewall.client import Rich_Rule + from firewall.client import FirewallClientZoneSettings +except ImportError: + # The import errors are handled via FirewallTransaction, don't need to + # duplicate that here + pass + + +class IcmpBlockTransaction(FirewallTransaction): + """ + IcmpBlockTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(IcmpBlockTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + def get_enabled_immediate(self, icmp_block, timeout): + return icmp_block in self.fw.getIcmpBlocks(self.zone) + + def get_enabled_permanent(self, icmp_block, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + return icmp_block in fw_settings.getIcmpBlocks() + + def set_enabled_immediate(self, icmp_block, timeout): + self.fw.addIcmpBlock(self.zone, icmp_block, timeout) + + def set_enabled_permanent(self, icmp_block, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.addIcmpBlock(icmp_block) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self, icmp_block, timeout): + self.fw.removeIcmpBlock(self.zone, icmp_block) + + def set_disabled_permanent(self, icmp_block, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.removeIcmpBlock(icmp_block) + self.update_fw_settings(fw_zone, fw_settings) + + +class IcmpBlockInversionTransaction(FirewallTransaction): + """ + IcmpBlockInversionTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(IcmpBlockInversionTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + def get_enabled_immediate(self): + if self.fw.queryIcmpBlockInversion(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.getIcmpBlockInversion() is True: + return True + else: + return False + + def set_enabled_immediate(self): + self.fw.addIcmpBlockInversion(self.zone) + + def set_enabled_permanent(self): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.setIcmpBlockInversion(True) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self): + self.fw.removeIcmpBlockInversion(self.zone) + + def set_disabled_permanent(self): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.setIcmpBlockInversion(False) + self.update_fw_settings(fw_zone, fw_settings) + + +class ServiceTransaction(FirewallTransaction): + """ + ServiceTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(ServiceTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + def get_enabled_immediate(self, service, timeout): + if service in self.fw.getServices(self.zone): + return True + else: + return False + + def get_enabled_permanent(self, service, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + + if service in fw_settings.getServices(): + return True + else: + return False + + def set_enabled_immediate(self, service, timeout): + self.fw.addService(self.zone, service, timeout) + + def set_enabled_permanent(self, service, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.addService(service) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self, service, timeout): + self.fw.removeService(self.zone, service) + + def set_disabled_permanent(self, service, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.removeService(service) + self.update_fw_settings(fw_zone, fw_settings) + + +class MasqueradeTransaction(FirewallTransaction): + """ + MasqueradeTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(MasqueradeTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + self.enabled_msg = "Added masquerade to zone %s" % self.zone + self.disabled_msg = "Removed masquerade from zone %s" % self.zone + + def get_enabled_immediate(self): + if self.fw.queryMasquerade(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.getMasquerade() is True: + return True + else: + return False + + def set_enabled_immediate(self): + self.fw.addMasquerade(self.zone) + + def set_enabled_permanent(self): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.setMasquerade(True) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self): + self.fw.removeMasquerade(self.zone) + + def set_disabled_permanent(self): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.setMasquerade(False) + self.update_fw_settings(fw_zone, fw_settings) + + +class PortTransaction(FirewallTransaction): + """ + PortTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(PortTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + def get_enabled_immediate(self, port, protocol, timeout): + port_proto = [port, protocol] + if self.fw_offline: + fw_zone, fw_settings = self.get_fw_zone_settings() + ports_list = fw_settings.getPorts() + else: + ports_list = self.fw.getPorts(self.zone) + + if port_proto in ports_list: + return True + else: + return False + + def get_enabled_permanent(self, port, protocol, timeout): + port_proto = (port, protocol) + fw_zone, fw_settings = self.get_fw_zone_settings() + + if port_proto in fw_settings.getPorts(): + return True + else: + return False + + def set_enabled_immediate(self, port, protocol, timeout): + self.fw.addPort(self.zone, port, protocol, timeout) + + def set_enabled_permanent(self, port, protocol, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.addPort(port, protocol) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self, port, protocol, timeout): + self.fw.removePort(self.zone, port, protocol) + + def set_disabled_permanent(self, port, protocol, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.removePort(port, protocol) + self.update_fw_settings(fw_zone, fw_settings) + + +class InterfaceTransaction(FirewallTransaction): + """ + InterfaceTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(InterfaceTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + self.enabled_msg = "Changed %s to zone %s" % \ + (self.action_args[0], self.zone) + + self.disabled_msg = "Removed %s from zone %s" % \ + (self.action_args[0], self.zone) + + def get_enabled_immediate(self, interface): + if self.fw_offline: + fw_zone, fw_settings = self.get_fw_zone_settings() + interface_list = fw_settings.getInterfaces() + else: + interface_list = self.fw.getInterfaces(self.zone) + if interface in interface_list: + return True + else: + return False + + def get_enabled_permanent(self, interface): + fw_zone, fw_settings = self.get_fw_zone_settings() + + if interface in fw_settings.getInterfaces(): + return True + else: + return False + + def set_enabled_immediate(self, interface): + self.fw.changeZoneOfInterface(self.zone, interface) + + def set_enabled_permanent(self, interface): + fw_zone, fw_settings = self.get_fw_zone_settings() + if self.fw_offline: + iface_zone_objs = [] + for zone in self.fw.config.get_zones(): + old_zone_obj = self.fw.config.get_zone(zone) + if interface in old_zone_obj.interfaces: + iface_zone_objs.append(old_zone_obj) + if len(iface_zone_objs) > 1: + # Even it shouldn't happen, it's actually possible that + # the same interface is in several zone XML files + self.module.fail_json( + msg='ERROR: interface {0} is in {1} zone XML file, can only be in one'.format( + interface, + len(iface_zone_objs) + ) + ) + old_zone_obj = iface_zone_objs[0] + if old_zone_obj.name != self.zone: + old_zone_settings = FirewallClientZoneSettings( + self.fw.config.get_zone_config(old_zone_obj) + ) + old_zone_settings.removeInterface(interface) # remove from old + self.fw.config.set_zone_config( + old_zone_obj, + old_zone_settings.settings + ) + fw_settings.addInterface(interface) # add to new + self.fw.config.set_zone_config(fw_zone, fw_settings.settings) + else: + old_zone_name = self.fw.config().getZoneOfInterface(interface) + if old_zone_name != self.zone: + if old_zone_name: + old_zone_obj = self.fw.config().getZoneByName(old_zone_name) + old_zone_settings = old_zone_obj.getSettings() + old_zone_settings.removeInterface(interface) # remove from old + old_zone_obj.update(old_zone_settings) + fw_settings.addInterface(interface) # add to new + fw_zone.update(fw_settings) + + def set_disabled_immediate(self, interface): + self.fw.removeInterface(self.zone, interface) + + def set_disabled_permanent(self, interface): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.removeInterface(interface) + self.update_fw_settings(fw_zone, fw_settings) + + +class RichRuleTransaction(FirewallTransaction): + """ + RichRuleTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(RichRuleTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + def get_enabled_immediate(self, rule, timeout): + # Convert the rule string to standard format + # before checking whether it is present + rule = str(Rich_Rule(rule_str=rule)) + if rule in self.fw.getRichRules(self.zone): + return True + else: + return False + + def get_enabled_permanent(self, rule, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + # Convert the rule string to standard format + # before checking whether it is present + rule = str(Rich_Rule(rule_str=rule)) + if rule in fw_settings.getRichRules(): + return True + else: + return False + + def set_enabled_immediate(self, rule, timeout): + self.fw.addRichRule(self.zone, rule, timeout) + + def set_enabled_permanent(self, rule, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.addRichRule(rule) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self, rule, timeout): + self.fw.removeRichRule(self.zone, rule) + + def set_disabled_permanent(self, rule, timeout): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.removeRichRule(rule) + self.update_fw_settings(fw_zone, fw_settings) + + +class SourceTransaction(FirewallTransaction): + """ + SourceTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, permanent=False, immediate=False): + super(SourceTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, permanent=permanent, immediate=immediate + ) + + self.enabled_msg = "Added %s to zone %s" % \ + (self.action_args[0], self.zone) + + self.disabled_msg = "Removed %s from zone %s" % \ + (self.action_args[0], self.zone) + + def get_enabled_immediate(self, source): + if source in self.fw.getSources(self.zone): + return True + else: + return False + + def get_enabled_permanent(self, source): + fw_zone, fw_settings = self.get_fw_zone_settings() + if source in fw_settings.getSources(): + return True + else: + return False + + def set_enabled_immediate(self, source): + self.fw.addSource(self.zone, source) + + def set_enabled_permanent(self, source): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.addSource(source) + self.update_fw_settings(fw_zone, fw_settings) + + def set_disabled_immediate(self, source): + self.fw.removeSource(self.zone, source) + + def set_disabled_permanent(self, source): + fw_zone, fw_settings = self.get_fw_zone_settings() + fw_settings.removeSource(source) + self.update_fw_settings(fw_zone, fw_settings) + + +class ZoneTransaction(FirewallTransaction): + """ + ZoneTransaction + """ + + def __init__(self, module, action_args=None, zone=None, desired_state=None, + permanent=True, immediate=False, enabled_values=None, disabled_values=None): + super(ZoneTransaction, self).__init__( + module, action_args=action_args, desired_state=desired_state, zone=zone, + permanent=permanent, immediate=immediate, + 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): + self.module.fail_json(msg=self.tx_not_permanent_error_msg) + + def get_enabled_permanent(self): + zones = self.fw.config().listZones() + zone_names = [self.fw.config().getZone(z).get_property("name") for z in zones] + if self.zone in zone_names: + return True + else: + return False + + def set_enabled_immediate(self): + self.module.fail_json(msg=self.tx_not_permanent_error_msg) + + def set_enabled_permanent(self): + self.fw.config().addZone(self.zone, FirewallClientZoneSettings()) + + def set_disabled_immediate(self): + 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(): + + module = AnsibleModule( + argument_spec=dict( + icmp_block=dict(type='str'), + icmp_block_inversion=dict(type='str'), + service=dict(type='str'), + port=dict(type='str'), + rich_rule=dict(type='str'), + zone=dict(type='str'), + immediate=dict(type='bool', default=False), + source=dict(type='str'), + permanent=dict(type='bool'), + state=dict(type='str', required=True, choices=['absent', 'disabled', 'enabled', 'present']), + timeout=dict(type='int', default=0), + interface=dict(type='str'), + masquerade=dict(type='str'), + offline=dict(type='bool'), + ), + supports_check_mode=True, + required_by=dict( + interface=('zone',), + source=('permanent',), + ), + ) + + permanent = module.params['permanent'] + desired_state = module.params['state'] + immediate = module.params['immediate'] + timeout = module.params['timeout'] + interface = module.params['interface'] + masquerade = module.params['masquerade'] + + # Sanity checks + FirewallTransaction.sanity_check(module) + + # If neither permanent or immediate is provided, assume immediate (as + # written in the module's docs) + if not permanent and not immediate: + immediate = True + + # Verify required params are provided + if immediate and fw_offline: + module.fail_json(msg='firewall is not currently running, unable to perform immediate actions without a running firewall daemon') + + changed = False + msgs = [] + icmp_block = module.params['icmp_block'] + icmp_block_inversion = module.params['icmp_block_inversion'] + service = module.params['service'] + rich_rule = module.params['rich_rule'] + source = module.params['source'] + zone = module.params['zone'] + + if module.params['port'] is not None: + if '/' in module.params['port']: + port, protocol = module.params['port'].strip().split('/') + else: + protocol = None + if not protocol: + module.fail_json(msg='improper port format (missing protocol?)') + else: + port = None + + modification_count = 0 + if icmp_block is not None: + modification_count += 1 + if icmp_block_inversion is not None: + modification_count += 1 + if service is not None: + modification_count += 1 + if port is not None: + modification_count += 1 + if rich_rule is not None: + modification_count += 1 + if interface is not None: + modification_count += 1 + if masquerade is not None: + modification_count += 1 + if source is not None: + modification_count += 1 + + if modification_count > 1: + module.fail_json( + msg='can only operate on port, service, rich_rule, masquerade, icmp_block, icmp_block_inversion, interface or source 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 icmp_block is not None: + + transaction = IcmpBlockTransaction( + module, + action_args=(icmp_block, 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 icmp-block %s to %s" % (icmp_block, desired_state)) + + if icmp_block_inversion is not None: + + transaction = IcmpBlockInversionTransaction( + module, + action_args=(), + 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 icmp-block-inversion %s to %s" % (icmp_block_inversion, desired_state)) + + if service is not None: + + transaction = ServiceTransaction( + module, + action_args=(service, 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 service %s to %s" % (service, desired_state)) + + if source is not None: + + transaction = SourceTransaction( + module, + action_args=(source,), + zone=zone, + desired_state=desired_state, + permanent=permanent, + immediate=immediate, + ) + + changed, transaction_msgs = transaction.run() + msgs = msgs + transaction_msgs + + if port is not None: + + transaction = PortTransaction( + module, + action_args=(port, protocol, 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 %s to %s" % ( + "%s/%s" % (port, protocol), desired_state + ) + ) + + if rich_rule is not None: + + transaction = RichRuleTransaction( + module, + action_args=(rich_rule, 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 rich_rule %s to %s" % (rich_rule, desired_state)) + + if interface is not None: + + transaction = InterfaceTransaction( + module, + action_args=(interface,), + zone=zone, + desired_state=desired_state, + permanent=permanent, + immediate=immediate, + ) + + changed, transaction_msgs = transaction.run() + msgs = msgs + transaction_msgs + + if masquerade is not None: + + transaction = MasqueradeTransaction( + module, + action_args=(), + zone=zone, + desired_state=desired_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 modification_count == 0 and desired_state in ['absent', 'present']: + + transaction = ZoneTransaction( + module, + action_args=(), + 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 zone %s to %s" % (zone, desired_state)) + + if fw_offline: + msgs.append("(offline operation: only on-disk configs were altered)") + + module.exit_json(changed=changed, msg=', '.join(msgs)) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/firewalld/aliases b/tests/integration/targets/firewalld/aliases new file mode 100644 index 0000000..96ae90e --- /dev/null +++ b/tests/integration/targets/firewalld/aliases @@ -0,0 +1,6 @@ +destructive +shippable/posix/group3 +skip/aix +skip/freebsd +skip/osx +disabled # fixme diff --git a/tests/integration/targets/firewalld/meta/main.yml b/tests/integration/targets/firewalld/meta/main.yml new file mode 100644 index 0000000..5438ced --- /dev/null +++ b/tests/integration/targets/firewalld/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_pkg_mgr diff --git a/tests/integration/targets/firewalld/tasks/main.yml b/tests/integration/targets/firewalld/tasks/main.yml new file mode 100644 index 0000000..84af185 --- /dev/null +++ b/tests/integration/targets/firewalld/tasks/main.yml @@ -0,0 +1,56 @@ +# Test playbook for the firewalld module +# (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: Run firewalld tests + block: + - name: Ensure firewalld is installed + package: + name: firewalld + state: present + # This doesn't work for CentOS 6 because firewalld doesn't exist in CentOS6 + + - name: Check to make sure the firewalld python module is available. + shell: "{{ansible_python.executable}} -c 'import firewall'" + register: check_output + ignore_errors: true + + - name: Test Online Operations + block: + - name: start firewalld + service: + name: firewalld + state: started + + - import_tasks: run_all_tests.yml + when: check_output.rc == 0 + + - name: Test Offline Operations + block: + - name: stop firewalld + service: + name: firewalld + state: stopped + + - import_tasks: run_all_tests.yml + when: check_output.rc == 0 + + when: + - ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version('7', '>=') + - not (ansible_distribution == "Ubuntu" and ansible_distribution_version is version('14.04', '==')) + # Firewalld package on OpenSUSE (15+) require Python 3, so we skip on OpenSUSE running py2 on these newer distros + - not (ansible_os_family == "Suse" and ansible_distribution_major_version|int != 42 and ansible_python.version.major != 3) diff --git a/tests/integration/targets/firewalld/tasks/port_test_cases.yml b/tests/integration/targets/firewalld/tasks/port_test_cases.yml new file mode 100644 index 0000000..5891e75 --- /dev/null +++ b/tests/integration/targets/firewalld/tasks/port_test_cases.yml @@ -0,0 +1,65 @@ +# 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 test permanent enabled + firewalld: + port: 8081/tcp + 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: 8081/tcp + 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: 8081/tcp + 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: 8081/tcp + permanent: true + state: disabled + register: result + +- name: assert firewalld port test permanent disabled rerun worked (verify not changed) + assert: + that: + - result is not changed diff --git a/tests/integration/targets/firewalld/tasks/run_all_tests.yml b/tests/integration/targets/firewalld/tasks/run_all_tests.yml new file mode 100644 index 0000000..d463ef2 --- /dev/null +++ b/tests/integration/targets/firewalld/tasks/run_all_tests.yml @@ -0,0 +1,35 @@ +# Test playbook for the firewalld module +# (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: Ensure /run/firewalld exists + file: + path: /run/firewalld + state: directory + +# firewalld service operation test cases +- include_tasks: service_test_cases.yml + # Skipping on CentOS 8 due to https://github.com/ansible/ansible/issues/64750 + when: not (ansible_facts.distribution == "CentOS" and ansible_distribution_major_version is version('8', '==')) + +# firewalld port operation test cases +- include_tasks: port_test_cases.yml + # Skipping on CentOS 8 due to https://github.com/ansible/ansible/issues/64750 + when: not (ansible_facts.distribution == "CentOS" and ansible_distribution_major_version is version('8', '==')) + +# firewalld source operation test cases +- import_tasks: source_test_cases.yml diff --git a/tests/integration/targets/firewalld/tasks/service_test_cases.yml b/tests/integration/targets/firewalld/tasks/service_test_cases.yml new file mode 100644 index 0000000..3c95d81 --- /dev/null +++ b/tests/integration/targets/firewalld/tasks/service_test_cases.yml @@ -0,0 +1,65 @@ +# Test playbook for the firewalld module - service 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 service test permanent enabled + firewalld: + service: https + permanent: true + state: enabled + register: result + +- name: assert firewalld service test permanent enabled worked + assert: + that: + - result is changed + +- name: firewalld service test permanent enabled rerun (verify not changed) + firewalld: + service: https + permanent: true + state: enabled + register: result + +- name: assert firewalld service test permanent enabled rerun worked (verify not changed) + assert: + that: + - result is not changed + +- name: firewalld service test permanent disabled + firewalld: + service: https + permanent: true + state: disabled + register: result + +- name: assert firewalld service test permanent disabled worked + assert: + that: + - result is changed + +- name: firewalld service test permanent disabled rerun (verify not changed) + firewalld: + service: https + permanent: true + state: disabled + register: result + +- name: assert firewalld service test permanent disabled rerun worked (verify not changed) + assert: + that: + - result is not changed diff --git a/tests/integration/targets/firewalld/tasks/source_test_cases.yml b/tests/integration/targets/firewalld/tasks/source_test_cases.yml new file mode 100644 index 0000000..f7c4f00 --- /dev/null +++ b/tests/integration/targets/firewalld/tasks/source_test_cases.yml @@ -0,0 +1,85 @@ +# Test playbook for the firewalld module - source operations +# (c) 2019, Hideki Saito + +# 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 source test permanent enabled + firewalld: + source: 192.0.2.0/24 + zone: internal + permanent: True + state: enabled + register: result + +- name: assert firewalld source test permanent enabled worked + assert: + that: + - result is changed + +- name: firewalld source test permanent enabled rerun (verify not changed) + firewalld: + source: 192.0.2.0/24 + zone: internal + permanent: True + state: enabled + register: result + +- name: assert firewalld source test permanent enabled rerun worked (verify not changed) + assert: + that: + - result is not changed + +- name: firewalld source test permanent disabled + firewalld: + source: 192.0.2.0/24 + zone: internal + permanent: True + state: disabled + register: result + +- name: assert firewalld source test permanent disabled worked + assert: + that: + - result is changed + +- name: firewalld source test permanent disabled rerun (verify not changed) + firewalld: + source: 192.0.2.0/24 + zone: internal + permanent: True + state: disabled + register: result + +- name: assert firewalld source test permanent disabled rerun worked (verify not changed) + assert: + that: + - result is not changed + +- name: firewalld source test permanent enabled is exclusive (verify exclusive error) + firewalld: + source: 192.0.2.0/24 + port: 8081/tcp + zone: internal + permanent: True + state: enabled + register: result + ignore_errors: true + +- name: assert firewalld source test permanent enabled is exclusive (verify exclusive error) + assert: + that: + - result is not changed + - "result.msg == 'can only operate on port, service, rich_rule, masquerade, icmp_block, icmp_block_inversion, interface or source at once'" diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index 75dc74e..f494948 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -1,3 +1,5 @@ +plugins/module_utils/firewalld.py future-import-boilerplate +plugins/module_utils/firewalld.py metaclass-boilerplate plugins/module_utils/mount.py future-import-boilerplate plugins/module_utils/mount.py metaclass-boilerplate plugins/modules/acl.py validate-modules:parameter-type-not-in-doc diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index 1308768..eba078e 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -1,3 +1,5 @@ +plugins/module_utils/firewalld.py future-import-boilerplate +plugins/module_utils/firewalld.py metaclass-boilerplate plugins/module_utils/mount.py future-import-boilerplate plugins/module_utils/mount.py metaclass-boilerplate plugins/modules/acl.py validate-modules:parameter-type-not-in-doc From e82c3907bb9bf7efc59031ba997bd672760a4347 Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Tue, 7 Jul 2020 09:42:37 -0500 Subject: [PATCH 2/6] add future boilerplate imports Signed-off-by: Adam Miller --- plugins/module_utils/firewalld.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/module_utils/firewalld.py b/plugins/module_utils/firewalld.py index b44e031..1201461 100644 --- a/plugins/module_utils/firewalld.py +++ b/plugins/module_utils/firewalld.py @@ -4,6 +4,7 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # Imports and info for sanity checking +from __future__ import (absolute_import, division, print_function) from distutils.version import LooseVersion FW_VERSION = None From 5dee4b0576b8c1e43632fadb44e87ed9f3021ece Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Tue, 7 Jul 2020 12:54:15 -0500 Subject: [PATCH 3/6] fix community.general -> ansible.posix import Signed-off-by: Adam Miller --- plugins/modules/firewalld.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py index 73666ef..f4f5fc0 100644 --- a/plugins/modules/firewalld.py +++ b/plugins/modules/firewalld.py @@ -171,7 +171,7 @@ EXAMPLES = r''' ''' from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.firewalld import FirewallTransaction, fw_offline +from ansible_collections.ansible.posix.plugins.module_utils.firewalld import FirewallTransaction, fw_offline try: from firewall.client import Rich_Rule From 6f822d08d4d143b239c1b36be34394a328664cfe Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Tue, 7 Jul 2020 15:14:41 -0500 Subject: [PATCH 4/6] Revert "add future boilerplate imports" This reverts commit e82c3907bb9bf7efc59031ba997bd672760a4347. --- plugins/module_utils/firewalld.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/module_utils/firewalld.py b/plugins/module_utils/firewalld.py index 1201461..b44e031 100644 --- a/plugins/module_utils/firewalld.py +++ b/plugins/module_utils/firewalld.py @@ -4,7 +4,6 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # Imports and info for sanity checking -from __future__ import (absolute_import, division, print_function) from distutils.version import LooseVersion FW_VERSION = None From 8c9bb906292354288e70e3121b0f9527e974f464 Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Wed, 15 Jul 2020 16:23:09 -0500 Subject: [PATCH 5/6] update firewalld examples to be FQCN Signed-off-by: Adam Miller --- plugins/modules/firewalld.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/modules/firewalld.py b/plugins/modules/firewalld.py index f4f5fc0..52d244a 100644 --- a/plugins/modules/firewalld.py +++ b/plugins/modules/firewalld.py @@ -101,68 +101,70 @@ author: ''' EXAMPLES = r''' -- firewalld: +- name: permit traffic in default zone for https service + ansible.posix.firewalld: service: https permanent: yes state: enabled -- firewalld: +- name: do not permit traffic in default zone on port 8081/tcp + ansible.posix.firewalld: port: 8081/tcp permanent: yes state: disabled -- firewalld: +- ansible.posix.firewalld: port: 161-162/udp permanent: yes state: enabled -- firewalld: +- ansible.posix.firewalld: zone: dmz service: http permanent: yes state: enabled -- firewalld: +- ansible.posix.firewalld: rich_rule: rule service name="ftp" audit limit value="1/m" accept permanent: yes state: enabled -- firewalld: +- ansible.posix.firewalld: source: 192.0.2.0/24 zone: internal state: enabled -- firewalld: +- ansible.posix.firewalld: zone: trusted interface: eth2 permanent: yes state: enabled -- firewalld: +- ansible.posix.firewalld: masquerade: yes state: enabled permanent: yes zone: dmz -- firewalld: +- ansible.posix.firewalld: zone: custom state: present permanent: yes -- firewalld: +- ansible.posix.firewalld: zone: drop state: enabled permanent: yes icmp_block_inversion: yes -- firewalld: +- ansible.posix.firewalld: zone: drop state: enabled permanent: yes icmp_block: echo-request - name: Redirect port 443 to 8443 with Rich Rule - firewalld: + ansible.posix.firewalld: rich_rule: rule family=ipv4 forward-port port=443 protocol=tcp to-port=8443 zone: public permanent: yes From 04441336fc86b32862177dcab3c007398bf65ef1 Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Wed, 15 Jul 2020 22:57:38 -0500 Subject: [PATCH 6/6] add changelog fragment Signed-off-by: Adam Miller --- changelogs/fragments/firewalld_migration.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/fragments/firewalld_migration.yml diff --git a/changelogs/fragments/firewalld_migration.yml b/changelogs/fragments/firewalld_migration.yml new file mode 100644 index 0000000..3d97e2b --- /dev/null +++ b/changelogs/fragments/firewalld_migration.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - firewalld - add firewalld module to ansible.posix collection