# -*- coding: utf-8 -*- # # Copyright (C) 2011-2016 Red Hat, Inc. # # Authors: # Thomas Woerner # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import time import copy from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET, SOURCE_IPSET_TYPES from firewall.core.fw_transaction import FirewallTransaction from firewall.core.io.policy import Policy from firewall.core.logger import log from firewall.core.rich import Rich_Service, Rich_Port, Rich_Protocol, Rich_SourcePort, Rich_ForwardPort, \ Rich_IcmpBlock, Rich_IcmpType, Rich_Masquerade, Rich_Mark from firewall.functions import checkIPnMask, checkIP6nMask, check_mac from firewall import errors from firewall.errors import FirewallError from firewall.fw_types import LastUpdatedOrderedDict class FirewallZone(object): ZONE_POLICY_PRIORITY = 0 def __init__(self, fw): self._fw = fw self._zones = { } self._zone_policies = { } def __repr__(self): return '%s(%r)' % (self.__class__, self._zones) def cleanup(self): self._zones.clear() self._zone_policies.clear() def new_transaction(self): return FirewallTransaction(self._fw) def policy_name_from_zones(self, fromZone, toZone): return "zone_{fromZone}_{toZone}".format(fromZone=fromZone, toZone=toZone) # zones def get_zones(self): return sorted(self._zones.keys()) def get_active_zones(self): active_zones = [] for zone in self.get_zones(): if self.list_interfaces(zone) or self.list_sources(zone): active_zones.append(zone) return active_zones def get_zone_of_interface(self, interface): interface_id = self.__interface_id(interface) for zone in self._zones: if interface_id in self._zones[zone].settings["interfaces"]: # an interface can only be part of one zone return zone return None def get_zone_of_source(self, source): source_id = self.__source_id(source) for zone in self._zones: if source_id in self._zones[zone].settings["sources"]: # a source_id can only be part of one zone return zone return None def get_zone(self, zone): z = self._fw.check_zone(zone) return self._zones[z] def policy_obj_from_zone_obj(self, z_obj, fromZone, toZone): p_obj = Policy() p_obj.derived_from_zone = z_obj.name p_obj.name = self.policy_name_from_zones(fromZone, toZone) p_obj.priority = self.ZONE_POLICY_PRIORITY p_obj.target = z_obj.target p_obj.ingress_zones = [fromZone] p_obj.egress_zones = [toZone] # copy zone permanent config to policy permanent config # WARN: This assumes the same attribute names. # for setting in ["services", "ports", "masquerade", "forward_ports", "source_ports", "icmp_blocks", "rules", "protocols"]: if fromZone == z_obj.name and toZone == "HOST" and \ setting in ["services", "ports", "source_ports", "icmp_blocks", "protocols"]: # zone --> HOST setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) elif fromZone == "ANY" and toZone == z_obj.name and setting in ["masquerade"]: # any zone --> zone setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) elif fromZone == z_obj.name and toZone == "ANY" and \ setting in ["icmp_blocks", "forward_ports"]: # zone --> any zone setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) elif setting in ["rules"]: p_obj.rules = [] for rule in z_obj.rules: current_policy = self.policy_name_from_zones(fromZone, toZone) if current_policy in self._rich_rule_to_policies(z_obj.name, rule): p_obj.rules.append(copy.deepcopy(rule)) return p_obj def add_zone(self, obj): obj.settings = { x : LastUpdatedOrderedDict() for x in ["interfaces", "sources", "icmp_block_inversion", "forward"] } self._zones[obj.name] = obj self._zone_policies[obj.name] = [] # Create policy objects, will need many: # - (zone --> HOST) - ports, service, etc # - (any zone --> zone) - masquerade # - (zone --> any zone) - ICMP block, icmp block inversion # - also includes forward-ports because it works on (nat, # PREROUTING) and therefore applies to redirects to the local # host or dnat to a different host. # - also includes rich rule "mark" action for the same reason # for fromZone,toZone in [(obj.name, "HOST"), ("ANY", obj.name), (obj.name, "ANY")]: p_obj = self.policy_obj_from_zone_obj(obj, fromZone, toZone) self._fw.policy.add_policy(p_obj) self._zone_policies[obj.name].append(p_obj.name) self.copy_permanent_to_runtime(obj.name) def copy_permanent_to_runtime(self, zone): obj = self._zones[zone] for arg in obj.interfaces: self.add_interface(zone, arg, allow_apply=False) for arg in obj.sources: self.add_source(zone, arg, allow_apply=False) if obj.forward: self.add_forward(zone) if obj.icmp_block_inversion: self.add_icmp_block_inversion(zone) def remove_zone(self, zone): obj = self._zones[zone] if obj.applied: self.unapply_zone_settings(zone) obj.settings.clear() del self._zones[zone] del self._zone_policies[zone] def apply_zones(self, use_transaction=None): for zone in self.get_zones(): z_obj = self._zones[zone] if len(z_obj.interfaces) > 0 or len(z_obj.sources) > 0: log.debug1("Applying zone '%s'", zone) self.apply_zone_settings(zone, use_transaction=use_transaction) def set_zone_applied(self, zone, applied): obj = self._zones[zone] obj.applied = applied # zone from chain def zone_from_chain(self, chain): if "_" not in chain: # no zone chain return None splits = chain.split("_") if len(splits) < 2: return None _chain = None for x in SHORTCUTS: if splits[0] == SHORTCUTS[x]: _chain = x if _chain is not None: # next part needs to be zone name if splits[1] not in self.get_zones(): return None if len(splits) == 2 or \ (len(splits) == 3 and splits[2] in [ "pre", "log", "deny", "allow", "post" ]): return (splits[1], _chain) return None def policy_from_chain(self, chain): x = self.zone_from_chain(chain) if x is None: return None (zone, _chain) = x # derived from _get_table_chains_for_zone_dispatch() if _chain in ["PREROUTING", "FORWARD_IN"]: fromZone = zone toZone = "ANY" elif _chain in ["INPUT"]: fromZone = zone toZone = "HOST" elif _chain in ["POSTROUTING", "FORWARD_OUT"]: fromZone = "ANY" toZone = zone else: raise FirewallError(errors.INVALID_CHAIN, "chain '%s' can't be mapped to a policy" % (chain)) return (self.policy_name_from_zones(fromZone, toZone), _chain) def create_zone_base_by_chain(self, ipv, table, chain, use_transaction=None): # Create zone base chains if the chain is reserved for a zone if ipv in [ "ipv4", "ipv6" ]: x = self.policy_from_chain(chain) if x is not None: (policy, _chain) = self.policy_from_chain(chain) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction self._fw.policy.gen_chain_rules(policy, True, table, _chain, transaction) if use_transaction is None: transaction.execute(True) # settings # generate settings record with sender, timeout def __gen_settings(self, timeout, sender): ret = { "date": time.time(), "sender": sender, "timeout": timeout, } return ret def get_settings(self, zone): return self.get_zone(zone).settings def _zone_settings(self, enable, zone, transaction): settings = self.get_settings(zone) for key in settings: for args in settings[key]: if key == "interfaces": self._interface(enable, zone, args, transaction) elif key == "sources": self._source(enable, zone, args[0], args[1], transaction) elif key == "icmp_block_inversion": continue elif key == "forward": # no need to call this when applying the zone as the rules # will be generated when adding the interfaces/sources pass else: log.warning("Zone '%s': Unknown setting '%s:%s', " "unable to apply", zone, key, args) # ICMP-block-inversion is always applied if enable: self._icmp_block_inversion(enable, zone, transaction) def apply_zone_settings(self, zone, use_transaction=None): _zone = self._fw.check_zone(zone) obj = self._zones[_zone] if obj.applied: return obj.applied = True if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction for policy in self._zone_policies[_zone]: log.debug1("Applying policy (%s) derived from zone '%s'", policy, zone) self._fw.policy.apply_policy_settings(policy, use_transaction=transaction) self._zone_settings(True, _zone, transaction) if use_transaction is None: transaction.execute(True) def unapply_zone_settings(self, zone, use_transaction=None): _zone = self._fw.check_zone(zone) obj = self._zones[_zone] if not obj.applied: return if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction for policy in self._zone_policies[_zone]: self._fw.policy.unapply_policy_settings(policy, use_transaction=transaction) self._zone_settings(False, _zone, transaction) if use_transaction is None: transaction.execute(True) def get_config_with_settings(self, zone): """ :return: exported config updated with runtime settings """ obj = self.get_zone(zone) conf_dict = self.get_config_with_settings_dict(zone) conf_list = [] for i in range(16): # tuple based API has 16 elements if obj.IMPORT_EXPORT_STRUCTURE[i][0] not in conf_dict: # old API needs the empty elements as well. Grab it from the # class otherwise we don't know the type. conf_list.append(copy.deepcopy(getattr(obj, obj.IMPORT_EXPORT_STRUCTURE[i][0]))) else: conf_list.append(conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]]) return tuple(conf_list) def get_config_with_settings_dict(self, zone): """ :return: exported config updated with runtime settings """ permanent = self.get_zone(zone).export_config_dict() if permanent["target"] == DEFAULT_ZONE_TARGET: permanent["target"] = "default" runtime = { "services": self.list_services(zone), "ports": self.list_ports(zone), "icmp_blocks": self.list_icmp_blocks(zone), "masquerade": self.query_masquerade(zone), "forward_ports": self.list_forward_ports(zone), "interfaces": self.list_interfaces(zone), "sources": self.list_sources(zone), "rules_str": self.list_rules(zone), "protocols": self.list_protocols(zone), "source_ports": self.list_source_ports(zone), "icmp_block_inversion": self.query_icmp_block_inversion(zone), "forward": self.query_forward(zone), } return self._fw.combine_runtime_with_permanent_settings(permanent, runtime) def set_config_with_settings_dict(self, zone, settings, sender): # stupid wrappers to convert rich rule string to rich rule object from firewall.core.rich import Rich_Rule def add_rule_wrapper(zone, rule_str, timeout=0, sender=None): self.add_rule(zone, Rich_Rule(rule_str=rule_str), timeout=0, sender=sender) def remove_rule_wrapper(zone, rule_str): self.remove_rule(zone, Rich_Rule(rule_str=rule_str)) setting_to_fn = { "services": (self.add_service, self.remove_service), "ports": (self.add_port, self.remove_port), "icmp_blocks": (self.add_icmp_block, self.remove_icmp_block), "masquerade": (self.add_masquerade, self.remove_masquerade), "forward_ports": (self.add_forward_port, self.remove_forward_port), "interfaces": (self.add_interface, self.remove_interface), "sources": (self.add_source, self.remove_source), "rules_str": (add_rule_wrapper, remove_rule_wrapper), "protocols": (self.add_protocol, self.remove_protocol), "source_ports": (self.add_source_port, self.remove_source_port), "icmp_block_inversion": (self.add_icmp_block_inversion, self.remove_icmp_block_inversion), "forward": (self.add_forward, self.remove_forward), } old_settings = self.get_config_with_settings_dict(zone) (add_settings, remove_settings) = self._fw.get_added_and_removed_settings(old_settings, settings) for key in remove_settings: if isinstance(remove_settings[key], list): for args in remove_settings[key]: if isinstance(args, tuple): setting_to_fn[key][1](zone, *args) else: setting_to_fn[key][1](zone, args) else: # bool setting_to_fn[key][1](zone) for key in add_settings: if isinstance(add_settings[key], list): for args in add_settings[key]: if key in ["interfaces", "sources"]: # no timeout arg setting_to_fn[key][0](zone, args, sender=sender) else: if isinstance(args, tuple): setting_to_fn[key][0](zone, *args, timeout=0, sender=sender) else: setting_to_fn[key][0](zone, args, timeout=0, sender=sender) else: # bool if key in ["icmp_block_inversion"]: # no timeout arg setting_to_fn[key][0](zone, sender=sender) else: setting_to_fn[key][0](zone, timeout=0, sender=sender) # INTERFACES def check_interface(self, interface): self._fw.check_interface(interface) def interface_get_sender(self, zone, interface): _zone = self._fw.check_zone(zone) _obj = self._zones[_zone] interface_id = self.__interface_id(interface) if interface_id in _obj.settings["interfaces"]: settings = _obj.settings["interfaces"][interface_id] if "sender" in settings and settings["sender"] is not None: return settings["sender"] return None def __interface_id(self, interface): self.check_interface(interface) return interface def add_interface(self, zone, interface, sender=None, use_transaction=None, allow_apply=True): self._fw.check_panic() _zone = self._fw.check_zone(zone) _obj = self._zones[_zone] interface_id = self.__interface_id(interface) if interface_id in _obj.settings["interfaces"]: raise FirewallError(errors.ZONE_ALREADY_SET, "'%s' already bound to '%s'" % (interface, zone)) if self.get_zone_of_interface(interface) is not None: raise FirewallError(errors.ZONE_CONFLICT, "'%s' already bound to a zone" % interface) log.debug1("Setting zone of interface '%s' to '%s'" % (interface, _zone)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if not _obj.applied and allow_apply: self.apply_zone_settings(zone, use_transaction=transaction) transaction.add_fail(self.set_zone_applied, _zone, False) if allow_apply: self._interface(True, _zone, interface, transaction) self.__register_interface(_obj, interface_id, zone, sender) transaction.add_fail(self.__unregister_interface, _obj, interface_id) if use_transaction is None: transaction.execute(True) return _zone def __register_interface(self, _obj, interface_id, zone, sender): _obj.settings["interfaces"][interface_id] = \ self.__gen_settings(0, sender) # add information whether we add to default or specific zone _obj.settings["interfaces"][interface_id]["__default__"] = \ (not zone or zone == "") def change_zone_of_interface(self, zone, interface, sender=None): self._fw.check_panic() _old_zone = self.get_zone_of_interface(interface) _new_zone = self._fw.check_zone(zone) if _new_zone == _old_zone: return _old_zone if _old_zone is not None: self.remove_interface(_old_zone, interface) _zone = self.add_interface(zone, interface, sender) return _zone def change_default_zone(self, old_zone, new_zone, use_transaction=None): self._fw.check_panic() if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction self.apply_zone_settings(new_zone, transaction) self._interface(True, new_zone, "+", transaction, append=True) if old_zone is not None and old_zone != "": self._interface(False, old_zone, "+", transaction, append=True) if use_transaction is None: transaction.execute(True) def remove_interface(self, zone, interface, use_transaction=None): self._fw.check_panic() zoi = self.get_zone_of_interface(interface) if zoi is None: raise FirewallError(errors.UNKNOWN_INTERFACE, "'%s' is not in any zone" % interface) _zone = zoi if zone == "" else self._fw.check_zone(zone) if zoi != _zone: raise FirewallError(errors.ZONE_CONFLICT, "remove_interface(%s, %s): zoi='%s'" % \ (zone, interface, zoi)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction _obj = self._zones[_zone] interface_id = self.__interface_id(interface) transaction.add_post(self.__unregister_interface, _obj, interface_id) self._interface(False, _zone, interface, transaction) if use_transaction is None: transaction.execute(True) return _zone def __unregister_interface(self, _obj, interface_id): if interface_id in _obj.settings["interfaces"]: del _obj.settings["interfaces"][interface_id] def query_interface(self, zone, interface): return self.__interface_id(interface) in self.get_settings(zone)["interfaces"] def list_interfaces(self, zone): return self.get_settings(zone)["interfaces"].keys() # SOURCES def check_source(self, source, applied=False): if checkIPnMask(source): return "ipv4" elif checkIP6nMask(source): return "ipv6" elif check_mac(source): return "" elif source.startswith("ipset:"): self._check_ipset_type_for_source(source[6:]) if applied: self._check_ipset_applied(source[6:]) return self._ipset_family(source[6:]) else: raise FirewallError(errors.INVALID_ADDR, source) def __source_id(self, source, applied=False): ipv = self.check_source(source, applied=applied) return (ipv, source) def add_source(self, zone, source, sender=None, use_transaction=None, allow_apply=True): self._fw.check_panic() _zone = self._fw.check_zone(zone) _obj = self._zones[_zone] if check_mac(source): source = source.upper() source_id = self.__source_id(source, applied=allow_apply) if source_id in _obj.settings["sources"]: raise FirewallError(errors.ZONE_ALREADY_SET, "'%s' already bound to '%s'" % (source, _zone)) if self.get_zone_of_source(source) is not None: raise FirewallError(errors.ZONE_CONFLICT, "'%s' already bound to a zone" % source) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if not _obj.applied and allow_apply: self.apply_zone_settings(zone, use_transaction=transaction) transaction.add_fail(self.set_zone_applied, _zone, False) if allow_apply: self._source(True, _zone, source_id[0], source_id[1], transaction) self.__register_source(_obj, source_id, zone, sender) transaction.add_fail(self.__unregister_source, _obj, source_id) if use_transaction is None: transaction.execute(True) return _zone def __register_source(self, _obj, source_id, zone, sender): _obj.settings["sources"][source_id] = \ self.__gen_settings(0, sender) # add information whether we add to default or specific zone _obj.settings["sources"][source_id]["__default__"] = (not zone or zone == "") def change_zone_of_source(self, zone, source, sender=None): self._fw.check_panic() _old_zone = self.get_zone_of_source(source) _new_zone = self._fw.check_zone(zone) if _new_zone == _old_zone: return _old_zone if check_mac(source): source = source.upper() if _old_zone is not None: self.remove_source(_old_zone, source) _zone = self.add_source(zone, source, sender) return _zone def remove_source(self, zone, source, use_transaction=None): self._fw.check_panic() if check_mac(source): source = source.upper() zos = self.get_zone_of_source(source) if zos is None: raise FirewallError(errors.UNKNOWN_SOURCE, "'%s' is not in any zone" % source) _zone = zos if zone == "" else self._fw.check_zone(zone) if zos != _zone: raise FirewallError(errors.ZONE_CONFLICT, "remove_source(%s, %s): zos='%s'" % \ (zone, source, zos)) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction _obj = self._zones[_zone] source_id = self.__source_id(source) transaction.add_post(self.__unregister_source, _obj, source_id) self._source(False, _zone, source_id[0], source_id[1], transaction) if use_transaction is None: transaction.execute(True) return _zone def __unregister_source(self, _obj, source_id): if source_id in _obj.settings["sources"]: del _obj.settings["sources"][source_id] def query_source(self, zone, source): if check_mac(source): source = source.upper() return self.__source_id(source) in self.get_settings(zone)["sources"] def list_sources(self, zone): return [ k[1] for k in self.get_settings(zone)["sources"].keys() ] def _interface(self, enable, zone, interface, transaction, append=False): for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue for policy in self._zone_policies[zone]: for (table, chain) in self._fw.policy._get_table_chains_for_zone_dispatch(policy): rules = backend.build_zone_source_interface_rules(enable, zone, policy, interface, table, chain, append) transaction.add_rules(backend, rules) # intra zone forward policy = self.policy_name_from_zones(zone, "ANY") # Skip adding wildcard/catch-all interface (for default # zone). Otherwise it would allow forwarding from interface # in default zone -> interface not in default zone (but in # a different zone). if self.get_settings(zone)["forward"] and interface not in ["+", "*"]: rules = backend.build_zone_forward_rules(enable, zone, policy, "filter", interface=interface) transaction.add_rules(backend, rules) # update policy dispatch for any policy using this zone in ingress # or egress for policy in self._fw.policy.get_policies_not_derived_from_zone(): if zone not in self._fw.policy.list_ingress_zones(policy) and \ zone not in self._fw.policy.list_egress_zones(policy): continue if policy in self._fw.policy.get_active_policies_not_derived_from_zone() and self._fw.policy.get_policy(policy).applied: # first remove the old set of interfaces using the current zone # settings. if not enable and len(self.list_interfaces(zone)) == 1: self._fw.policy.unapply_policy_settings(policy, use_transaction=transaction) else: self._fw.policy._ingress_egress_zones(False, policy, transaction) # after the transaction ends and therefore the interface # has been added to the zone's settings, update the # dependent policies transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy._ingress_egress_zones_transaction(True, p), policy) elif enable: transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy.apply_policy_settings(p), policy) # IPSETS def _ipset_family(self, name): if self._ipset_type(name) == "hash:mac": return None return self._fw.ipset.get_family(name, applied=False) def _ipset_type(self, name): return self._fw.ipset.get_type(name, applied=False) def _ipset_match_flags(self, name, flag): return ",".join([flag] * self._fw.ipset.get_dimension(name)) def _check_ipset_applied(self, name): return self._fw.ipset.check_applied(name) def _check_ipset_type_for_source(self, name): _type = self._ipset_type(name) if _type not in SOURCE_IPSET_TYPES: raise FirewallError( errors.INVALID_IPSET, "ipset '%s' with type '%s' not usable as source" % \ (name, _type)) def _source(self, enable, zone, ipv, source, transaction): # For mac source bindings ipv is an empty string, the mac source will # be added for ipv4 and ipv6 for backend in [self._fw.get_backend_by_ipv(ipv)] if ipv else self._fw.enabled_backends(): if not backend.policies_supported: continue for policy in self._zone_policies[zone]: for (table, chain) in self._fw.policy._get_table_chains_for_zone_dispatch(policy): rules = backend.build_zone_source_address_rules(enable, zone, policy, source, table, chain) transaction.add_rules(backend, rules) # intra zone forward policy = self.policy_name_from_zones(zone, "ANY") if self.get_settings(zone)["forward"]: rules = backend.build_zone_forward_rules(enable, zone, policy, "filter", source=source) transaction.add_rules(backend, rules) # update policy dispatch for any policy using this zone in ingress # or egress for policy in self._fw.policy.get_policies_not_derived_from_zone(): if zone not in self._fw.policy.list_ingress_zones(policy) and \ zone not in self._fw.policy.list_egress_zones(policy): continue if policy in self._fw.policy.get_active_policies_not_derived_from_zone() and self._fw.policy.get_policy(policy).applied: # first remove the old set of sources using the current zone # settings. if not enable and len(self.list_sources(zone)) == 1: self._fw.policy.unapply_policy_settings(policy, use_transaction=transaction) else: self._fw.policy._ingress_egress_zones(False, policy, transaction) # after the transaction ends and therefore the sources # has been added to the zone's settings, update the # dependent policies transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy._ingress_egress_zones_transaction(True, p), policy) elif enable: transaction.add_post(lambda p: (p in self._fw.policy.get_active_policies_not_derived_from_zone()) and \ self._fw.policy.apply_policy_settings(p), policy) def add_service(self, zone, service, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_service(p_name, service, timeout, sender) return zone def remove_service(self, zone, service): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_service(p_name, service) return zone def query_service(self, zone, service): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_service(p_name, service) def list_services(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_services(p_name) def add_port(self, zone, port, protocol, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_port(p_name, port, protocol, timeout, sender) return zone def remove_port(self, zone, port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_port(p_name, port, protocol) return zone def query_port(self, zone, port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_port(p_name, port, protocol) def list_ports(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_ports(p_name) def add_source_port(self, zone, source_port, protocol, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_source_port(p_name, source_port, protocol, timeout, sender) return zone def remove_source_port(self, zone, source_port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_source_port(p_name, source_port, protocol) return zone def query_source_port(self, zone, source_port, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_source_port(p_name, source_port, protocol) def list_source_ports(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_source_ports(p_name) def _rich_rule_to_policies(self, zone, rule): zone = self._fw.check_zone(zone) if type(rule.action) == Rich_Mark: return [self.policy_name_from_zones(zone, "ANY")] elif type(rule.element) in [Rich_Service, Rich_Port, Rich_Protocol, Rich_SourcePort]: return [self.policy_name_from_zones(zone, "HOST")] elif type(rule.element) in [Rich_IcmpBlock, Rich_IcmpType]: return [self.policy_name_from_zones(zone, "HOST"), self.policy_name_from_zones(zone, "ANY")] elif type(rule.element) in [Rich_ForwardPort]: return [self.policy_name_from_zones(zone, "ANY")] elif type(rule.element) in [Rich_Masquerade]: return [self.policy_name_from_zones("ANY", zone)] elif rule.element is None: return [self.policy_name_from_zones(zone, "HOST")] else: raise FirewallError("Rich rule type (%s) not handled." % (type(rule.element))) def add_rule(self, zone, rule, timeout=0, sender=None): for p_name in self._rich_rule_to_policies(zone, rule): self._fw.policy.add_rule(p_name, rule, timeout, sender) return zone def remove_rule(self, zone, rule): for p_name in self._rich_rule_to_policies(zone, rule): self._fw.policy.remove_rule(p_name, rule) return zone def query_rule(self, zone, rule): ret = True for p_name in self._rich_rule_to_policies(zone, rule): ret = ret and self._fw.policy.query_rule(p_name, rule) return ret def list_rules(self, zone): zone = self._fw.check_zone(zone) ret = set() for p_name in [self.policy_name_from_zones(zone, "ANY"), self.policy_name_from_zones(zone, "HOST"), self.policy_name_from_zones("ANY", zone)]: ret.update(set(self._fw.policy.list_rules(p_name))) return list(ret) def add_protocol(self, zone, protocol, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_protocol(p_name, protocol, timeout, sender) return zone def remove_protocol(self, zone, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_protocol(p_name, protocol) return zone def query_protocol(self, zone, protocol): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.query_protocol(p_name, protocol) def list_protocols(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") return self._fw.policy.list_protocols(p_name) def add_masquerade(self, zone, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones("ANY", zone) self._fw.policy.add_masquerade(p_name, timeout, sender) return zone def remove_masquerade(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones("ANY", zone) self._fw.policy.remove_masquerade(p_name) return zone def query_masquerade(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones("ANY", zone) return self._fw.policy.query_masquerade(p_name) def add_forward_port(self, zone, port, protocol, toport=None, toaddr=None, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.add_forward_port(p_name, port, protocol, toport, toaddr, timeout, sender) return zone def remove_forward_port(self, zone, port, protocol, toport=None, toaddr=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.remove_forward_port(p_name, port, protocol, toport, toaddr) return zone def query_forward_port(self, zone, port, protocol, toport=None, toaddr=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.query_forward_port(p_name, port, protocol, toport, toaddr) def list_forward_ports(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.list_forward_ports(p_name) def add_icmp_block(self, zone, icmp, timeout=0, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_icmp_block(p_name, icmp, timeout, sender) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.add_icmp_block(p_name, icmp, timeout, sender) return zone def remove_icmp_block(self, zone, icmp): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_icmp_block(p_name, icmp) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.remove_icmp_block(p_name, icmp) return zone def query_icmp_block(self, zone, icmp): zone = self._fw.check_zone(zone) p_name_host = self.policy_name_from_zones(zone, "HOST") p_name_fwd = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.query_icmp_block(p_name_host, icmp) and \ self._fw.policy.query_icmp_block(p_name_fwd, icmp) def list_icmp_blocks(self, zone): zone = self._fw.check_zone(zone) p_name_host = self.policy_name_from_zones(zone, "HOST") p_name_fwd = self.policy_name_from_zones(zone, "ANY") return sorted(set(self._fw.policy.list_icmp_blocks(p_name_host) + self._fw.policy.list_icmp_blocks(p_name_fwd))) def add_icmp_block_inversion(self, zone, sender=None): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.add_icmp_block_inversion(p_name, sender) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.add_icmp_block_inversion(p_name, sender) return zone def _icmp_block_inversion(self, enable, zone, transaction): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy._icmp_block_inversion(enable, p_name, transaction) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy._icmp_block_inversion(enable, p_name, transaction) def remove_icmp_block_inversion(self, zone): zone = self._fw.check_zone(zone) p_name = self.policy_name_from_zones(zone, "HOST") self._fw.policy.remove_icmp_block_inversion(p_name) p_name = self.policy_name_from_zones(zone, "ANY") self._fw.policy.remove_icmp_block_inversion(p_name) return zone def query_icmp_block_inversion(self, zone): zone = self._fw.check_zone(zone) p_name_host = self.policy_name_from_zones(zone, "HOST") p_name_fwd = self.policy_name_from_zones(zone, "ANY") return self._fw.policy.query_icmp_block_inversion(p_name_host) and \ self._fw.policy.query_icmp_block_inversion(p_name_fwd) def _forward(self, enable, zone, transaction): p_name = self.policy_name_from_zones(zone, "ANY") for interface in self._zones[zone].settings["interfaces"]: for backend in self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_zone_forward_rules(enable, zone, p_name, "filter", interface=interface) transaction.add_rules(backend, rules) for ipv,source in self._zones[zone].settings["sources"]: for backend in [self._fw.get_backend_by_ipv(ipv)] if ipv else self._fw.enabled_backends(): if not backend.policies_supported: continue rules = backend.build_zone_forward_rules(enable, zone, p_name, "filter", source=source) transaction.add_rules(backend, rules) def __forward_id(self): return True def add_forward(self, zone, timeout=0, sender=None, use_transaction=None): _zone = self._fw.check_zone(zone) self._fw.check_timeout(timeout) self._fw.check_panic() _obj = self._zones[_zone] forward_id = self.__forward_id() if forward_id in _obj.settings["forward"]: raise FirewallError(errors.ALREADY_ENABLED, "forward already enabled in '%s'" % _zone) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._forward(True, _zone, transaction) self.__register_forward(_obj, forward_id, timeout, sender) transaction.add_fail(self.__unregister_forward, _obj, forward_id) if use_transaction is None: transaction.execute(True) return _zone def __register_forward(self, _obj, forward_id, timeout, sender): _obj.settings["forward"][forward_id] = \ self.__gen_settings(timeout, sender) def remove_forward(self, zone, use_transaction=None): _zone = self._fw.check_zone(zone) self._fw.check_panic() _obj = self._zones[_zone] forward_id = self.__forward_id() if forward_id not in _obj.settings["forward"]: raise FirewallError(errors.NOT_ENABLED, "forward not enabled in '%s'" % _zone) if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction if _obj.applied: self._forward(False, _zone, transaction) transaction.add_post(self.__unregister_forward, _obj, forward_id) if use_transaction is None: transaction.execute(True) return _zone def __unregister_forward(self, _obj, forward_id): if forward_id in _obj.settings["forward"]: del _obj.settings["forward"][forward_id] def query_forward(self, zone): return self.__forward_id() in self.get_settings(zone)["forward"]