def __init__(self): self.fw = FirewallClient() self.config = self.fw.config() if not self.config: log.warning('FirewallD is not running attempting to start...') import subprocess import time subprocess.check_output( ['systemctl', 'enable', '--now', 'firewalld']) # firewall-cmd synchronously waits for FirewallD startup subprocess.check_output(['firewall-cmd', '--state']) self.fw = FirewallClient() self.config = self.fw.config()
def __init__(self, rules_id, ipv4_public_addr=None, ipv4_private_addr=None): self.fw = FirewallClient() self.config = self.fw.config() self.fw_direct = self.config.direct() self.prio = 0 self.ipv = 'ipv4' self.table = 'filter' self.chain = 'FORWARD' self.rules_id = rules_id self.ipv4_public_addr = ipv4_public_addr self.ipv4_private_addr = ipv4_private_addr self.firewall_in_chain = FIREWALL_IN_NAME + str(rules_id) self.firewall_out_chain = FIREWALL_OUT_NAME + str(rules_id)
def main(): module = AnsibleModule( argument_spec=dict( table=dict(required=False, default='filter'), chain=dict(required=True), priority=dict(required=False, default=0, type='int'), args=dict(required=True), type=dict(choices=['ipv4', 'ipv6', 'eb'], default='ipv4'), permanent=dict(type='bool', required=False, default=None), state=dict(choices=['present', 'absent'], required=True), ), supports_check_mode=True ) if not HAVE_FIREWALLD: module.fail_json(msg='firewalld and the python module are required ' 'for this module') if not FIREWALLD_RUNNING: module.fail_json(msg='firewalld is not running, and offline operations' ' are not supported yet') table = module.params['table'] chain = module.params['chain'] priority = module.params['priority'] args = module.params['args'].split(' ') address_type = module.params['type'] permanent = module.params['permanent'] state = module.params['state'] if permanent: fw = FirewallClient().config().direct() else: fw = FirewallClient() rules = fw.getRules(ipv=address_type, table=table, chain=chain) changed = False if state == 'present': if (priority, args) not in rules: fw.addRule(ipv=address_type, table=table, chain=chain, priority=priority, args=args) changed = True else: if (priority, args) in rules: fw.removeRule(ipv=address_type, table=table, chain=chain, priority=priority, args=args) changed = True result = dict(changed=changed) module.exit_json(**result)
def main(): module = AnsibleModule( argument_spec=dict( name=dict(required=True), state=dict(choices=['present', 'absent'], required=True), no_reload=dict(type='bool', default='no'), ), supports_check_mode=False ) if not HAVE_FIREWALLD: module.fail_json(msg='firewalld and the python module are required ' 'for this module') if not FIREWALLD_RUNNING: module.fail_json(msg='firewalld is not running, and offline operations' ' are not supported yet') name = module.params['name'] state = module.params['state'] no_reload = module.params['no_reload'] fw = FirewallClient().config() zones = fw.getZoneNames() changed = False if state == 'present': if name not in zones: fw.addZone(name, FirewallClientZoneSettings()) changed = True else: if name in zones: z = fw.getZoneByName(name) z.remove() changed = True if changed and not no_reload: FirewallClient().reload() result = dict(changed=changed) module.exit_json(**result)
def __init__(self): try: from firewall.client import FirewallClient self._fw = FirewallClient() if not self._fw.connected: debugprint("FirewallD seems to be installed but not running") self._fw = None self._zone = None self.running = False return zone_name = self._get_active_zone() if zone_name: self._zone = self._fw.config().getZoneByName(zone_name) else: self._zone = None self.running = True debugprint("Using /org/fedoraproject/FirewallD1") except (ImportError, dbus.exceptions.DBusException): self._fw = None self._zone = None self.running = False
def __init__ (self): try: from firewall.client import FirewallClient self._fw = FirewallClient () if not self._fw.connected: debugprint ("FirewallD seems to be installed but not running") self._fw = None self._zone = None self.running = False return zone_name = self._get_active_zone () if zone_name: self._zone = self._fw.config().getZoneByName (zone_name) else: self._zone = None self.running = True debugprint ("Using /org/fedoraproject/FirewallD1") except (ImportError, dbus.exceptions.DBusException): self._fw = None self._zone = None self.running = False
def main(): module = AnsibleModule(argument_spec=dict( name=dict(required=True), state=dict(choices=['present', 'absent'], required=True), no_reload=dict(type='bool', default='no'), ), supports_check_mode=False) if not HAVE_FIREWALLD: module.fail_json(msg='firewalld and the python module are required ' 'for this module') if not FIREWALLD_RUNNING: module.fail_json(msg='firewalld is not running, and offline operations' ' are not supported yet') name = module.params['name'] state = module.params['state'] no_reload = module.params['no_reload'] fw = FirewallClient().config() zones = fw.getZoneNames() changed = False if state == 'present': if name not in zones: fw.addZone(name, FirewallClientZoneSettings()) changed = True else: if name in zones: z = fw.getZoneByName(name) z.remove() changed = True if changed and not no_reload: FirewallClient().reload() result = dict(changed=changed) module.exit_json(**result)
# Imports try: import firewall.config FW_VERSION = firewall.config.VERSION from firewall.client import Rich_Rule from firewall.client import FirewallClient from firewall.client import FirewallClientZoneSettings from firewall.errors import FirewallError fw = None fw_offline = False 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 from firewall.core.fw_test import Firewall_test fw = Firewall_test() fw.start() except ImportError: import_failure = True
def main(): global module ## make module global so we don't have to pass it to action_handler every ## function call global module module = AnsibleModule( argument_spec = dict( service=dict(required=False,default=None), port=dict(required=False,default=None), rich_rule=dict(required=False,default=None), zone=dict(required=False,default=None), 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), timeout=dict(type='int',required=False,default=0), interface=dict(required=False,default=None), masquerade=dict(required=False,default=None), offline=dict(type='bool',required=False,default=None), ), supports_check_mode=True ) ## Handle running (online) daemon vs non-running (offline) daemon global fw global fw_offline global Rich_Rule global FirewallClientZoneSettings ## Imports try: import firewall.config FW_VERSION = firewall.config.VERSION from firewall.client import Rich_Rule from firewall.client import FirewallClient fw = None fw_offline = False try: fw = FirewallClient() fw.getDefaultZone() except AttributeError: ## Firewalld is not currently running, permanent-only operations ## Import other required parts of the firewalld API ## ## 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() fw_offline = True except ImportError: ## Make python 2.4 shippable ci tests happy e = sys.exc_info()[1] module.fail_json(msg='firewalld and its python 2 module are required for this module, version 2.0.11 or newer required (3.0.9 or newer for offline operations) \n %s' % e) if fw_offline: ## Pre-run version checking if FW_VERSION < "0.3.9": module.fail_json(msg='unsupported version of firewalld, offline operations require >= 3.0.9') else: ## Pre-run version checking if FW_VERSION < "0.2.11": module.fail_json(msg='unsupported version of firewalld, requires >= 2.0.11') ## Check for firewalld running try: if fw.connected == 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 >= 2.0.11" % FW_VERSION) ## Verify required params are provided if module.params['source'] == None and module.params['permanent'] == None: module.fail_json(msg='permanent is a required parameter') if module.params['interface'] != None and module.params['zone'] == None: module.fail(msg='zone is a required parameter') if module.params['immediate'] and fw_offline: module.fail(msg='firewall is not currently running, unable to perform immediate actions without a running firewall daemon') ## Global Vars changed=False msgs = [] service = module.params['service'] rich_rule = module.params['rich_rule'] source = module.params['source'] if module.params['port'] != None: port, protocol = module.params['port'].split('/') if protocol == None: module.fail_json(msg='improper port format (missing protocol?)') else: port = None if module.params['zone'] != None: zone = module.params['zone'] else: if fw_offline: zone = fw.get_default_zone() else: zone = fw.getDefaultZone() 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'] modification_count = 0 if service != None: modification_count += 1 if port != None: modification_count += 1 if rich_rule != None: modification_count += 1 if interface != None: modification_count += 1 if masquerade != None: modification_count += 1 if modification_count > 1: module.fail_json(msg='can only operate on port, service, rich_rule or interface at once') if service != None: if immediate and permanent: is_enabled_permanent = action_handler( get_service_enabled_permanent, (zone, service) ) is_enabled_immediate = action_handler( get_service_enabled, (zone, service) ) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler( set_service_enabled_permanent, (zone, service) ) changed=True if not is_enabled_immediate: action_handler( set_service_enabled, (zone, service, timeout) ) changed=True elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler( set_service_disabled_permanent, (zone, service) ) changed=True if is_enabled_immediate: action_handler( set_service_disabled, (zone, service) ) changed=True elif permanent and not immediate: is_enabled = action_handler( get_service_enabled_permanent, (zone, service) ) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler( set_service_enabled_permanent, (zone, service) ) changed=True elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler( set_service_disabled_permanent, (zone, service) ) changed=True elif immediate and not permanent: is_enabled = action_handler( get_service_enabled, (zone, service) ) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler( set_service_enabled, (zone, service, timeout) ) changed=True elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler( set_service_disabled, (zone, service) ) changed=True if changed == True: msgs.append("Changed service %s to %s" % (service, desired_state)) # FIXME - source type does not handle non-permanent mode, this was an # oversight in the past. if source != None: is_enabled = action_handler(get_source, (zone, source)) if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler(add_source, (zone, source)) changed=True msgs.append("Added %s to zone %s" % (source, zone)) elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler(remove_source, (zone, source)) changed=True msgs.append("Removed %s from zone %s" % (source, zone)) if port != None: if immediate and permanent: is_enabled_permanent = action_handler( get_port_enabled_permanent, (zone,[port, protocol]) ) is_enabled_immediate = action_handler( get_port_enabled, (zone, [port, protocol]) ) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler( set_port_enabled_permanent, (zone, port, protocol) ) changed=True if not is_enabled_immediate: action_handler( set_port_enabled, (zone, port, protocol, timeout) ) changed=True elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler( set_port_disabled_permanent, (zone, port, protocol) ) changed=True if is_enabled_immediate: action_handler( set_port_disabled, (zone, port, protocol) ) changed=True elif permanent and not immediate: is_enabled = action_handler( get_port_enabled_permanent, (zone, [port, protocol]) ) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler( set_port_enabled_permanent, (zone, port, protocol) ) changed=True elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler( set_port_disabled_permanent, (zone, port, protocol) ) changed=True if immediate and not permanent: is_enabled = action_handler( get_port_enabled, (zone, [port,protocol]) ) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler( set_port_enabled, (zone, port, protocol, timeout) ) changed=True elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler( set_port_disabled, (zone, port, protocol) ) changed=True if changed == True: msgs.append("Changed port %s to %s" % ("%s/%s" % (port, protocol), \ desired_state)) if rich_rule != None: if immediate and permanent: is_enabled_permanent = action_handler( get_rich_rule_enabled_permanent, (zone, rich_rule) ) is_enabled_immediate = action_handler( get_rich_rule_enabled, (zone, rich_rule) ) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler( set_rich_rule_enabled_permanent, (zone, rich_rule) ) changed=True if not is_enabled_immediate: action_handler( set_rich_rule_enabled, (zone, rich_rule, timeout) ) changed=True elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler( set_rich_rule_disabled_permanent, (zone, rich_rule) ) changed=True if is_enabled_immediate: action_handler( set_rich_rule_disabled, (zone, rich_rule) ) changed=True if permanent and not immediate: is_enabled = action_handler( get_rich_rule_enabled_permanent, (zone, rich_rule) ) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler( set_rich_rule_enabled_permanent, (zone, rich_rule) ) changed=True elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler( set_rich_rule_disabled_permanent, (zone, rich_rule) ) changed=True if immediate and not permanent: is_enabled = action_handler( get_rich_rule_enabled, (zone, rich_rule) ) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler( set_rich_rule_enabled, (zone, rich_rule, timeout) ) changed=True elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler( set_rich_rule_disabled, (zone, rich_rule) ) changed=True if changed == True: msgs.append("Changed rich_rule %s to %s" % (rich_rule, desired_state)) if interface != None: if immediate and permanent: is_enabled_permanent = action_handler( get_interface_permanent, (zone, interface) ) is_enabled_immediate = action_handler( get_interface, (zone, interface) ) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: change_zone_of_interface_permanent(zone, interface) changed=True if not is_enabled_immediate: change_zone_of_interface(zone, interface) changed=True if changed: msgs.append("Changed %s to zone %s" % (interface, zone)) elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: remove_interface_permanent(zone, interface) changed=True if is_enabled_immediate: remove_interface(zone, interface) changed=True if changed: msgs.append("Removed %s from zone %s" % (interface, zone)) elif permanent and not immediate: is_enabled = action_handler( get_interface_permanent, (zone, interface) ) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) change_zone_of_interface_permanent(zone, interface) changed=True msgs.append("Changed %s to zone %s" % (interface, zone)) elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) remove_interface_permanent(zone, interface) changed=True msgs.append("Removed %s from zone %s" % (interface, zone)) elif immediate and not permanent: is_enabled = action_handler( get_interface, (zone, interface) ) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) change_zone_of_interface(zone, interface) changed=True msgs.append("Changed %s to zone %s" % (interface, zone)) elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) remove_interface(zone, interface) changed=True msgs.append("Removed %s from zone %s" % (interface, zone)) if masquerade != None: if immediate and permanent: is_enabled_permanent = action_handler( get_masquerade_enabled_permanent, (zone) ) is_enabled_immediate = action_handler(get_masquerade_enabled, (zone)) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler(set_masquerade_permanent, (zone, True)) changed=True if not is_enabled_immediate: action_handler(set_masquerade_enabled, (zone)) changed=True if changed: msgs.append("Added masquerade to zone %s" % (zone)) elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler(set_masquerade_permanent, (zone, False)) changed=True if is_enabled_immediate: action_handler(set_masquerade_disabled, (zone)) changed=True if changed: msgs.append("Removed masquerade from zone %s" % (zone)) elif permanent and not immediate: is_enabled = action_handler(get_masquerade_enabled_permanent, (zone)) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_permanent, (zone, True)) changed=True msgs.append("Added masquerade to zone %s" % (zone)) elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_permanent, (zone, False)) changed=True msgs.append("Removed masquerade from zone %s" % (zone)) elif immediate and not permanent: is_enabled = action_handler(get_masquerade_enabled, (zone)) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled == False: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_enabled, (zone)) changed=True msgs.append("Added masquerade to zone %s" % (zone)) elif desired_state == "disabled": if is_enabled == True: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_disabled, (zone)) changed=True msgs.append("Removed masquerade from zone %s" % (zone)) if fw_offline: msgs.append("(offline operation: only on-disk configs were altered)") module.exit_json(changed=changed, msg=', '.join(msgs))
def setUp(self): unittest.TestCase.setUp(self) self.fw = FirewallClient()
def getActiveZones(): fw = FirewallClient() zones = fw.getActiveZones() return zones
def addServiceToZone(service, zone): fw = FirewallClient() fw_zone = fw.config().getZoneByName(zone) fw_settings = fw_zone.getSettings() fw_settings.addService(service) fw_zone.update(fw_settings)
class FirewallWrapper: NETWORKBLOCK_IPSET4 = 'networkblock4' NETWORKBLOCK_IPSET6 = 'networkblock6' NETWORKBLOCK_IPSET_BASE_NAME = 'networkblock' def __init__(self): self.fw = FirewallClient() self.config = self.fw.config() if not self.config: log.warning('FirewallD is not running attempting to start...') import subprocess import time subprocess.check_output( ['systemctl', 'enable', '--now', 'firewalld']) # firewall-cmd synchronously waits for FirewallD startup subprocess.check_output(['firewall-cmd', '--state']) self.fw = FirewallClient() self.config = self.fw.config() def get_create_set(self, name, family='inet'): if name in self.config.getIPSetNames(): return self.config.getIPSetByName(name) settings = FirewallClientIPSetSettings() settings.setType('hash:net') settings.setOptions({ 'maxelem': '1000000', 'family': family, 'hashsize': '4096' }) return self.config.addIPSet(name, settings) def get_block_ipset4(self, name=None): if not name: name = FirewallWrapper.NETWORKBLOCK_IPSET_BASE_NAME name = name + '4' if name in self.config.getIPSetNames(): return self.config.getIPSetByName(name) settings = FirewallClientIPSetSettings() settings.setType('hash:net') settings.setOptions({ 'maxelem': '1000000', 'family': 'inet', 'hashsize': '4096' }) return self.config.addIPSet(name, settings) def get_block_ipset6(self, name=None): if not name: name = FirewallWrapper.NETWORKBLOCK_IPSET_BASE_NAME name = name + '6' if name in self.config.getIPSetNames(): return self.config.getIPSetByName(name) settings = FirewallClientIPSetSettings() settings.setType('hash:net') settings.setOptions({ 'maxelem': '1000000', 'family': 'inet6', 'hashsize': '4096' }) return self.config.addIPSet(name, settings) def get_block_ipset_for_ip(self, ip, name=None): if ip.version == 4: return self.get_block_ipset4(name) if ip.version == 6: return self.get_block_ipset6(name) return None @do_maybe_already_enabled def ensure_ipset_entries(self, ipset, entries): return ipset.setEntries(entries) @do_maybe_already_enabled def ensure_entry_in_ipset(self, ipset, entry): return ipset.addEntry(str(entry)) @do_maybe_already_enabled def ensure_entry_not_in_ipset(self, ipset, entry): return ipset.removeEntry(str(entry)) @do_maybe_already_enabled def ensure_block_ipset_in_drop_zone(self, ipset): # ensure that the block ipset is in drop zone: drop_zone = self.config.getZoneByName('drop') # self.config.getIPSetNames return drop_zone.addSource('ipset:{}'.format( ipset.get_property('name'))) @do_maybe_already_enabled def add_service(self, name, zone='public'): self.fw.addService(zone, name) self.fw.runtimeToPermanent() @do_maybe_already_enabled def block_ip(self, ip, ipset_name=None, reload=True): block_ipset = self.get_block_ipset_for_ip(ip, ipset_name) if not block_ipset: # TODO err: unsupported protocol raise Exception('Unsupported protocol') self.ensure_block_ipset_in_drop_zone(block_ipset) log.info('Adding IP address {} to block set {}'.format( ip, block_ipset.get_property('name'))) try: from aggregate6 import aggregate entries = [] for entry in block_ipset.getEntries(): entries.append(str(entry)) entries.append(str(ip)) block_ipset.setEntries(aggregate(entries)) except ImportError: block_ipset.addEntry(str(ip)) if reload: log.info('Reloading FirewallD to apply permanent configuration') self.fw.reload() log.info('Breaking connection with {}'.format(ip)) from subprocess import CalledProcessError, check_output, STDOUT try: check_output(["/sbin/conntrack", "-D", "-s", str(ip)], stderr=STDOUT) except CalledProcessError as e: pass def get_blocked_ips4(self, name=None): block_ipset4 = self.get_block_ipset4(name) return block_ipset4.getEntries() def get_blocked_ips6(self, name=None): block_ipset6 = self.get_block_ipset6(name) return block_ipset6.getEntries() @do_maybe_not_enabled def remove_ipset_from_zone(self, zone, ipset_name): # drop_zone.removeSource('ipset:') zone.removeSource('ipset:{}'.format(ipset_name)) @do_maybe_invalid_ipset def clear_ipset_by_name(self, ipset_name): try: # does not work: ipset.setEntries([]) self.fw.setEntries(ipset_name, []) except dbus.exceptions.DBusException: pass @do_maybe_invalid_ipset def destroy_ipset_by_name(self, name): log.info('Destroying IPSet {}'.format(name)) # firewalld up to this commit # https://github.com/firewalld/firewalld/commit/f5ed30ce71755155493e78c13fd9036be8f70fc4 # does not delete runtime ipsets, so we have to clear them :( # they are not removed from runtime as still reported by ipset -L # although they *are* removed from FirewallD if name not in self.fw.getIPSets(): return ipset = self.config.getIPSetByName(name) if ipset: self.clear_ipset_by_name(name) ipset.remove() def get_blocked_countries(self): blocked_countries = [] all_ipsets = self.fw.getIPSets() from .Countries import Countries countries = Countries() for ipset_name in all_ipsets: if ipset_name.startswith('fds-'): country_code = ipset_name.split('-')[1] if country_code in countries.names_by_code: blocked_countries.append( countries.names_by_code[country_code]) return blocked_countries def update_ipsets(self): need_reload = False all_ipsets = self.fw.getIPSets() from .Countries import Countries countries = Countries() is_tor_blocked = False for ipset_name in all_ipsets: if ipset_name.startswith('fds-tor-'): is_tor_blocked = True elif ipset_name.startswith('fds-'): country_code = ipset_name.split('-')[1] if country_code in countries.names_by_code: country_name = countries.names_by_code[country_code] country = countries.get_by_name(country_name) self.block_country(country, reload=False) need_reload = True if is_tor_blocked: self.block_tor(reload=False) need_reload = True if need_reload: self.fw.reload() return True def reset(self): drop_zone = self.config.getZoneByName('drop') self.remove_ipset_from_zone(drop_zone, self.NETWORKBLOCK_IPSET4) self.destroy_ipset_by_name(self.NETWORKBLOCK_IPSET4) self.remove_ipset_from_zone(drop_zone, self.NETWORKBLOCK_IPSET6) self.destroy_ipset_by_name(self.NETWORKBLOCK_IPSET6) all_ipsets = self.fw.getIPSets() # get any ipsets prefixed with "fds-" for ipset_name in all_ipsets: if ipset_name.startswith('fds-'): self.remove_ipset_from_zone(drop_zone, ipset_name) self.destroy_ipset_by_name(ipset_name) self.fw.reload() def block_tor(self, reload=True): log.info('Blocking Tor exit nodes') w = WebClient() tor4_exits = w.get_tor_exits(family=4) tor6_exits = w.get_tor_exits(family=6) tor4_ipset = self.get_create_set('fds-tor-4') self.ensure_ipset_entries(tor4_ipset, tor4_exits) tor6_ipset = self.get_create_set('fds-tor-6', family='inet6') self.ensure_ipset_entries(tor6_ipset, tor6_exits) self.ensure_block_ipset_in_drop_zone(tor4_ipset) self.ensure_block_ipset_in_drop_zone(tor6_ipset) if reload: log.info('Reloading FirewallD...') self.fw.reload() log.info('Done!') # while cron will do "sync" behavior" def block_country(self, country, reload=True): # print('address/netmask is invalid: %s' % sys.argv[1]) # parse out as a country log.info('Blocking {} {}'.format(country.name, country.getFlag())) # print("\N{grinning face}") # TODO get aggregated zone file, save as cache, # do diff to know which stuff was changed and add/remove blocks # https://docs.python.org/2/library/difflib.html # TODO persist info on which countries were blocked (in the config file) # then sync zones via "fds cron" # TODO conditional get test on getpagespeed.com w = WebClient() country_networks = w.get_country_networks(country=country) ipset = self.get_create_set(country.get_set_name()) self.ensure_ipset_entries(ipset, country_networks) # this is slow. setEntries is a lot faster # for network in tqdm(country_networks, unit='network', # desc='Adding {} networks to IPSet {}'.format(c.getNation(), c.get_set_name())): # log.debug(network) # fw.ensure_entry_in_ipset(ipset=ipset, entry=network) # TODO retry, timeout # this action re-adds all entries entirely # there should be "fds-<country.code>-<family>" ip set self.ensure_block_ipset_in_drop_zone(ipset) if reload: log.info('Reloading FirewallD...') self.fw.reload() log.info('Done!') # while cron will do "sync" behavior" def unblock_country(self, ip_or_country_name): # print('address/netmask is invalid: %s' % sys.argv[1]) # parse out as a country from .Countries import Countries countries = Countries() c = countries.get_by_name(ip_or_country_name) if not c: log.error( '{} does not look like a correct IP or a country name'.format( ip_or_country_name)) return False drop_zone = self.config.getZoneByName('drop') log.info('Unblocking {} {}'.format(c.name, c.getFlag())) self.remove_ipset_from_zone(drop_zone, c.get_set_name()) self.destroy_ipset_by_name(c.get_set_name()) log.info('Reloading FirewallD...') self.fw.reload() log.info('Done!') # while cron will do "sync" behavior" def unblock_ip(self, ip_or_country_name): block_ipset = self.get_block_ipset_for_ip(ip_or_country_name) if not block_ipset: # TODO err: unsupported protocol raise Exception('Unsupported protocol') log.info('Removing {} from block set {}'.format( ip_or_country_name, block_ipset.get_property('name'))) self.ensure_entry_not_in_ipset(block_ipset, ip_or_country_name) log.info('Reloading FirewallD to apply permanent configuration') self.fw.reload()
class FirewallMgr(object): def __init__(self, rules_id, ipv4_public_addr=None, ipv4_private_addr=None): self.fw = FirewallClient() self.config = self.fw.config() self.fw_direct = self.config.direct() self.prio = 0 self.ipv = 'ipv4' self.table = 'filter' self.chain = 'FORWARD' self.rules_id = rules_id self.ipv4_public_addr = ipv4_public_addr self.ipv4_private_addr = ipv4_private_addr self.firewall_in_chain = FIREWALL_IN_NAME + str(rules_id) self.firewall_out_chain = FIREWALL_OUT_NAME + str(rules_id) def attach(self, rules_inbound, rules_outbound): err_msg = None if not self.is_locked(): self.set_state() self.create_firewall() self.create_rule('inbound', rules_inbound) self.create_rule('outbound', rules_outbound) self.save() else: err_msg = 'Firewall closed connection by timeout %ssec.' % FIREWALLD_STATE_TIMEOUT return err_msg def detach(self): err_msg = None if not self.is_locked(): self.set_state() self.remove_firewall() self.save() else: err_msg = 'Firewall closed connection by timeout %ssec.' % FIREWALLD_STATE_TIMEOUT return err_msg def add_rule(self, rules_inbound, rules_outbound): err_msg = None if not self.is_locked(): self.set_state() if rules_inbound: self.create_rule('inbound', rules_inbound) if rules_outbound: self.create_rule('outbound', rules_outbound) self.save() else: err_msg = 'Firewall closed connection by timeout %ssec.' % FIREWALLD_STATE_TIMEOUT return err_msg def delete_rule(self, rules_inbound, rules_outbound): err_msg = None if not self.is_locked(): self.set_state() if rules_inbound: self.remove_rule('inbound', rules_inbound) if rules_outbound: self.remove_rule('outbound', rules_outbound) self.save() else: err_msg = 'Firewall closed connection by timeout %ssec.' % FIREWALLD_STATE_TIMEOUT return err_msg def set_state(self): f = open(FIREWALLD_STATE_FILE, "w") f.write('1') f.close() def unset_state(self): f = open(FIREWALLD_STATE_FILE, "w") f.write('0') f.close() def read_state(self): if os.path.isfile(FIREWALLD_STATE_FILE): f = open(FIREWALLD_STATE_FILE, "r") f_data = f.read() f.close() if f_data: state = bool(f_data) else: self.unset_state() state = False return state else: return False def is_locked(self): if self.read_state(): seconds = 0 while self.read_state(): seconds += 1 time.sleep(1) if seconds >= FIREWALLD_STATE_TIMEOUT: return True return False def save(self): self.fw_direct.update(self.fw_direct.getSettings()) self.unset_state() def query_rule(self, args): chain = self.chain + FIREWALL_CHAIN_PREFIX ipt_cmd = 'iptables -t {0} -C {1} {2}'.format(self.table, chain, ' '.join(args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 1: return False return True def query_rule_cfg(self, args): check = self.fw_direct.queryRule(self.ipv, self.table, self.chain, self.prio, args) return check def query_chain(self, chain): ipt_cmd = 'iptables -t {0} -L {1}'.format(self.table, chain) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 1: return False return True def query_chain_cfg(self, chain): check = self.fw_direct.queryChain(self.ipv, self.table, chain) return check def query_chain_rule(self, chain, args): ipt_cmd = 'iptables -t {0} -C {1} {2}'.format(self.table, chain, ' '.join(args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 1: return False return True def query_chain_rule_cfg(self, chain, args): check = self.fw_direct.queryRule(self.ipv, self.table, chain, self.prio, args) return check def rule_args(self, chain, rule): src_dst = '-d' port = rule.get('port') action = rule.get('action') address = rule.get('address') protocol = rule.get('protocol') if chain == 'inbound': src_dst = '-s' if action == 'DROP': # Dirty hack for icmp request if protocol == 'icmp': args = ['-p', 'icmp', '-m', 'conntrack', '--ctstate', 'NEW', '-j', action] # Dirty hack for TCP dynamic socket ports if protocol == 'tcp': args = ['-p', 'tcp', '-m', 'conntrack', '--ctstate', 'NEW', '-j', action] # Dirty hack for UDP dynamic socket ports if protocol == 'udp': args = ['-p', 'udp', '-m', 'conntrack', '--ctstate', 'NEW', '-j', action] # Block all type of protocols if not protocol: args = ['-m', 'conntrack', '--ctstate', 'NEW', '-j', action] if action == 'ACCEPT': if protocol == 'icmp': args = ['-p', protocol, src_dst, address, '-j', action] if protocol == 'tcp' or protocol == 'udp': if port: args = ['-p', protocol, src_dst, address, '--dport', str(port), '-j', action] else: args = ['-p', protocol, src_dst, address, '-j', action] # Allow all traffic from address if not protocol and not port: args = [src_dst, address, '-j', action] return args def create_firewall(self): ipv4_addrs = [] # IPv4 Public if self.ipv4_public_addr: ipv4_addrs.append(self.ipv4_public_addr) # IPv4 Private if self.ipv4_private_addr: ipv4_addrs.append(self.ipv4_private_addr) for ipaddr in ipv4_addrs: in_args = ['-d', ipaddr, '-j', self.firewall_in_chain] out_args = ['-s', ipaddr, '-j', self.firewall_out_chain] # Create firewall IN chain if not self.query_chain(self.firewall_in_chain): ipt_cmd = 'iptables -N {0}'.format(self.firewall_in_chain) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if not self.query_chain_cfg(self.firewall_in_chain): self.fw_direct.addChain(self.ipv, self.table, self.firewall_in_chain) # Create firewall IN rule for instance if not self.query_rule(in_args): chain = self.chain + FIREWALL_CHAIN_PREFIX ipt_cmd = 'iptables -t {0} -I {1} {2} {3}'.format(self.table, chain, FIREWALL_INSERT_LINE, ' '.join(in_args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if not self.query_rule_cfg(in_args): self.fw_direct.addRule(self.ipv, self.table, self.chain, self.prio, in_args) # Create firewall OUT chain if not self.query_chain(self.firewall_out_chain): ipt_cmd = 'iptables -N {0}'.format(self.firewall_out_chain) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if not self.query_chain_cfg(self.firewall_out_chain): self.fw_direct.addChain(self.ipv, self.table, self.firewall_out_chain) # Create firewall OUT rule for instance if not self.query_rule(out_args): chain = self.chain + FIREWALL_CHAIN_PREFIX ipt_cmd = 'iptables -t {0} -I {1} {2} {3}'.format(self.table, chain, FIREWALL_INSERT_LINE, ' '.join(out_args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if not self.query_rule_cfg(out_args): self.fw_direct.addRule(self.ipv, self.table, self.chain, self.prio, out_args) def remove_firewall(self): ipv4_addrs = [] # IPv4 Public if self.ipv4_public_addr: ipv4_addrs.append(self.ipv4_public_addr) # IPv4 Private if self.ipv4_private_addr: ipv4_addrs.append(self.ipv4_private_addr) for ipaddr in ipv4_addrs: in_args = ['-d', ipaddr, '-j', self.firewall_in_chain] out_args = ['-s', ipaddr, '-j', self.firewall_out_chain] # Remove firewall IN rule for instance if self.query_rule(in_args): chain = self.chain + FIREWALL_CHAIN_PREFIX ipt_cmd = 'iptables -t {0} -D {1} {2}'.format(self.table, chain, ' '.join(in_args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if self.query_rule_cfg(in_args): self.fw_direct.removeRule(self.ipv, self.table, self.chain, self.prio, in_args) # Remove firewall OUT rule for instance if self.query_rule(out_args): chain = self.chain + FIREWALL_CHAIN_PREFIX ipt_cmd = 'iptables -t {0} -D {1} {2}'.format(self.table, chain, ' '.join(out_args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if self.query_rule_cfg(out_args): self.fw_direct.removeRule(self.ipv, self.table, self.chain, self.prio, out_args) # Check unused firewall rule used_firewall_rules = False for rule in self.fw_direct.getAllRules(): if self.chain in rule and self.firewall_in_chain in rule[4][3]: used_firewall_rules = True # If firewall doesn't have any rules with instances than remove sgfirewallroup rules and chains if not used_firewall_rules: for rule in self.fw_direct.getAllRules(): if self.firewall_in_chain in rule: ipt_cmd = 'iptables -t {0} -D {1} {2}'.format(self.table, self.firewall_in_chain, ' '.join(rule[4])) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: self.fw_direct.removeRule(rule[0], rule[1], rule[2], rule[3], rule[4]) if self.firewall_out_chain in rule: ipt_cmd = 'iptables -t {0} -D {1} {2}'.format(self.table, self.firewall_out_chain, ' '.join(rule[4])) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: self.fw_direct.removeRule(rule[0], rule[1], rule[2], rule[3], rule[4]) for chain in self.fw_direct.getAllChains(): if self.firewall_in_chain in chain: ipt_cmd = 'iptables -X {0}'.format(self.firewall_in_chain) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: self.fw_direct.removeChain(chain[0], chain[1], chain[2]) if self.firewall_out_chain in chain: ipt_cmd = 'iptables -X {0}'.format(self.firewall_out_chain) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: self.fw_direct.removeChain(chain[0], chain[1], chain[2]) def create_rule(self, chain, rules): if chain == 'inbound': firewall_chain = self.firewall_in_chain else: firewall_chain = self.firewall_out_chain for rule in rules: args = self.rule_args(chain, rule) if not self.query_chain_rule(firewall_chain, args): ipt_cmd = 'iptables -t {0} -I {1} {2}'.format(self.table, firewall_chain, ' '.join(args)) if 'DROP' in args: ipt_cmd = 'iptables -t {0} -A {1} {2}'.format(self.table, firewall_chain, ' '.join(args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if 'DROP' in args: self.prio = 1 else: self.prio = 0 if not self.query_chain_rule_cfg(firewall_chain, args): self.fw_direct.addRule(self.ipv, self.table, firewall_chain, self.prio, args) def remove_rule(self, chain, rules): if chain == 'inbound': firewall_chain = self.firewall_in_chain else: firewall_chain = self.firewall_out_chain for rule in rules: args = self.rule_args(chain, rule) if self.query_chain_rule(firewall_chain, args): ipt_cmd = 'iptables -t {0} -D {1} {2}'.format(self.table, firewall_chain, ' '.join(args)) run_ipt_cmd = call(ipt_cmd.split(), stdout=DEVNULL, stderr=STDOUT) if run_ipt_cmd == 0: if 'DROP' in args: self.prio = 1 else: self.prio = 0 if self.query_chain_rule_cfg(firewall_chain, args): self.fw_direct.removeRule(self.ipv, self.table, firewall_chain, self.prio, args)
def main(): module = AnsibleModule(argument_spec=dict( service=dict(required=False, type='list', default=[]), port=dict(required=False, type='list', default=[]), trust=dict(required=False, type='list', default=[]), trust_by_mac=dict(required=False, type='list', default=[]), masq=dict(required=False, type='list', default=[]), masq_by_mac=dict(required=False, type='list', default=[]), forward_port=dict(required=False, type='list', default=[]), forward_port_by_mac=dict(required=False, type='list', default=[]), state=dict(choices=['enabled', 'disabled'], required=True), ), required_one_of=([ 'service', 'port', 'trust', 'trust_by_mac', 'masq', 'masq_by_mac', 'forward_prot' ], ), supports_check_mode=True) if not HAS_FIREWALLD and not HAS_SYSTEM_CONFIG_FIREWALL: module.fail_json(msg='No firewall backend could be imported.') service = module.params['service'] port = [] for port_proto in module.params['port']: _port, _protocol = port_proto.split('/') if _protocol is None: module.fail_json(msg='improper port format (missing protocol?)') port.append((_port, _protocol)) trust = module.params['trust'] trust_by_mac = [] for item in module.params['trust_by_mac']: _interface = get_device_for_mac(item) if _interface is None: module.fail_json(msg='MAC address not found %s' % item) trust_by_mac.append(_interface) masq = module.params['masq'] masq_by_mac = [] for item in module.params['masq_by_mac']: _interface = get_device_for_mac(item) if _interface is None: module.fail_json(msg='MAC address not found %s' % item) masq_by_mac.append(_interface) forward_port = [] for item in module.params['forward_port']: args = item.split(";") if len(args) != 4: module.fail_json(msg='improper forward_port format: %s' % item) _interface, __port, _to_port, _to_addr = args _port, _protocol = __port.split('/') if _protocol is None: module.fail_json(msg='improper port format (missing protocol?)') if _to_port == "": _to_port = None if _to_addr == "": _to_addr = None forward_port.append((_interface, _port, _protocol, _to_port, _to_addr)) forward_port_by_mac = [] for item in module.params['forward_port_by_mac']: args = item.split(";") if len(args) != 4: module.fail_json(msg='improper forward_port_by_mac format') _mac_addr, __port, _to_port, _to_addr = args _port, _protocol = __port.split('/') if _protocol is None: module.fail_json(msg='improper port format (missing protocol?)') if _to_port == "": _to_port = None if _to_addr == "": _to_addr = None _interface = get_device_for_mac(_mac_addr) if _interface is None: module.fail_json(msg='MAC address not found %s' % _mac_addr) forward_port_by_mac.append( (_interface, _port, _protocol, _to_port, _to_addr)) desired_state = module.params['state'] if HAS_FIREWALLD: fw = FirewallClient() def exception_handler(exception_message): module.fail_json(msg=exception_message) fw.setExceptionHandler(exception_handler) if not fw.connected: module.fail_json(msg='firewalld service must be running') trusted_zone = "trusted" external_zone = "external" default_zone = fw.getDefaultZone() fw_zone = fw.config().getZoneByName(default_zone) fw_settings = fw_zone.getSettings() changed = False changed_zones = {} # service for item in service: if desired_state == "enabled": if not fw.queryService(default_zone, item): fw.addService(default_zone, item) changed = True if not fw_settings.queryService(item): fw_settings.addService(item) changed = True changed_zones[fw_zone] = fw_settings elif desired_state == "disabled": if fw.queryService(default_zone, item): fw.removeService(default_zone, item) if fw_settings.queryService(item): fw_settings.removeService(item) changed = True changed_zones[fw_zone] = fw_settings # port for _port, _protocol in port: if desired_state == "enabled": if not fw.queryPort(default_zone, _port, _protocol): fw.addPort(default_zone, _port, _protocol) changed = True if not fw_settings.queryPort(_port, _protocol): fw_settings.addPort(_port, _protocol) changed = True changed_zones[fw_zone] = fw_settings elif desired_state == "disabled": if fw.queryPort(default_zone, _port, _protocol): fw.removePort(default_zone, _port, _protocol) changed = True if fw_settings.queryPort(_port, _protocol): fw_settings.removePort(_port, _protocol) changed = True changed_zones[fw_zone] = fw_settings # trust, trust_by_mac if len(trust) > 0 or len(trust_by_mac) > 0: items = trust if len(trust_by_mac) > 0: items.extend(trust_by_mac) if default_zone != trusted_zone: fw_zone = fw.config().getZoneByName(trusted_zone) fw_settings = fw_zone.getSettings() for item in items: if desired_state == "enabled": if try_set_zone_of_interface(trusted_zone, item): changed = True else: if not fw.queryInterface(trusted_zone, item): fw.changeZoneOfInterface(trusted_zone, item) changed = True if not fw_settings.queryInterface(item): fw_settings.addInterface(item) changed = True changed_zones[fw_zone] = fw_settings elif desired_state == "disabled": if try_set_zone_of_interface("", item): if module.check_mode: module.exit_json(changed=True) else: if fw.queryInterface(trusted_zone, item): fw.removeInterface(trusted_zone, item) changed = True if fw_settings.queryInterface(item): fw_settings.removeInterface(item) changed = True changed_zones[fw_zone] = fw_settings # masq, masq_by_mac if len(masq) > 0 or len(masq_by_mac) > 0: items = masq if len(masq_by_mac) > 0: items.extend(masq_by_mac) if default_zone != external_zone: fw_zone = fw.config().getZoneByName(external_zone) fw_settings = fw_zone.getSettings() for item in items: if desired_state == "enabled": if try_set_zone_of_interface(external_zone, item): changed = True else: if not fw.queryInterface(external_zone, item): fw.changeZoneOfInterface(external_zone, item) changed = True if not fw_settings.queryInterface(item): fw_settings.addInterface(item) changed = True changed_zones[fw_zone] = fw_settings elif desired_state == "disabled": if try_set_zone_of_interface("", item): if module.check_mode: module.exit_json(changed=True) else: if fw.queryInterface(external_zone, item): fw.removeInterface(external_zone, item) changed = True if fw_settings.queryInterface(item): fw_settings.removeInterface(item) changed = True changed_zones[fw_zone] = fw_settings # forward_port, forward_port_by_mac if len(forward_port) > 0 or len(forward_port_by_mac) > 0: items = forward_port if len(forward_port_by_mac) > 0: items.extend(forward_port_by_mac) for _interface, _port, _protocol, _to_port, _to_addr in items: if _interface != "": _zone = fw.getZoneOfInterface(_interface) if _zone != "" and _zone != default_zone: fw_zone = fw.config().getZoneByName(_zone) fw_settings = fw_zone.getSettings() if desired_state == "enabled": if not fw.queryForwardPort(_zone, _port, _protocol, _to_port, _to_addr): fw.addForwardPort(_zone, _port, _protocol, _to_port, _to_addr) changed = True if not fw_settings.queryForwardPort( _port, _protocol, _to_port, _to_addr): fw_settings.addForwardPort(_port, _protocol, _to_port, _to_addr) changed = True changed_zones[fw_zone] = fw_settings elif desired_state == "disabled": if fw.queryForwardPort(_zone, _port, _protocol, _to_port, _to_addr): fw.removeForwardPort(_zone, _port, _protocol, _to_port, _to_addr) changed = True if fw_settings.queryForwardPort(_port, _protocol, _to_port, _to_addr): fw_settings.removeForwardPort(_port, _protocol, _to_port, _to_addr) changed = True changed_zones[fw_zone] = fw_settings # apply changes if changed: for _zone in changed_zones: _zone.update(changed_zones[_zone]) if module.check_mode: module.exit_json(changed=True) elif HAS_SYSTEM_CONFIG_FIREWALL: (config, old_config, _) = fw_lokkit.loadConfig(args=[], dbus_parser=True) changed = False # service for item in service: if config.services is None: config.services = [] if desired_state == "enabled": if item not in config.services: config.services.append(item) changed = True elif desired_state == "disabled": if item in config.services: config.services.remove(item) changed = True # port for _port, _protocol in port: if config.ports is None: config.ports = [] _range = getPortRange(_port) if _range < 0: module.fail_json(msg='invalid port definition %s' % _port) elif _range is None: module.fail_json(msg='port _range is not unique.') elif len(_range) == 2 and _range[0] >= _range[1]: module.fail_json(msg='invalid port range %s' % _port) port_proto = (_range, _protocol) if desired_state == "enabled": if port_proto not in config.ports: config.ports.append(port_proto) changed = True elif desired_state == "disabled": if port_proto in config.ports: config.ports.remove(port_proto) changed = True # trust, trust_by_mac if len(trust) > 0 or len(trust_by_mac) > 0: if config.trust is None: config.trust = [] items = trust if len(trust_by_mac) > 0: items.extend(trust_by_mac) for item in items: if desired_state == "enabled": if item not in config.trust: config.trust.append(item) changed = True elif desired_state == "disabled": if item in config.trust: config.trust.remove(item) changed = True # masq, masq_by_mac if len(masq) > 0 or len(masq_by_mac) > 0: if config.masq is None: config.masq = [] items = masq if len(masq_by_mac) > 0: items.extend(masq_by_mac) for item in items: if desired_state == "enabled": if item not in config.masq: config.masq.append(item) changed = True elif desired_state == "disabled": if item in config.masq: config.masq.remove(item) changed = True # forward_port, forward_port_by_mac if len(forward_port) > 0 or len(forward_port_by_mac) > 0: if config.forward_port is None: config.forward_port = [] items = forward_port if len(forward_port_by_mac) > 0: items.extend(forward_port_by_mac) for _interface, _port, _protocol, _to_port, _to_addr in items: _range = getPortRange(_port) if _range < 0: module.fail_json(msg='invalid port definition') elif _range is None: module.fail_json(msg='port _range is not unique.') elif len(_range) == 2 and _range[0] >= _range[1]: module.fail_json(msg='invalid port range') fwd_port = { "if": _interface, "port": _range, "proto": _protocol } if _to_port is not None: _range = getPortRange(_to_port) if _range < 0: module.fail_json(msg='invalid port definition %s' % \ _to_port) elif _range is None: module.fail_json(msg='port _range is not unique.') elif len(_range) == 2 and _range[0] >= _range[1]: module.fail_json(msg='invalid port range') fwd_port["toport"] = _range if _to_addr is not None: fwd_port["toaddr"] = _to_addr if desired_state == "enabled": if fwd_port not in config.forward_port: config.forward_port.append(fwd_port) changed = True elif desired_state == "disabled": if fwd_port in config.forward_port: config.forward_port.remove(fwd_port) changed = True # apply changes if changed: fw_lokkit.updateFirewall(config, old_config) if module.check_mode: module.exit_json(changed=True) else: module.fail_json(msg='No firewalld and system-config-firewall') module.exit_json(changed=False)
class TestFirewallDInterfaceConfig(unittest.TestCase): """ For testing of permanent changes, ie. those that survive restart: """ def setUp(self): unittest.TestCase.setUp(self) self.fw = FirewallClient() def tearDown(self): unittest.TestCase.tearDown(self) def test_zones(self): """ /org/fedoraproject/FirewallD1/config listZones() getZoneByName(String name) addZone(String name, Dict of {String, Variant} zone_settings) /org/fedoraproject/FirewallD1/config/zone/<id> getSettings() loadDefaults() update() rename() remove() """ print("\nGetting invalid zone") self.assertRaisesRegexp(Exception, 'INVALID_ZONE', self.fw.config().getZoneByName, "dummyname") zone_version = "1.0" zone_short = "Testing" zone_description = "this is just a testing zone" zone_target = DEFAULT_ZONE_TARGET zone_services = ["dhcpv6-client", "ssh"] zone_ports = [("123", "tcp"), ("666-667", "udp")] zone_icmpblocks = ["redirect", "echo-reply"] zone_masquerade = False zone_forward_ports = [("443", "tcp", "441", "192.168.0.2"), ("123", "udp", "321", "192.168.1.1")] settings = FirewallClientZoneSettings() settings.setVersion(zone_version) settings.setShort(zone_short) settings.setDescription(zone_description) settings.setTarget(zone_target) settings.setServices(zone_services) settings.setPorts(zone_ports) settings.setIcmpBlocks(zone_icmpblocks) settings.setMasquerade(zone_masquerade) settings.setForwardPorts(zone_forward_ports) print("Adding zone with name that already exists") self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', self.fw.config().addZone, "home", settings) print("Adding zone with empty name") self.assertRaisesRegexp(Exception, 'INVALID_NAME', self.fw.config().addZone, "", settings) zone_name = "test" print("Adding proper zone") self.fw.config().addZone(zone_name, settings) print("Checking the saved (permanent) settings") config_zone = self.fw.config().getZoneByName(zone_name) self.assertIsInstance(config_zone, firewall.client.FirewallClientConfigZone) zone_settings = config_zone.getSettings() self.assertIsInstance(zone_settings, firewall.client.FirewallClientZoneSettings) self.assertEquals(zone_settings.getVersion(), zone_version) self.assertEquals(zone_settings.getShort(), zone_short) self.assertEquals(zone_settings.getDescription(), zone_description) self.assertEquals(zone_settings.getTarget(), "default") self.assertEquals(zone_settings.getServices().sort(), zone_services.sort()) self.assertEquals(zone_settings.getPorts().sort(), zone_ports.sort()) self.assertEquals(zone_settings.getIcmpBlocks().sort(), zone_icmpblocks.sort()) self.assertEquals(zone_settings.getMasquerade(), zone_masquerade) self.assertEquals(zone_settings.getForwardPorts().sort(), zone_forward_ports.sort()) print("Updating settings") zone_services.append("mdns") zone_settings.setServices(zone_services) config_zone.update(zone_settings) print("Reloading firewalld") self.fw.reload() print("Checking of runtime settings") self.assertTrue(zone_name in self.fw.getZones()) self.assertEquals( self.fw.getServices(zone_name).sort(), zone_services.sort()) self.assertEquals( self.fw.getPorts(zone_name).sort(), zone_ports.sort()) self.assertEquals( self.fw.getIcmpBlocks(zone_name).sort(), zone_icmpblocks.sort()) self.assertEquals(self.fw.queryMasquerade(zone_name), zone_masquerade) self.assertEquals( self.fw.getForwardPorts(zone_name).sort(), zone_forward_ports.sort()) print("Renaming zone to name that already exists") config_zone = self.fw.config().getZoneByName(zone_name) self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', config_zone.rename, "home") new_zone_name = "renamed" print("Renaming zone '%s' to '%s'" % (zone_name, new_zone_name)) config_zone.rename(new_zone_name) print( "Checking whether the zone '%s' is accessible (it shouldn't be)" % zone_name) self.assertRaisesRegexp(Exception, 'INVALID_ZONE', self.fw.config().getZoneByName, zone_name) print("Checking whether the zone '%s' is accessible" % new_zone_name) config_zone = self.fw.config().getZoneByName(new_zone_name) zone_settings = config_zone.getSettings() self.assertEquals(zone_settings.getVersion(), zone_version) self.assertEquals(zone_settings.getShort(), zone_short) self.assertEquals(zone_settings.getDescription(), zone_description) self.assertEquals(zone_settings.getTarget(), "default") self.assertEquals(zone_settings.getServices().sort(), zone_services.sort()) self.assertEquals(zone_settings.getPorts().sort(), zone_ports.sort()) self.assertEquals(zone_settings.getIcmpBlocks().sort(), zone_icmpblocks.sort()) self.assertEquals(zone_settings.getMasquerade(), zone_masquerade) self.assertEquals(zone_settings.getForwardPorts().sort(), zone_forward_ports.sort()) print("Removing the zone '%s'" % new_zone_name) config_zone.remove() print( "Checking whether the removed zone is accessible (it shouldn't be)" ) self.assertRaisesRegexp(Exception, 'INVALID_ZONE', self.fw.config().getZoneByName, new_zone_name) # TODO test loadDefaults() ? def test_services(self): """ /org/fedoraproject/FirewallD1/config listServices() getServiceByName(String name) addService(String name, Dict of {String, Variant} settings) /org/fedoraproject/FirewallD1/config/service/<id> getSettings() loadDefaults() update() rename() remove() """ print("\nGetting invalid service") self.assertRaisesRegexp(Exception, 'INVALID_SERVICE', self.fw.config().getServiceByName, "dummyname") service_version = "1.0" service_short = "Testing" service_description = "this is just a testing service" service_ports = [("123", "tcp"), ("666-667", "udp")] service_modules = ["nf_conntrack_tftp"] service_destinations = {'ipv4': '1.2.3.4', 'ipv6': 'dead::beef'} settings = FirewallClientServiceSettings() # ["", "", "", [], [], {}] settings.setVersion(service_version) settings.setShort(service_short) settings.setDescription(service_description) settings.setPorts(service_ports) settings.setModules(service_modules) settings.setDestinations(service_destinations) print("Adding service with name that already exists") self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', self.fw.config().addService, "mdns", settings) print("Adding service with empty name") self.assertRaisesRegexp(Exception, 'INVALID_NAME', self.fw.config().addService, "", settings) service_name = "test" print("Adding proper service") self.fw.config().addService(service_name, settings) print("Checking the saved (permanent) settings") config_service = self.fw.config().getServiceByName(service_name) self.assertIsInstance(config_service, firewall.client.FirewallClientConfigService) service_settings = config_service.getSettings() self.assertIsInstance(service_settings, firewall.client.FirewallClientServiceSettings) print("Updating settings") service_modules.append("nf_conntrack_sip") service_destinations["ipv6"] = "3ffe:501:ffff::" service_settings.setModules(service_modules) service_settings.setDestinations(service_destinations) config_service.update(service_settings) self.assertEquals(service_settings.getVersion(), service_version) self.assertEquals(service_settings.getShort(), service_short) self.assertEquals(service_settings.getDescription(), service_description) self.assertEquals(service_settings.getPorts().sort(), service_ports.sort()) self.assertEquals(service_settings.getModules().sort(), service_modules.sort()) self.assertDictEqual(service_settings.getDestinations(), service_destinations) print("Renaming service to name that already exists") config_service = self.fw.config().getServiceByName(service_name) self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', config_service.rename, "mdns") new_service_name = "renamed" print("Renaming service '%s' to '%s'" % (service_name, new_service_name)) config_service.rename(new_service_name) print( "Checking whether the service '%s' is accessible (it shouldn't be)" % service_name) self.assertRaisesRegexp(Exception, 'INVALID_SERVICE', self.fw.config().getServiceByName, service_name) print("Checking whether the service '%s' is accessible" % new_service_name) config_service = self.fw.config().getServiceByName(new_service_name) service_settings = config_service.getSettings() self.assertEquals(service_settings.getVersion(), service_version) self.assertEquals(service_settings.getShort(), service_short) self.assertEquals(service_settings.getDescription(), service_description) self.assertEquals(service_settings.getPorts().sort(), service_ports.sort()) self.assertEquals(service_settings.getModules().sort(), service_modules.sort()) self.assertDictEqual(service_settings.getDestinations(), service_destinations) print("Removing the service '%s'" % new_service_name) config_service.remove() print( "Checking whether the removed service is accessible (it shouldn't be)" ) self.assertRaisesRegexp(Exception, 'INVALID_SERVICE', self.fw.config().getServiceByName, new_service_name) # TODO test loadDefaults() ? def test_icmptypes(self): """ /org/fedoraproject/FirewallD1/config listIcmpTypes() getIcmpTypeByName(String name) addIcmpType(String name, Dict of {String, Variant} settings) /org/fedoraproject/FirewallD1/config/icmptype/<id> getSettings() loadDefaults() update() rename() remove() """ print("\nGetting invalid icmp-type") self.assertRaisesRegexp(Exception, 'INVALID_ICMPTYPE', self.fw.config().getIcmpTypeByName, "dummyname") icmptype_version = "1.0" icmptype_short = "Testing" icmptype_description = "this is just a testing icmp type" icmptype_destinations = ['ipv4'] settings = FirewallClientIcmpTypeSettings() # ["", "", "", []] settings.setVersion(icmptype_version) settings.setShort(icmptype_short) settings.setDescription(icmptype_description) settings.setDestinations(icmptype_destinations) print("Adding icmp type with name that already exists") self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', self.fw.config().addIcmpType, "echo-reply", settings) print("Adding icmp type with empty name") self.assertRaisesRegexp(Exception, 'INVALID_NAME', self.fw.config().addIcmpType, "", settings) icmptype_name = "test" print("Adding proper icmp type") self.fw.config().addIcmpType(icmptype_name, settings) print("Checking the saved (permanent) settings") config_icmptype = self.fw.config().getIcmpTypeByName(icmptype_name) self.assertIsInstance(config_icmptype, firewall.client.FirewallClientConfigIcmpType) icmptype_settings = config_icmptype.getSettings() self.assertIsInstance(icmptype_settings, firewall.client.FirewallClientIcmpTypeSettings) print("Updating settings") icmptype_destinations.append("ipv6") icmptype_settings.setDestinations(icmptype_destinations) config_icmptype.update(icmptype_settings) self.assertEquals(icmptype_settings.getVersion(), icmptype_version) self.assertEquals(icmptype_settings.getShort(), icmptype_short) self.assertEquals(icmptype_settings.getDescription(), icmptype_description) self.assertEquals(icmptype_settings.getDestinations().sort(), icmptype_destinations.sort()) print("Renaming icmp type to name that already exists") config_icmptype = self.fw.config().getIcmpTypeByName(icmptype_name) self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', config_icmptype.rename, "echo-reply") new_icmptype_name = "renamed" print("Renaming icmp type '%s' to '%s'" % (icmptype_name, new_icmptype_name)) config_icmptype.rename(new_icmptype_name) print( "Checking whether the icmp type '%s' is accessible (it shouldn't be)" % icmptype_name) self.assertRaisesRegexp(Exception, 'INVALID_ICMPTYPE', self.fw.config().getIcmpTypeByName, icmptype_name) print("Checking whether the icmp type '%s' is accessible" % new_icmptype_name) config_icmptype = self.fw.config().getIcmpTypeByName(new_icmptype_name) icmptype_settings = config_icmptype.getSettings() self.assertEquals(icmptype_settings.getVersion(), icmptype_version) self.assertEquals(icmptype_settings.getShort(), icmptype_short) self.assertEquals(icmptype_settings.getDescription(), icmptype_description) self.assertEquals(icmptype_settings.getDestinations().sort(), icmptype_destinations.sort()) print("Removing the icmp type '%s'" % new_icmptype_name) config_icmptype.remove() print( "Checking whether the removed icmp type is accessible (it shouldn't be)" ) self.assertRaisesRegexp(Exception, 'INVALID_ICMPTYPE', self.fw.config().getIcmpTypeByName, new_icmptype_name)
class TestFirewallDInterfaceConfig(unittest.TestCase): """ For testing of permanent changes, ie. those that survive restart: """ def setUp(self): unittest.TestCase.setUp(self) self.fw = FirewallClient() def tearDown(self): unittest.TestCase.tearDown(self) def test_zones(self): """ /org/fedoraproject/FirewallD1/config listZones() getZoneByName(String name) addZone(String name, Dict of {String, Variant} zone_settings) /org/fedoraproject/FirewallD1/config/zone/<id> getSettings() loadDefaults() update() rename() remove() """ print ("\nGetting invalid zone") self.assertRaisesRegexp(Exception, 'INVALID_ZONE', self.fw.config().getZoneByName, "dummyname") zone_version = "1.0" zone_short = "Testing" zone_description = "this is just a testing zone" zone_target = DEFAULT_ZONE_TARGET zone_services = ["dhcpv6-client", "ssh"] zone_ports = [("123", "tcp"), ("666-667", "udp")] zone_icmpblocks = ["redirect", "echo-reply"] zone_masquerade = False zone_forward_ports = [("443", "tcp", "441", "192.168.0.2"), ("123", "udp", "321", "192.168.1.1")] settings = FirewallClientZoneSettings() settings.setVersion(zone_version) settings.setShort(zone_short) settings.setDescription(zone_description) settings.setTarget(zone_target) settings.setServices(zone_services) settings.setPorts(zone_ports) settings.setIcmpBlocks(zone_icmpblocks) settings.setMasquerade(zone_masquerade) settings.setForwardPorts(zone_forward_ports) print ("Adding zone with name that already exists") self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', self.fw.config().addZone, "home", settings) print ("Adding zone with empty name") self.assertRaisesRegexp(Exception, 'INVALID_NAME', self.fw.config().addZone, "", settings) zone_name = "test" print ("Adding proper zone") self.fw.config().addZone (zone_name, settings) print ("Checking the saved (permanent) settings") config_zone = self.fw.config().getZoneByName(zone_name) self.assertIsInstance(config_zone, firewall.client.FirewallClientConfigZone) zone_settings = config_zone.getSettings() self.assertIsInstance(zone_settings, firewall.client.FirewallClientZoneSettings) self.assertEquals(zone_settings.getVersion(), zone_version) self.assertEquals(zone_settings.getShort(), zone_short) self.assertEquals(zone_settings.getDescription(), zone_description) self.assertEquals(zone_settings.getTarget(), zone_target) self.assertEquals(zone_settings.getServices().sort(), zone_services.sort()) self.assertEquals(zone_settings.getPorts().sort(), zone_ports.sort()) self.assertEquals(zone_settings.getIcmpBlocks().sort(), zone_icmpblocks.sort()) self.assertEquals(zone_settings.getMasquerade(), zone_masquerade) self.assertEquals(zone_settings.getForwardPorts().sort(), zone_forward_ports.sort()) print ("Updating settings") zone_services.append("mdns") zone_settings.setServices(zone_services) config_zone.update(zone_settings) print ("Reloading firewalld") self.fw.reload() print ("Checking of runtime settings") self.assertTrue(zone_name in self.fw.getZones()) self.assertEquals(self.fw.getServices(zone_name).sort(), zone_services.sort()) self.assertEquals(self.fw.getPorts(zone_name).sort(), zone_ports.sort()) self.assertEquals(self.fw.getIcmpBlocks(zone_name).sort(), zone_icmpblocks.sort()) self.assertEquals(self.fw.queryMasquerade(zone_name), zone_masquerade) self.assertEquals(self.fw.getForwardPorts(zone_name).sort(), zone_forward_ports.sort()) print ("Renaming zone to name that already exists") config_zone = self.fw.config().getZoneByName(zone_name) self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', config_zone.rename, "home") new_zone_name = "renamed" print ("Renaming zone '%s' to '%s'" % (zone_name, new_zone_name)) config_zone.rename(new_zone_name) print ("Checking whether the zone '%s' is accessible (it shouldn't be)" % zone_name) self.assertRaisesRegexp(Exception, 'INVALID_ZONE', self.fw.config().getZoneByName, zone_name) print ("Checking whether the zone '%s' is accessible" % new_zone_name) config_zone = self.fw.config().getZoneByName(new_zone_name) zone_settings = config_zone.getSettings() self.assertEquals(zone_settings.getVersion(), zone_version) self.assertEquals(zone_settings.getShort(), zone_short) self.assertEquals(zone_settings.getDescription(), zone_description) self.assertEquals(zone_settings.getTarget(), zone_target) self.assertEquals(zone_settings.getServices().sort(), zone_services.sort()) self.assertEquals(zone_settings.getPorts().sort(), zone_ports.sort()) self.assertEquals(zone_settings.getIcmpBlocks().sort(), zone_icmpblocks.sort()) self.assertEquals(zone_settings.getMasquerade(), zone_masquerade) self.assertEquals(zone_settings.getForwardPorts().sort(), zone_forward_ports.sort()) print ("Removing the zone '%s'" % new_zone_name) config_zone.remove() print ("Checking whether the removed zone is accessible (it shouldn't be)") self.assertRaisesRegexp(Exception, 'INVALID_ZONE', self.fw.config().getZoneByName, new_zone_name) # TODO test loadDefaults() ? def test_services(self): """ /org/fedoraproject/FirewallD1/config listServices() getServiceByName(String name) addService(String name, Dict of {String, Variant} settings) /org/fedoraproject/FirewallD1/config/service/<id> getSettings() loadDefaults() update() rename() remove() """ print ("\nGetting invalid service") self.assertRaisesRegexp(Exception, 'INVALID_SERVICE', self.fw.config().getServiceByName, "dummyname") service_version = "1.0" service_short = "Testing" service_description = "this is just a testing service" service_ports = [("123", "tcp"), ("666-667", "udp")] service_modules = ["nf_test_first", "nf_test_second"] service_destinations = {'ipv4': '1.2.3.4', 'ipv6': 'dead::beef'} settings = FirewallClientServiceSettings() # ["", "", "", [], [], {}] settings.setVersion(service_version) settings.setShort(service_short) settings.setDescription(service_description) settings.setPorts(service_ports) settings.setModules(service_modules) settings.setDestinations(service_destinations) print ("Adding service with name that already exists") self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', self.fw.config().addService, "mdns", settings) print ("Adding service with empty name") self.assertRaisesRegexp(Exception, 'INVALID_NAME', self.fw.config().addService, "", settings) service_name = "test" print ("Adding proper service") self.fw.config().addService (service_name, settings) print ("Checking the saved (permanent) settings") config_service = self.fw.config().getServiceByName(service_name) self.assertIsInstance(config_service, firewall.client.FirewallClientConfigService) service_settings = config_service.getSettings() self.assertIsInstance(service_settings, firewall.client.FirewallClientServiceSettings) print ("Updating settings") service_modules.append("nf_test_third") service_destinations["ipv6"] = "3ffe:501:ffff::" service_settings.setModules(service_modules) service_settings.setDestinations(service_destinations) config_service.update(service_settings) self.assertEquals(service_settings.getVersion(), service_version) self.assertEquals(service_settings.getShort(), service_short) self.assertEquals(service_settings.getDescription(), service_description) self.assertEquals(service_settings.getPorts().sort(), service_ports.sort()) self.assertEquals(service_settings.getModules().sort(), service_modules.sort()) self.assertDictEqual(service_settings.getDestinations(), service_destinations) print ("Renaming service to name that already exists") config_service = self.fw.config().getServiceByName(service_name) self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', config_service.rename, "mdns") new_service_name = "renamed" print ("Renaming service '%s' to '%s'" % (service_name, new_service_name)) config_service.rename(new_service_name) print ("Checking whether the service '%s' is accessible (it shouldn't be)" % service_name) self.assertRaisesRegexp(Exception, 'INVALID_SERVICE', self.fw.config().getServiceByName, service_name) print ("Checking whether the service '%s' is accessible" % new_service_name) config_service = self.fw.config().getServiceByName(new_service_name) service_settings = config_service.getSettings() self.assertEquals(service_settings.getVersion(), service_version) self.assertEquals(service_settings.getShort(), service_short) self.assertEquals(service_settings.getDescription(), service_description) self.assertEquals(service_settings.getPorts().sort(), service_ports.sort()) self.assertEquals(service_settings.getModules().sort(), service_modules.sort()) self.assertDictEqual(service_settings.getDestinations(), service_destinations) print ("Removing the service '%s'" % new_service_name) config_service.remove() print ("Checking whether the removed service is accessible (it shouldn't be)") self.assertRaisesRegexp(Exception, 'INVALID_SERVICE', self.fw.config().getServiceByName, new_service_name) # TODO test loadDefaults() ? def test_icmptypes(self): """ /org/fedoraproject/FirewallD1/config listIcmpTypes() getIcmpTypeByName(String name) addIcmpType(String name, Dict of {String, Variant} settings) /org/fedoraproject/FirewallD1/config/icmptype/<id> getSettings() loadDefaults() update() rename() remove() """ print ("\nGetting invalid icmp-type") self.assertRaisesRegexp(Exception, 'INVALID_ICMPTYPE', self.fw.config().getIcmpTypeByName, "dummyname") icmptype_version = "1.0" icmptype_short = "Testing" icmptype_description = "this is just a testing icmp type" icmptype_destinations = ['ipv4'] settings = FirewallClientIcmpTypeSettings() # ["", "", "", []] settings.setVersion(icmptype_version) settings.setShort(icmptype_short) settings.setDescription(icmptype_description) settings.setDestinations(icmptype_destinations) print ("Adding icmp type with name that already exists") self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', self.fw.config().addIcmpType, "echo-reply", settings) print ("Adding icmp type with empty name") self.assertRaisesRegexp(Exception, 'INVALID_NAME', self.fw.config().addIcmpType, "", settings) icmptype_name = "test" print ("Adding proper icmp type") self.fw.config().addIcmpType (icmptype_name, settings) print ("Checking the saved (permanent) settings") config_icmptype = self.fw.config().getIcmpTypeByName(icmptype_name) self.assertIsInstance(config_icmptype, firewall.client.FirewallClientConfigIcmpType) icmptype_settings = config_icmptype.getSettings() self.assertIsInstance(icmptype_settings, firewall.client.FirewallClientIcmpTypeSettings) print ("Updating settings") icmptype_destinations.append("ipv6") icmptype_settings.setDestinations(icmptype_destinations) config_icmptype.update(icmptype_settings) self.assertEquals(icmptype_settings.getVersion(), icmptype_version) self.assertEquals(icmptype_settings.getShort(), icmptype_short) self.assertEquals(icmptype_settings.getDescription(), icmptype_description) self.assertEquals(icmptype_settings.getDestinations().sort(), icmptype_destinations.sort()) print ("Renaming icmp type to name that already exists") config_icmptype = self.fw.config().getIcmpTypeByName(icmptype_name) self.assertRaisesRegexp(Exception, 'NAME_CONFLICT', config_icmptype.rename, "echo-reply") new_icmptype_name = "renamed" print ("Renaming icmp type '%s' to '%s'" % (icmptype_name, new_icmptype_name)) config_icmptype.rename(new_icmptype_name) print ("Checking whether the icmp type '%s' is accessible (it shouldn't be)" % icmptype_name) self.assertRaisesRegexp(Exception, 'INVALID_ICMPTYPE', self.fw.config().getIcmpTypeByName, icmptype_name) print ("Checking whether the icmp type '%s' is accessible" % new_icmptype_name) config_icmptype = self.fw.config().getIcmpTypeByName(new_icmptype_name) icmptype_settings = config_icmptype.getSettings() self.assertEquals(icmptype_settings.getVersion(), icmptype_version) self.assertEquals(icmptype_settings.getShort(), icmptype_short) self.assertEquals(icmptype_settings.getDescription(), icmptype_description) self.assertEquals(icmptype_settings.getDestinations().sort(), icmptype_destinations.sort()) print ("Removing the icmp type '%s'" % new_icmptype_name) config_icmptype.remove() print ("Checking whether the removed icmp type is accessible (it shouldn't be)") self.assertRaisesRegexp(Exception, 'INVALID_ICMPTYPE', self.fw.config().getIcmpTypeByName, new_icmptype_name)
class FirewallD: def __init__(self): try: from firewall.client import FirewallClient self._fw = FirewallClient() if not self._fw.connected: debugprint("FirewallD seems to be installed but not running") self._fw = None self._zone = None self.running = False return zone_name = self._get_active_zone() if zone_name: self._zone = self._fw.config().getZoneByName(zone_name) else: self._zone = None self.running = True debugprint("Using /org/fedoraproject/FirewallD1") except (ImportError, dbus.exceptions.DBusException): self._fw = None self._zone = None self.running = False def _get_active_zone(self): zones = self._fw.getActiveZones().keys() if not zones: debugprint("FirewallD: no changeable zone") return None elif len(zones) == 1: # most probable case return zones[0] else: # Do we need to handle the 'more active zones' case ? # It's quite unlikely case because that would mean that more # network connections are up and running and they are # in different network zones at the same time. debugprint("FirewallD returned more zones, taking first one") return zones[0] def _get_fw_data(self, reply_handler=None, error_handler=None): try: debugprint("%s in _get_fw_data: _fw_data is %s" % (self, repr(self._fw_data.getServices()))) if self._fw_data: debugprint("Using cached firewall data") if reply_handler: reply_handler(self._fw_data) except AttributeError: try: self._fw_data = self._zone.getSettings() debugprint("Firewall data obtained") if reply_handler: reply_handler(self._fw_data) except (dbus.exceptions.DBusException, AttributeError, ValueError) as e: self._fw_data = None debugprint("Exception examining firewall") if error_handler: error_handler(e) return self._fw_data def read(self, reply_handler=None, error_handler=None): if reply_handler: self._get_fw_data(reply_handler, error_handler) else: self._get_fw_data() def write(self): try: if self._zone: self._zone.update(self._fw_data) self._fw.reload() except dbus.exceptions.DBusException: nonfatalException() def add_service(self, service): if not self._get_fw_data(): return self._fw_data.addService(service) def check_ipp_client_allowed(self): if not self._get_fw_data(): return True return (IPP_CLIENT_SERVICE in self._fw_data.getServices() or [IPP_CLIENT_PORT, IPP_CLIENT_PROTOCOL ] in self._fw_data.getPorts()) def check_ipp_server_allowed(self): if not self._get_fw_data(): return True return (IPP_SERVER_SERVICE in self._fw_data.getServices() or [IPP_SERVER_PORT, IPP_SERVER_PROTOCOL ] in self._fw_data.getPorts()) def check_samba_client_allowed(self): if not self._get_fw_data(): return True return (SAMBA_CLIENT_SERVICE in self._fw_data.getServices()) def check_mdns_allowed(self): if not self._get_fw_data(): return True return (MDNS_SERVICE in self._fw_data.getServices() or [MDNS_PORT, MDNS_PROTOCOL] in self._fw_data.getPorts())
def main(): module = AnsibleModule( argument_spec=dict( service=dict(required=False, type="list", default=[]), port=dict(required=False, type="list", default=[]), trust=dict(required=False, type="list", default=[]), trust_by_mac=dict(required=False, type="list", default=[]), masq=dict(required=False, type="list", default=[]), masq_by_mac=dict(required=False, type="list", default=[]), forward_port=dict(required=False, type="list", default=[]), forward_port_by_mac=dict(required=False, type="list", default=[]), zone=dict(required=False, type="str", default=None), state=dict(choices=["enabled", "disabled"], required=True), ), required_one_of=([ "service", "port", "trust", "trust_by_mac", "masq", "masq_by_mac", "forward_prot", ], ), supports_check_mode=True, ) if not HAS_FIREWALLD and not HAS_SYSTEM_CONFIG_FIREWALL: module.fail_json(msg="No firewall backend could be imported.") service = module.params["service"] port = [] for port_proto in module.params["port"]: _port, _protocol = port_proto.split("/") if _protocol is None: module.fail_json(msg="improper port format (missing protocol?)") port.append((_port, _protocol)) trust = module.params["trust"] trust_by_mac = [] for item in module.params["trust_by_mac"]: _interface = get_device_for_mac(item) if _interface is None: module.fail_json(msg="MAC address not found %s" % item) trust_by_mac.append(_interface) masq = module.params["masq"] masq_by_mac = [] for item in module.params["masq_by_mac"]: _interface = get_device_for_mac(item) if _interface is None: module.fail_json(msg="MAC address not found %s" % item) masq_by_mac.append(_interface) forward_port = [] for item in module.params["forward_port"]: args = item.split(";") if len(args) == 4: _interface, __port, _to_port, _to_addr = args elif len(args) == 3: _interface = "" __port, _to_port, _to_addr = args else: module.fail_json(msg="improper forward_port format: %s" % item) _port, _protocol = __port.split("/") if _protocol is None: module.fail_json( msg="improper forward port format (missing protocol?)") if _to_port == "": _to_port = None if _to_addr == "": _to_addr = None forward_port.append((_interface, _port, _protocol, _to_port, _to_addr)) forward_port_by_mac = [] for item in module.params["forward_port_by_mac"]: args = item.split(";") if len(args) != 4: module.fail_json(msg="improper forward_port_by_mac format") _mac_addr, __port, _to_port, _to_addr = args _port, _protocol = __port.split("/") if _protocol is None: module.fail_json( msg="improper forward_port_by_mac format (missing protocol?)") if _to_port == "": _to_port = None if _to_addr == "": _to_addr = None _interface = get_device_for_mac(_mac_addr) if _interface is None: module.fail_json(msg="MAC address not found %s" % _mac_addr) forward_port_by_mac.append( (_interface, _port, _protocol, _to_port, _to_addr)) zone = module.params["zone"] if HAS_SYSTEM_CONFIG_FIREWALL and zone is not None: module.fail_json( msg="Zone can not be used with system-config-firewall/lokkit.") desired_state = module.params["state"] if HAS_FIREWALLD: fw = FirewallClient() def exception_handler(exception_message): module.fail_json(msg=exception_message) fw.setExceptionHandler(exception_handler) if not fw.connected: module.fail_json(msg="firewalld service must be running") trusted_zone = "trusted" external_zone = "external" if zone is not None: if zone not in fw.getZones(): module.fail_json(msg="Runtime zone '%s' does not exist." % zone) if zone not in fw.config().getZoneNames(): module.fail_json(msg="Permanent zone '%s' does not exist." % zone) else: zone = fw.getDefaultZone() fw_zone = fw.config().getZoneByName(zone) fw_settings = fw_zone.getSettings() changed = False changed_zones = {} # service for item in service: if desired_state == "enabled": if not fw.queryService(zone, item): fw.addService(zone, item) changed = True if not fw_settings.queryService(item): fw_settings.addService(item) changed = True changed_zones[fw_zone] = fw_settings elif desired_state == "disabled": if fw.queryService(zone, item): fw.removeService(zone, item) if fw_settings.queryService(item): fw_settings.removeService(item) changed = True changed_zones[fw_zone] = fw_settings # port for _port, _protocol in port: if desired_state == "enabled": if not fw.queryPort(zone, _port, _protocol): fw.addPort(zone, _port, _protocol) changed = True if not fw_settings.queryPort(_port, _protocol): fw_settings.addPort(_port, _protocol) changed = True changed_zones[fw_zone] = fw_settings elif desired_state == "disabled": if fw.queryPort(zone, _port, _protocol): fw.removePort(zone, _port, _protocol) changed = True if fw_settings.queryPort(_port, _protocol): fw_settings.removePort(_port, _protocol) changed = True changed_zones[fw_zone] = fw_settings # trust, trust_by_mac if len(trust) > 0 or len(trust_by_mac) > 0: items = trust if len(trust_by_mac) > 0: items.extend(trust_by_mac) if zone != trusted_zone: _fw_zone = fw.config().getZoneByName(trusted_zone) if _fw_zone in changed_zones: _fw_settings = changed_zones[_fw_zone] else: _fw_settings = _fw_zone.getSettings() else: _fw_zone = fw_zone _fw_settings = fw_settings for item in items: if desired_state == "enabled": if try_set_zone_of_interface(trusted_zone, item): changed = True else: if not fw.queryInterface(trusted_zone, item): fw.changeZoneOfInterface(trusted_zone, item) changed = True if not _fw_settings.queryInterface(item): _fw_settings.addInterface(item) changed = True changed_zones[_fw_zone] = _fw_settings elif desired_state == "disabled": if try_set_zone_of_interface("", item): if module.check_mode: module.exit_json(changed=True) else: if fw.queryInterface(trusted_zone, item): fw.removeInterface(trusted_zone, item) changed = True if _fw_settings.queryInterface(item): _fw_settings.removeInterface(item) changed = True changed_zones[_fw_zone] = _fw_settings # masq, masq_by_mac if len(masq) > 0 or len(masq_by_mac) > 0: items = masq if len(masq_by_mac) > 0: items.extend(masq_by_mac) if zone != external_zone: _fw_zone = fw.config().getZoneByName(external_zone) if _fw_zone in changed_zones: _fw_settings = changed_zones[_fw_zone] else: _fw_settings = _fw_zone.getSettings() else: _fw_zone = fw_zone _fw_settings = fw_settings for item in items: if desired_state == "enabled": if try_set_zone_of_interface(external_zone, item): changed = True else: if not fw.queryInterface(external_zone, item): fw.changeZoneOfInterface(external_zone, item) changed = True if not _fw_settings.queryInterface(item): _fw_settings.addInterface(item) changed = True changed_zones[_fw_zone] = _fw_settings elif desired_state == "disabled": if try_set_zone_of_interface("", item): if module.check_mode: module.exit_json(changed=True) else: if fw.queryInterface(external_zone, item): fw.removeInterface(external_zone, item) changed = True if _fw_settings.queryInterface(item): _fw_settings.removeInterface(item) changed = True changed_zones[_fw_zone] = _fw_settings # forward_port, forward_port_by_mac if len(forward_port) > 0 or len(forward_port_by_mac) > 0: items = forward_port if len(forward_port_by_mac) > 0: items.extend(forward_port_by_mac) for _interface, _port, _protocol, _to_port, _to_addr in items: if _interface != "": _zone = fw.getZoneOfInterface(_interface) else: _zone = zone if _zone != "" and _zone != zone: _fw_zone = fw.config().getZoneByName(_zone) if _fw_zone in changed_zones: _fw_settings = changed_zones[_fw_zone] else: _fw_settings = _fw_zone.getSettings() else: _fw_zone = fw_zone _fw_settings = fw_settings if desired_state == "enabled": if not fw.queryForwardPort(_zone, _port, _protocol, _to_port, _to_addr): fw.addForwardPort(_zone, _port, _protocol, _to_port, _to_addr) changed = True if not _fw_settings.queryForwardPort( _port, _protocol, _to_port, _to_addr): _fw_settings.addForwardPort(_port, _protocol, _to_port, _to_addr) changed = True changed_zones[_fw_zone] = _fw_settings elif desired_state == "disabled": if fw.queryForwardPort(_zone, _port, _protocol, _to_port, _to_addr): fw.removeForwardPort(_zone, _port, _protocol, _to_port, _to_addr) changed = True if _fw_settings.queryForwardPort(_port, _protocol, _to_port, _to_addr): _fw_settings.removeForwardPort(_port, _protocol, _to_port, _to_addr) changed = True changed_zones[_fw_zone] = _fw_settings # apply changes if changed: for _zone in changed_zones: _zone.update(changed_zones[_zone]) module.exit_json(changed=True) elif HAS_SYSTEM_CONFIG_FIREWALL: (config, old_config, _) = fw_lokkit.loadConfig(args=[], dbus_parser=True) changed = False # service for item in service: if config.services is None: config.services = [] if desired_state == "enabled": if item not in config.services: config.services.append(item) changed = True elif desired_state == "disabled": if item in config.services: config.services.remove(item) changed = True # port for _port, _protocol in port: if config.ports is None: config.ports = [] _range = getPortRange(_port) if _range < 0: module.fail_json(msg="invalid port definition %s" % _port) elif _range is None: module.fail_json(msg="port _range is not unique.") elif len(_range) == 2 and _range[0] >= _range[1]: module.fail_json(msg="invalid port range %s" % _port) port_proto = (_range, _protocol) if desired_state == "enabled": if port_proto not in config.ports: config.ports.append(port_proto) changed = True elif desired_state == "disabled": if port_proto in config.ports: config.ports.remove(port_proto) changed = True # trust, trust_by_mac if len(trust) > 0 or len(trust_by_mac) > 0: if config.trust is None: config.trust = [] items = trust if len(trust_by_mac) > 0: items.extend(trust_by_mac) for item in items: if desired_state == "enabled": if item not in config.trust: config.trust.append(item) changed = True elif desired_state == "disabled": if item in config.trust: config.trust.remove(item) changed = True # masq, masq_by_mac if len(masq) > 0 or len(masq_by_mac) > 0: if config.masq is None: config.masq = [] items = masq if len(masq_by_mac) > 0: items.extend(masq_by_mac) for item in items: if desired_state == "enabled": if item not in config.masq: config.masq.append(item) changed = True elif desired_state == "disabled": if item in config.masq: config.masq.remove(item) changed = True # forward_port, forward_port_by_mac if len(forward_port) > 0 or len(forward_port_by_mac) > 0: if config.forward_port is None: config.forward_port = [] items = forward_port if len(forward_port_by_mac) > 0: items.extend(forward_port_by_mac) for _interface, _port, _protocol, _to_port, _to_addr in items: _range = getPortRange(_port) if _range < 0: module.fail_json(msg="invalid port definition") elif _range is None: module.fail_json(msg="port _range is not unique.") elif len(_range) == 2 and _range[0] >= _range[1]: module.fail_json(msg="invalid port range") fwd_port = { "if": _interface, "port": _range, "proto": _protocol } if _to_port is not None: _range = getPortRange(_to_port) if _range < 0: module.fail_json(msg="invalid port definition %s" % _to_port) elif _range is None: module.fail_json(msg="port _range is not unique.") elif len(_range) == 2 and _range[0] >= _range[1]: module.fail_json(msg="invalid port range") fwd_port["toport"] = _range if _to_addr is not None: fwd_port["toaddr"] = _to_addr if desired_state == "enabled": if fwd_port not in config.forward_port: config.forward_port.append(fwd_port) changed = True elif desired_state == "disabled": if fwd_port in config.forward_port: config.forward_port.remove(fwd_port) changed = True # apply changes if changed: fw_lokkit.updateFirewall(config, old_config) if module.check_mode: module.exit_json(changed=True) else: module.fail_json(msg="No firewalld and system-config-firewall") module.exit_json(changed=False)
def installFirewall(self): """install firewall""" log.debug1("%s.installFirewall()", self._log_prefix) # are there any firewall settings to apply? if len(self._settings["firewall"]["services"]) + \ len(self._settings["firewall"]["ports"]) < 1: return # create firewall client fw = FirewallClient() log.debug2("TRACE: Firewall client created") # Make sure firewalld is running by getting the # default zone try: default_zone = fw.getDefaultZone() except DBusException: # firewalld is not running log.error("Firewalld is not running or rolekit cannot access it") raise # save changes to the firewall try: fw_changes = self._settings["firewall-changes"] except KeyError: fw_changes = { } log.debug2("TRACE: Checking for zones: {}".format(self._settings)) try: zones = self._settings["firewall_zones"] except KeyError: zones = [] # if firewall_zones setting is empty, use default zone if len(zones) < 1: zones = [ default_zone ] log.debug2("TRACE: default zone {}".format(zones[0])) for zone in zones: log.debug2("TRACE: Processing zone {0}".format(zone)) # get permanent zone settings, run-time settings do not need a # special treatment z_perm = fw.config().getZoneByName(zone).getSettings() for service in self._settings["firewall"]["services"]: try: fw.addService(zone, service, 0) except Exception as e: if not "ALREADY_ENABLED" in str(e): raise else: fw_changes.setdefault(zone, {}).setdefault("services", {}).setdefault(service, []).append("runtime") if not z_perm.queryService(service): z_perm.addService(service) fw_changes.setdefault(zone, {}).setdefault("services", {}).setdefault(service, []).append("permanent") for port_proto in self._settings["firewall"]["ports"]: port, proto = port_proto.split("/") try: fw.addPort(zone, port, proto, 0) except Exception as e: if not "ALREADY_ENABLED" in str(e): raise else: fw_changes.setdefault(zone, {}).setdefault("ports", {}).setdefault(port_proto, []).append("runtime") if not z_perm.queryPort(port, proto): z_perm.addPort(port, proto) fw_changes.setdefault(zone, {}).setdefault("ports", {}).setdefault(port_proto, []).append("permanent") fw.config().getZoneByName(zone).update(z_perm) self._settings["firewall-changes"] = fw_changes self._settings.write()
def main(): global module ## make module global so we don't have to pass it to action_handler every ## function call global module module = AnsibleModule(argument_spec=dict( service=dict(required=False, default=None), port=dict(required=False, default=None), rich_rule=dict(required=False, default=None), zone=dict(required=False, default=None), 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), timeout=dict(type='int', required=False, default=0), interface=dict(required=False, default=None), masquerade=dict(required=False, default=None), offline=dict(type='bool', required=False, default=None), ), supports_check_mode=True) ## Handle running (online) daemon vs non-running (offline) daemon global fw global fw_offline global Rich_Rule global FirewallClientZoneSettings ## Imports try: import firewall.config FW_VERSION = firewall.config.VERSION from firewall.client import Rich_Rule from firewall.client import FirewallClient fw = None fw_offline = False try: fw = FirewallClient() fw.getDefaultZone() except AttributeError: ## Firewalld is not currently running, permanent-only operations ## Import other required parts of the firewalld API ## ## 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() fw_offline = True except ImportError: ## Make python 2.4 shippable ci tests happy e = sys.exc_info()[1] module.fail_json( msg= 'firewalld and its python 2 module are required for this module, version 2.0.11 or newer required ' '(3.0.9 or newer for offline operations) \n %s' % e) if fw_offline: ## Pre-run version checking if FW_VERSION < "0.3.9": module.fail_json( msg= 'unsupported version of firewalld, offline operations require >= 3.0.9' ) else: ## Pre-run version checking if FW_VERSION < "0.2.11": module.fail_json( msg='unsupported version of firewalld, requires >= 2.0.11') ## 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 >= 2.0.11" % FW_VERSION) ## Verify required params are provided if module.params['source'] is None and module.params['permanent'] is None: module.fail_json(msg='permanent is a required parameter') if module.params['interface'] is not None and module.params['zone'] is None: module.fail(msg='zone is a required parameter') if module.params['immediate'] and fw_offline: module.fail( msg= 'firewall is not currently running, unable to perform immediate actions without a running firewall daemon' ) ## Global Vars changed = False msgs = [] service = module.params['service'] rich_rule = module.params['rich_rule'] source = module.params['source'] if module.params['port'] is not None: port, protocol = module.params['port'].split('/') if protocol is None: module.fail_json(msg='improper port format (missing protocol?)') else: port = None if module.params['zone'] is not None: zone = module.params['zone'] else: if fw_offline: zone = fw.get_default_zone() else: zone = fw.getDefaultZone() 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'] modification_count = 0 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 modification_count > 1: module.fail_json( msg= 'can only operate on port, service, rich_rule or interface at once' ) if service is not None: if immediate and permanent: is_enabled_permanent = action_handler( get_service_enabled_permanent, (zone, service)) is_enabled_immediate = action_handler(get_service_enabled, (zone, service)) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler(set_service_enabled_permanent, (zone, service)) changed = True if not is_enabled_immediate: action_handler(set_service_enabled, (zone, service, timeout)) changed = True elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler(set_service_disabled_permanent, (zone, service)) changed = True if is_enabled_immediate: action_handler(set_service_disabled, (zone, service)) changed = True elif permanent and not immediate: is_enabled = action_handler(get_service_enabled_permanent, (zone, service)) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_service_enabled_permanent, (zone, service)) changed = True elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_service_disabled_permanent, (zone, service)) changed = True elif immediate and not permanent: is_enabled = action_handler(get_service_enabled, (zone, service)) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_service_enabled, (zone, service, timeout)) changed = True elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_service_disabled, (zone, service)) changed = True if changed is True: msgs.append("Changed service %s to %s" % (service, desired_state)) # FIXME - source type does not handle non-permanent mode, this was an # oversight in the past. if source is not None: is_enabled = action_handler(get_source, (zone, source)) if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(add_source, (zone, source)) changed = True msgs.append("Added %s to zone %s" % (source, zone)) elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(remove_source, (zone, source)) changed = True msgs.append("Removed %s from zone %s" % (source, zone)) if port is not None: if immediate and permanent: is_enabled_permanent = action_handler(get_port_enabled_permanent, (zone, [port, protocol])) is_enabled_immediate = action_handler(get_port_enabled, (zone, [port, protocol])) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler(set_port_enabled_permanent, (zone, port, protocol)) changed = True if not is_enabled_immediate: action_handler(set_port_enabled, (zone, port, protocol, timeout)) changed = True elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler(set_port_disabled_permanent, (zone, port, protocol)) changed = True if is_enabled_immediate: action_handler(set_port_disabled, (zone, port, protocol)) changed = True elif permanent and not immediate: is_enabled = action_handler(get_port_enabled_permanent, (zone, [port, protocol])) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_port_enabled_permanent, (zone, port, protocol)) changed = True elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_port_disabled_permanent, (zone, port, protocol)) changed = True if immediate and not permanent: is_enabled = action_handler(get_port_enabled, (zone, [port, protocol])) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_port_enabled, (zone, port, protocol, timeout)) changed = True elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_port_disabled, (zone, port, protocol)) changed = True if changed is True: msgs.append("Changed port %s to %s" % ("%s/%s" % (port, protocol), \ desired_state)) if rich_rule is not None: if immediate and permanent: is_enabled_permanent = action_handler( get_rich_rule_enabled_permanent, (zone, rich_rule)) is_enabled_immediate = action_handler(get_rich_rule_enabled, (zone, rich_rule)) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler(set_rich_rule_enabled_permanent, (zone, rich_rule)) changed = True if not is_enabled_immediate: action_handler(set_rich_rule_enabled, (zone, rich_rule, timeout)) changed = True elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler(set_rich_rule_disabled_permanent, (zone, rich_rule)) changed = True if is_enabled_immediate: action_handler(set_rich_rule_disabled, (zone, rich_rule)) changed = True if permanent and not immediate: is_enabled = action_handler(get_rich_rule_enabled_permanent, (zone, rich_rule)) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_rich_rule_enabled_permanent, (zone, rich_rule)) changed = True elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_rich_rule_disabled_permanent, (zone, rich_rule)) changed = True if immediate and not permanent: is_enabled = action_handler(get_rich_rule_enabled, (zone, rich_rule)) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_rich_rule_enabled, (zone, rich_rule, timeout)) changed = True elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_rich_rule_disabled, (zone, rich_rule)) changed = True if changed is True: msgs.append("Changed rich_rule %s to %s" % (rich_rule, desired_state)) if interface is not None: if immediate and permanent: is_enabled_permanent = action_handler(get_interface_permanent, (zone, interface)) is_enabled_immediate = action_handler(get_interface, (zone, interface)) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: change_zone_of_interface_permanent(zone, interface) changed = True if not is_enabled_immediate: change_zone_of_interface(zone, interface) changed = True if changed: msgs.append("Changed %s to zone %s" % (interface, zone)) elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: remove_interface_permanent(zone, interface) changed = True if is_enabled_immediate: remove_interface(zone, interface) changed = True if changed: msgs.append("Removed %s from zone %s" % (interface, zone)) elif permanent and not immediate: is_enabled = action_handler(get_interface_permanent, (zone, interface)) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) change_zone_of_interface_permanent(zone, interface) changed = True msgs.append("Changed %s to zone %s" % (interface, zone)) elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) remove_interface_permanent(zone, interface) changed = True msgs.append("Removed %s from zone %s" % (interface, zone)) elif immediate and not permanent: is_enabled = action_handler(get_interface, (zone, interface)) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) change_zone_of_interface(zone, interface) changed = True msgs.append("Changed %s to zone %s" % (interface, zone)) elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) remove_interface(zone, interface) changed = True msgs.append("Removed %s from zone %s" % (interface, zone)) if masquerade is not None: if immediate and permanent: is_enabled_permanent = action_handler( get_masquerade_enabled_permanent, (zone, )) is_enabled_immediate = action_handler(get_masquerade_enabled, (zone, )) msgs.append('Permanent and Non-Permanent(immediate) operation') if desired_state == "enabled": if not is_enabled_permanent or not is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if not is_enabled_permanent: action_handler(set_masquerade_permanent, (zone, True)) changed = True if not is_enabled_immediate: action_handler(set_masquerade_enabled, (zone, )) changed = True if changed: msgs.append("Added masquerade to zone %s" % (zone)) elif desired_state == "disabled": if is_enabled_permanent or is_enabled_immediate: if module.check_mode: module.exit_json(changed=True) if is_enabled_permanent: action_handler(set_masquerade_permanent, (zone, False)) changed = True if is_enabled_immediate: action_handler(set_masquerade_disabled, (zone, )) changed = True if changed: msgs.append("Removed masquerade from zone %s" % (zone)) elif permanent and not immediate: is_enabled = action_handler(get_masquerade_enabled_permanent, (zone, )) msgs.append('Permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_permanent, (zone, True)) changed = True msgs.append("Added masquerade to zone %s" % (zone)) elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_permanent, (zone, False)) changed = True msgs.append("Removed masquerade from zone %s" % (zone)) elif immediate and not permanent: is_enabled = action_handler(get_masquerade_enabled, (zone, )) msgs.append('Non-permanent operation') if desired_state == "enabled": if is_enabled is False: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_enabled, (zone)) changed = True msgs.append("Added masquerade to zone %s" % (zone)) elif desired_state == "disabled": if is_enabled is True: if module.check_mode: module.exit_json(changed=True) action_handler(set_masquerade_disabled, (zone)) changed = True msgs.append("Removed masquerade from zone %s" % (zone)) if fw_offline: msgs.append("(offline operation: only on-disk configs were altered)") module.exit_json(changed=changed, msg=', '.join(msgs))
def uninstallFirewall(self): """uninstall firewall""" log.debug1("%s.uninstallFirewall()", self._log_prefix) # Removes the settings that have been added in the installFirewall call # get applied changes from installFirewall call if "firewall-changes" in self._settings: fw_changes = self._settings["firewall-changes"] else: # fallback if there was a severe error in deploy before or while # installing the firewall fw_changes = { } # only continue if there are any changes if len(fw_changes) < 1: return # create firewall client fw = FirewallClient() # for all zones for zone in fw_changes: z_perm = fw.config().getZoneByName(zone).getSettings() if "services" in fw_changes[zone]: services = fw_changes[zone]["services"] for service in services: if "runtime" in services[service]: try: fw.removeService(zone, service) except Exception as e: if not "NOT_ENABLED" in str(e): raise if "permanent" in services[service]: try: z_perm.removeService(service) except Exception as e: if not "NOT_ENABLED" in str(e): raise if "ports" in fw_changes[zone]: ports = fw_changes[zone]["ports"] for port_proto in ports: port, proto = port_proto.split("/") if "runtime" in ports[port_proto]: try: fw.removePort(zone, port, proto) except Exception as e: if not "NOT_ENABLED" in str(e): raise if "permanent" in ports[port_proto]: try: z_perm.removePort(port, proto) except Exception as e: if not "NOT_ENABLED" in str(e): raise fw.config().getZoneByName(zone).update(z_perm) # clear fw_changes and save it in _settings fw_changes.clear() self._settings["firewall-changes"] = fw_changes self._settings.write()
# (c) 2017, Michael Scherer ([email protected]) #TODO DOCUMENTATION, example, license from ansible.module_utils.basic import AnsibleModule HAVE_FIREWALLD = True FIREWALLD_RUNNING = True try: import firewall.config FW_VERSION = firewall.config.VERSION from firewall.client import FirewallClient, FirewallClientZoneSettings try: fw = FirewallClient() fw.getDefaultZone() except AttributeError: FIREWALLD_RUNNING = False except ImportError: HAVE_FIREWALLD = False def main(): module = AnsibleModule( argument_spec=dict( name=dict(required=True), state=dict(choices=['present', 'absent'], required=True), no_reload=dict(type='bool', default='no'), ),
# globals module = None # Imports try: import firewall.config FW_VERSION = firewall.config.VERSION from firewall.client import Rich_Rule from firewall.client import FirewallClient fw = None fw_offline = False import_failure = False try: fw = FirewallClient() fw.getDefaultZone() except AttributeError: # Firewalld is not currently running, permanent-only operations # Import other required parts of the firewalld API # # 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() fw_offline = True except ImportError as e:
class FirewallD: def __init__ (self): try: from firewall.client import FirewallClient self._fw = FirewallClient () if not self._fw.connected: debugprint ("FirewallD seems to be installed but not running") self._fw = None self._zone = None self.running = False return zone_name = self._get_active_zone () if zone_name: self._zone = self._fw.config().getZoneByName (zone_name) else: self._zone = None self.running = True debugprint ("Using /org/fedoraproject/FirewallD1") except (ImportError, dbus.exceptions.DBusException): self._fw = None self._zone = None self.running = False def _get_active_zone (self): zones = list(self._fw.getActiveZones().keys()) if not zones: debugprint ("FirewallD: no changeable zone") return None elif len (zones) == 1: # most probable case return zones[0] else: # Do we need to handle the 'more active zones' case ? # It's quite unlikely case because that would mean that more # network connections are up and running and they are # in different network zones at the same time. debugprint ("FirewallD returned more zones, taking first one") return zones[0] def _get_fw_data (self, reply_handler=None, error_handler=None): try: debugprint ("%s in _get_fw_data: _fw_data is %s" % (self, repr(self._fw_data.getServices()))) if self._fw_data: debugprint ("Using cached firewall data") if reply_handler: reply_handler (self._fw_data) except AttributeError: try: self._fw_data = self._zone.getSettings () debugprint ("Firewall data obtained") if reply_handler: reply_handler (self._fw_data) except (dbus.exceptions.DBusException, AttributeError, ValueError) as e: self._fw_data = None debugprint ("Exception examining firewall") if error_handler: error_handler (e) return self._fw_data def read (self, reply_handler=None, error_handler=None): if reply_handler: self._get_fw_data (reply_handler, error_handler) else: self._get_fw_data () def write (self): try: if self._zone: self._zone.update (self._fw_data) self._fw.reload () except dbus.exceptions.DBusException: nonfatalException () def add_service (self, service): if not self._get_fw_data (): return self._fw_data.addService (service) def check_ipp_client_allowed (self): if not self._get_fw_data (): return True return (IPP_CLIENT_SERVICE in self._fw_data.getServices () or [IPP_CLIENT_PORT, IPP_CLIENT_PROTOCOL] in self._fw_data.getPorts ()) def check_ipp_server_allowed (self): if not self._get_fw_data (): return True return (IPP_SERVER_SERVICE in self._fw_data.getServices () or [IPP_SERVER_PORT, IPP_SERVER_PROTOCOL] in self._fw_data.getPorts ()) def check_samba_client_allowed (self): if not self._get_fw_data (): return True return (SAMBA_CLIENT_SERVICE in self._fw_data.getServices ()) def check_mdns_allowed (self): if not self._get_fw_data (): return True return (MDNS_SERVICE in self._fw_data.getServices () or [MDNS_PORT, MDNS_PROTOCOL] in self._fw_data.getPorts ())
def fw(retries=6): ''' firewalld interface is nice, but due to DBUS, it is a great field for various errors and hickups. We then try to get a viable connexion, but also be really smart on establishing one ''' # after 2 success full calls to the firewall # we assume it is correctly configured if _cache.get('f') is not None: if _cache.get('f_call', 0) < 2: try: if _cache['f'].getZones() is None: time.sleep(0.05) assert _cache['f'].getZones() is not None _cache.setdefault('f_call', 0) _cache['f_call'] += 1 except (Exception, ): remove_client() if _cache.get('f') is None: state = 'notready' ttl = time.time() + TIMEOUT attempt = 0 established = False loggued = False while time.time() < ttl: # if we have an instance, we have to wait until the firewall # is in the running state try: attempt += 1 remove_client() _cache['f'] = FirewallClient() state = _cache['f'].get_property('state') # if we have first contacted the firewall without being # in running mode, there is a great chance for the dbus # interface to be bugged. # So there are two cases, # - we assume that the connexion is established only if we # get directly a running state. # - In other cases, we assume the firewall to be ready for # commands only on the second successful connection # on the dbus interface. if state in RUNNINGS: stop = False if attempt == 1: stop = True elif not established: established = True else: stop = True if stop: log.info('firewalld seems now ready') break continue else: established = False time.sleep(0.4) except (Exception, ): log.error(traceback.format_exc()) state = 'unknown' # DBUS doent like at all retry spam time.sleep(5) if not loggued: log.error('firewalld is not ready yet,' ' waiting ({0})'.format(state)) loggued = True if _cache.get('f') is None: if not retries: raise IOError('Cant contact firewalld daemon interface') else: return fw(retries - 1) return _cache['f']
- firewalld: port=8081/tcp permanent=true state=disabled - firewalld: port=161-162/udp permanent=true state=enabled - firewalld: zone=dmz service=http permanent=true state=enabled - firewalld: rich_rule='rule service name="ftp" audit limit value="1/m" accept' permanent=true state=enabled - firewalld: source='192.168.1.0/24' zone=internal state=enabled ''' import os import re try: import firewall.config FW_VERSION = firewall.config.VERSION from firewall.client import FirewallClient fw = FirewallClient() HAS_FIREWALLD = True except ImportError: HAS_FIREWALLD = False ################ # port handling # def get_port_enabled(zone, port_proto): if port_proto in fw.getPorts(zone): return True else: return False
def main(): module_args = dict( name=dict(type="str", required=True), state=dict(choices=["present", "absent"], required=True), settype=dict( choices=[ "hash:ip", "hash:ip,port", "hash:ip,port,ip", "hash:ip,port,net", "hash:ip,mark", "hash:net", "hash:net,net", "hash:net,port", "hash:net,port,net", "hash:net,iface", "hash:mac", ], required=True, ), permanent=dict(type="bool", required=False, default=False), immediate=dict(type="bool", required=False, default=False), addresses=dict(type="list", required=False), ) module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) FirewallTransaction.sanity_check(module) # setup the FirewallDClient client = FirewallClient() sets = client.getIPSets() settings = FirewallClientIPSetSettings() settings.setType(module.params["settype"]) config = client.config() # construct return data result = {"firewalld_ipset_name": module.params["name"]} # Modifying a preexisting ipset if module.params["name"] in sets and module.params["state"] == "present": client_ipset_config = config.getIPSetByName(module.params["name"]) original_entries = client_ipset_config.getEntries() # when we're in check mode we shouldn't commit anything to the # state of the target. Just output what would happen. if module.check_mode: new_entries = module.params["addresses"] else: client_ipset_config.setEntries(module.params["addresses"]) firewalld_state(client, module.params) new_entries = client_ipset_config.getEntries() result["changed"] = new_entries != original_entries result["firewalld_ipset_addresses"] = new_entries # Creating a new ipset because the one proposed in the module declaration # does not exist already. elif module.params["name"] not in sets and module.params["state"] == "present": if module.check_mode: result["firewalld_ipset_addresses"] = module.params["addresses"] else: client_ipset_config = config.addIPSet(module.params["name"], settings) client_ipset_config.setEntries(module.params["addresses"]) firewalld_state(client, module.params) new_entries = client_ipset_config.getEntries() result["firewalld_ipset_addresses"] = new_entries result["changed"] = True # Removing an ipset that exists right now. If an ipset is asked to be # removed when it doesn't exist, we should just return an "unchanged" # state. elif module.params["name"] in sets and module.params["state"] == "absent": client_ipset_config = config.getIPSetByName(module.params["name"]) # avoid stateful actions in check mode if not module.check_mode: original_entries = client_ipset_config.remove() firewalld_state(client, module.params) result["changed"] = True result["firewalld_ipset_addresses"] = [] module.exit_json(**result)