def apply_default_rules(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction for ipv in [ "ipv4", "ipv6", "eb" ]: self.__apply_default_rules(ipv, transaction) if self.ipv6_rpfilter_enabled and \ "raw" in self.get_available_tables("ipv6"): # Execute existing transaction transaction.execute(True) # Start new transaction transaction.clear() self.ip6tables_backend.apply_rpfilter_rules(transaction, self._log_denied) # Execute ipv6_rpfilter transaction, it might fail try: transaction.execute(True) except FirewallError as msg: log.warning("Applying rules for ipv6_rpfilter failed: %s", msg) # Start new transaction transaction.clear() else: if use_transaction is None: transaction.execute(True)
def read(self): self.clear() try: f = open(self.filename, "r") except Exception as msg: log.error("Failed to load '%s': %s", self.filename, msg) raise for line in f: if not line: break line = line.strip() if len(line) < 1 or line[0] in ['#', ';']: continue # get key/value pair pair = [x.strip() for x in line.split("=", 1)] if len(pair) != 2: continue if len(pair[1]) >= 2 and \ pair[1].startswith('"') and pair[1].endswith('"'): pair[1] = pair[1][1:-1] if pair[1] == '': continue elif self._config.get(pair[0]) is not None: log.warning("%s: Duplicate option definition: '%s'", self.filename, line.strip()) continue self._config[pair[0]] = pair[1] f.close()
def ipset_reader(filename, path): ipset = IPSet() if not filename.endswith(".xml"): raise FirewallError(INVALID_NAME, "'%s' is missing .xml suffix" % filename) ipset.name = filename[:-4] ipset.check_name(ipset.name) ipset.filename = filename ipset.path = path ipset.builtin = False if path.startswith(ETC_FIREWALLD) else True ipset.default = ipset.builtin handler = ipset_ContentHandler(ipset) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "r") as f: parser.parse(f) del handler del parser if "timeout" in ipset.options: # no entries visible for ipsets with timeout log.warning("ipset '%s' uses timeout, entries are removed" % ipset.name) del ipset.entries[:] if PY2: ipset.encode_strings() return ipset
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name) self.item.parser_check_element_attrs(name, attrs) if name == "service": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "port": if attrs["port"] != "": self.item.ports.append((attrs["port"], attrs["protocol"])) else: self.item.protocols.append(attrs["protocol"]) elif name == "protocol": self.item.protocols.append(attrs["value"]) elif name == "destination": for x in [ "ipv4", "ipv6" ]: if x in attrs: if not check_address(x, attrs[x]): raise FirewallError(INVALID_ADDR, "'%s' is not valid %s address" % (attrs[x], x)) self.item.destination[x] = attrs[x] elif name == "module": self.item.modules.append(attrs["name"])
def ipset_reader(filename, path): ipset = IPSet() if not filename.endswith(".xml"): raise FirewallError(INVALID_NAME, "'%s' is missing .xml suffix" % filename) ipset.name = filename[:-4] ipset.check_name(ipset.name) ipset.filename = filename ipset.path = path ipset.default = False if path.startswith(ETC_FIREWALLD) else True handler = ipset_ContentHandler(ipset) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "r") as f: parser.parse(f) del handler del parser if "timeout" in ipset.options: # no entries visible for ipsets with timeout log.warning("ipset '%s' uses timeout, entries are removed" % ipset.name) del ipset.entries[:] if PY2: ipset.encode_strings() return ipset
def apply_default_rules(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction for backend in self.enabled_backends(): rules = backend.build_default_rules(self._log_denied) transaction.add_rules(backend, rules) ipv6_backend = self.get_backend_by_ipv("ipv6") if self.ipv6_rpfilter_enabled and \ "raw" in ipv6_backend.get_available_tables(): # Execute existing transaction transaction.execute(True) # Start new transaction transaction.clear() rules = ipv6_backend.build_rpfilter_rules(self._log_denied) transaction.add_rules(ipv6_backend, rules) # Execute ipv6_rpfilter transaction, it might fail try: transaction.execute(True) except FirewallError as msg: log.warning("Applying rules for ipv6_rpfilter failed: %s", msg) # Start new transaction transaction.clear() else: if use_transaction is None: transaction.execute(True)
def _error2warning(self, f, name, *args): # transform errors into warnings try: f(name, *args) except FirewallError as error: msg = str(error) log.warning("%s: %s" % (name, msg))
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "helper": if "version" in attrs: self.item.version = attrs["version"] if "family" in attrs: self.item.check_ipv(attrs["family"]) self.item.family = attrs["family"] if "module" in attrs: if not attrs["module"].startswith("nf_conntrack_"): raise FirewallError( errors.INVALID_MODULE, "'%s' does not start with 'nf_conntrack_'" % \ attrs["module"]) if len(attrs["module"].replace("nf_conntrack_", "")) < 1: raise FirewallError( errors.INVALID_MODULE, "Module name '%s' too short" % attrs["module"]) self.item.module = attrs["module"] elif name == "short": pass elif name == "description": pass elif name == "port": check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) entry = (attrs["port"], attrs["protocol"]) if entry not in self.item.ports: self.item.ports.append(entry) else: log.warning("Port '%s/%s' already set, ignoring.", attrs["port"], attrs["protocol"])
def add_icmptype(self, obj): orig_ipvs = obj.destination if len(orig_ipvs) == 0: orig_ipvs = ["ipv4", "ipv6"] ipvs = orig_ipvs[:] for ipv in orig_ipvs: if ipv == "ipv4": if not self._fw.ip4tables_enabled: continue supported_icmps = self._fw.ip4tables_supported_icmp_types elif ipv == "ipv6": if not self._fw.ip6tables_enabled: continue supported_icmps = self._fw.ip6tables_supported_icmp_types else: supported_icmps = [] if obj.name.lower() not in supported_icmps: log.warning( "ICMP type '%s' is not supported by the kernel for %s." % (obj.name, ipv)) ipvs.remove(ipv) if len(ipvs) != len(orig_ipvs): if len(ipvs) < 1: raise FirewallError(errors.INVALID_ICMPTYPE, "No supported ICMP type.") new_obj = copy.deepcopy(obj) new_obj.destination = ipvs self._icmptypes[obj.name] = new_obj else: self._icmptypes[obj.name] = obj
def read(self): self.clear() try: f = open(self.filename, "r") except Exception as msg: log.error("Failed to load '%s': %s", self.filename, msg) raise for line in f: if not line: break line = line.strip() if len(line) < 1 or line[0] in ['#', ';']: continue # get key/value pair pair = [ x.strip() for x in line.split("=", 1) ] if len(pair) != 2: log.warning("%: Invalid option definition: '%s'", self.filename, line.strip()) continue elif pair[1] == '': continue elif self._config.get(pair[0]) is not None: log.warning("%s: Duplicate option definition: '%s'", self.filename, line.strip()) continue self._config[pair[0]] = pair[1] f.close()
def endElement(self, name): IO_Object_ContentHandler.endElement(self, name) if name == "entry": if self._element not in self.item.entries: self.item.entries.append(self._element) else: log.warning("Entry %s already set, ignoring.", self._element)
def apply_default_rules(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction for ipv in self.enabled_backends(): self.__apply_default_rules(ipv, transaction) if self.ipv6_rpfilter_enabled and \ "raw" in self.get_ipv_backend("ipv6").get_available_tables(): # Execute existing transaction transaction.execute(True) # Start new transaction transaction.clear() self.ip6tables_backend.apply_rpfilter_rules(transaction, self._log_denied) # Execute ipv6_rpfilter transaction, it might fail try: transaction.execute(True) except FirewallError as msg: log.warning("Applying rules for ipv6_rpfilter failed: %s", msg) # Start new transaction transaction.clear() else: if use_transaction is None: transaction.execute(True)
def startElement(self, name, attrs): self.item.parser_check_element_attrs(name, attrs) if name == "service": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = str(attrs["version"]) elif name == "short": pass elif name == "description": pass elif name == "port": self.item.ports.append((str(attrs["port"]), str(attrs["protocol"]))) elif name == "destination": for x in [ "ipv4", "ipv6" ]: if x in attrs: s = str(attrs[x]) if x == "ipv4" and not functions.checkIPnMask(s): raise FirewallError(INVALID_DESTINATION, s) if x == "ipv6" and not functions.checkIP6nMask(s): raise FirewallError(INVALID_DESTINATION, s) self.item.destination[x] = str(attrs[x]) elif name == "module": self.item.modules.append(str(attrs["name"]))
def startElement(self, name, attrs): self.item.parser_check_element_attrs(name, attrs) if name == "service": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = str(attrs["version"]) elif name == "short": pass elif name == "description": pass elif name == "port": self.item.ports.append( (str(attrs["port"]), str(attrs["protocol"]))) elif name == "destination": for x in ["ipv4", "ipv6"]: if x in attrs: s = str(attrs[x]) if x == "ipv4" and not functions.checkIPnMask(s): raise FirewallError(INVALID_DESTINATION, s) if x == "ipv6" and not functions.checkIP6nMask(s): raise FirewallError(INVALID_DESTINATION, s) self.item.destination[x] = str(attrs[x]) elif name == "module": self.item.modules.append(str(attrs["name"]))
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name) self.item.parser_check_element_attrs(name, attrs) if name == "service": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "port": self.item.ports.append((attrs["port"], attrs["protocol"])) elif name == "destination": for x in ["ipv4", "ipv6"]: if x in attrs: if not check_address(x, attrs[x]): raise FirewallError( INVALID_ADDR, "'%s' is not valid %s address" % (attrs[x], x)) self.item.destination[x] = attrs[x] elif name == "module": self.item.modules.append(attrs["name"])
def add_passthrough(self, ipv, args): self._check_ipv(ipv) if ipv not in self.passthroughs: self.passthroughs[ipv] = [ ] if args not in self.passthroughs[ipv]: self.passthroughs[ipv].append(args) else: log.warning("Passthrough '%s' for ipv '%s'" % \ ("',".join(args), ipv) + "already in list, ignoring")
def add_passthrough(self, ipv, args): self._check_ipv(ipv) if ipv not in self.passthroughs: self.passthroughs[ipv] = [] if args not in self.passthroughs[ipv]: self.passthroughs[ipv].append(args) else: log.warning("Passthrough '%s' for ipv '%s'" % \ ("',".join(args), ipv) + "already in list, ignoring")
def add_chain(self, ipv, table, chain): self._check_ipv_table(ipv, table) key = (ipv, table) if key not in self.chains: self.chains[key] = [] if chain not in self.chains[key]: self.chains[key].append(chain) else: log.warning("Chain '%s' for table '%s' with ipv '%s' " % \ (chain, table, ipv) + "already in list, ignoring")
def add_chain(self, ipv, table, chain): self._check_ipv_table(ipv, table) key = (ipv, table) if key not in self.chains: self.chains[key] = [ ] if chain not in self.chains[key]: self.chains[key].append(chain) else: log.warning("Chain '%s' for table '%s' with ipv '%s' " % \ (chain, table, ipv) + "already in list, ignoring")
def __remove_dangling_lock(self): if os.path.exists(self.ebtables_lock): ret = runProg("pidof", ["-s", "ebtables"]) ret2 = runProg("pidof", ["-s", "ebtables-restore"]) if ret[1] == "" and ret2[1] == "": log.warning("Removing dangling ebtables lock file: '%s'" % self.ebtables_lock) try: os.unlink(self.ebtables_lock) except OSError as e: if e.errno != errno.ENOENT: raise
def available_tables(self, table=None): ret = [] tables = [table] if table else CHAINS.keys() for table in tables: try: self.__run(["-t", table, "-L"]) ret.append(table) except ValueError: log.warning("ebtables table '%s' does not exist." % table) return ret
def available_tables(self, table=None): ret = [] tables = [ table ] if table else CHAINS.keys() for table in tables: try: self.__run(["-t", table, "-L"]) ret.append(table) except ValueError: log.warning("ebtables table '%s' does not exist." % table) return ret
def __remove_dangling_lock(self): if os.path.exists(self.ebtables_lock): (status, ret) = runProg("pidof", ["-s", "ebtables"]) if ret == "": log.warning("Removing dangling ebtables lock file: '%s'" % self.ebtables_lock) try: os.unlink(self.ebtables_lock) except OSError as e: if e.errno != errno.ENOENT: raise
def add_rule(self, ipv, table, chain, priority, args): key = (ipv, table, chain) if key not in self.rules: self.rules[key] = LastUpdatedOrderedDict() value = (priority, tuple(args)) if value not in self.rules[key]: self.rules[key][value] = priority else: log.warning("Rule '%s' for table '%s' and chain '%s' " % \ ("',".join(args), table, chain) + "with ipv '%s' and priority %d " % (ipv, priority) + "already in list, ignoring")
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "ipset": if "type" in attrs: if attrs["type"] not in IPSET_TYPES: raise FirewallError(errors.INVALID_TYPE, "%s" % attrs["type"]) self.item.type = attrs["type"] if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "option": value = "" if "value" in attrs: value = attrs["value"] if attrs["name"] not in \ [ "family", "timeout", "hashsize", "maxelem" ]: raise FirewallError( errors.INVALID_OPTION, "Unknown option '%s'" % attrs["name"]) if self.item.type == "hash:mac" and attrs["name"] in [ "family" ]: raise FirewallError( errors.INVALID_OPTION, "Unsupported option '%s' for type '%s'" % \ (attrs["name"], self.item.type)) if attrs["name"] in [ "family", "timeout", "hashsize", "maxelem" ] \ and not value: raise FirewallError( errors.INVALID_OPTION, "Missing mandatory value of option '%s'" % attrs["name"]) if attrs["name"] in [ "timeout", "hashsize", "maxelem" ]: try: int_value = int(value) except ValueError: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is not an integer" % \ (attrs["name"], value)) if int_value < 0: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is negative" % \ (attrs["name"], value)) if attrs["name"] == "family" and value not in [ "inet", "inet6" ]: raise FirewallError(errors.INVALID_FAMILY, value) if attrs["name"] not in self.item.options: self.item.options[attrs["name"]] = value else: log.warning("Option %s already set, ignoring.", attrs["name"])
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "ipset": if "type" in attrs: self.item.type = attrs["type"] if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "option": value = "" if "value" in attrs: value = attrs["value"] if attrs["name"] not in \ [ "family", "timeout", "hashsize", "maxelem" ]: raise FirewallError( errors.INVALID_OPTION, "Unknown option '%s'" % attrs["name"]) if self.item.type == "hash:mac" and attrs["name"] in [ "family" ]: raise FirewallError( errors.INVALID_OPTION, "Unsupported option '%s' for type '%s'" % \ (attrs["name"], self.item.type)) if attrs["name"] in [ "family", "timeout", "hashsize", "maxelem" ] \ and not value: raise FirewallError( errors.INVALID_OPTION, "Missing mandatory value of option '%s'" % attrs["name"]) if attrs["name"] in [ "timeout", "hashsize", "maxelem" ]: try: int_value = int(value) except ValueError: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is not an integer" % \ (attrs["name"], value)) if int_value < 0: raise FirewallError( errors.INVALID_VALUE, "Option '%s': Value '%s' is negative" % \ (attrs["name"], value)) if attrs["name"] == "family" and value not in [ "inet", "inet6" ]: raise FirewallError(errors.INVALID_FAMILY, value) if attrs["name"] not in self.item.options: self.item.options[attrs["name"]] = value else: log.warning("Option %s already set, ignoring.", attrs["name"])
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name, attrs) self.item.parser_check_element_attrs(name, attrs) if name == "icmptype": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "destination": for x in ["ipv4", "ipv6"]: if x in attrs and attrs[x].lower() in ["yes", "true"]: self.item.destination.append(str(x))
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 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 _check_tables(self): # check if iptables, ip6tables and ebtables are usable, else disable if "filter" not in ipXtables.ip4tables_available_tables: log.warning("iptables not usable, disabling IPv4 firewall.") self.ip4tables_enabled = False if "filter" not in ipXtables.ip6tables_available_tables: log.warning("ip6tables not usable, disabling IPv6 firewall.") self.ip6tables_enabled = False if "filter" not in ebtables.ebtables_available_tables: log.error("ebtables not usable, disabling ethernet bridge firewall.") self.ebtables_enabled = False if not self.ip4tables_enabled and not self.ip6tables_enabled: log.fatal("No IPv4 and IPv6 firewall.") sys.exit(1)
def apply_default_rules(self, use_transaction=None): if use_transaction is None: transaction = FirewallTransaction(self) else: transaction = use_transaction for ipv in [ "ipv4", "ipv6", "eb" ]: self.__apply_default_rules(ipv, transaction) if self.ipv6_rpfilter_enabled and \ "raw" in self.get_available_tables("ipv6"): # Execute existing transaction transaction.execute(True) # Start new transaction transaction.clear() # here is no check for ebtables.restore_noflush_option needed # as ebtables is not used in here transaction.add_rule("ipv6", [ "-I", "PREROUTING", "1", "-t", "raw", "-p", "ipv6-icmp", "--icmpv6-type=router-advertisement", "-j", "ACCEPT" ]) # RHBZ#1058505 transaction.add_rule("ipv6", [ "-I", "PREROUTING", "2", "-t", "raw", "-m", "rpfilter", "--invert", "-j", "DROP" ]) if self._log_denied != "off": transaction.add_rule("ipv6", [ "-I", "PREROUTING", "2", "-t", "raw", "-m", "rpfilter", "--invert", "-j", "LOG", "--log-prefix", "rpfilter_DROP: " ]) # Execute ipv6_rpfilter transaction, it might fail try: transaction.execute(True) except FirewallError as msg: log.warning("Applying rules for ipv6_rpfilter failed: %s", msg) # Start new transaction transaction.clear() else: if use_transaction is None: transaction.execute(True)
def common_endElement(obj, name): if name == "rule": if not obj._rule_error: try: obj._rule.check() except Exception as e: log.warning("%s: %s", e, str(obj._rule)) else: if str(obj._rule) not in obj.item.rules_str: obj.item.rules.append(obj._rule) obj.item.rules_str.append(str(obj._rule)) else: log.warning("Rule '%s' already set, ignoring.", str(obj._rule)) obj._rule = None obj._rule_error = False elif name in ["accept", "reject", "drop", "mark", "log", "audit"]: obj._limit_ok = None
def startElement(self, name, attrs): self.item.parser_check_element_attrs(name, attrs) if name == "icmptype": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = str(attrs["version"]) elif name == "short": pass elif name == "description": pass elif name == "destination": for x in ["ipv4", "ipv6"]: if x in attrs and \ attrs[x].lower() in [ "yes", "true" ]: self.item.destination.append(str(x))
def _check_tables(self): # check if iptables, ip6tables and ebtables are usable, else disable if "filter" not in ipXtables.ip4tables_available_tables: log.warning("iptables not usable, disabling IPv4 firewall.") self.ip4tables_enabled = False if "filter" not in ipXtables.ip6tables_available_tables: log.warning("ip6tables not usable, disabling IPv6 firewall.") self.ip6tables_enabled = False if "filter" not in ebtables.ebtables_available_tables: log.error( "ebtables not usable, disabling ethernet bridge firewall.") self.ebtables_enabled = False if not self.ip4tables_enabled and not self.ip6tables_enabled: log.fatal("No IPv4 and IPv6 firewall.") sys.exit(1)
def endElement(self, name): IO_Object_ContentHandler.endElement(self, name) if name == "rule": if not self._rule_error: try: self._rule.check() except Exception as e: log.warning("%s: %s", e, str(self._rule)) else: if str(self._rule) not in [str(x) for x in self.item.rules]: self.item.rules.append(self._rule) else: log.warning("Rule '%s' already set, ignoring.", str(self._rule)) self._rule = None self._rule_error = False elif name in ["accept", "reject", "drop", "mark", "log", "audit"]: self._limit_ok = None
def set_config(self, config): (_chains, _rules) = config for table_id in _chains: (ipv, table) = table_id for chain in _chains[table_id]: if not self.query_chain(ipv, table, chain): try: self.add_chain(ipv, table, chain) except FirewallError as error: log.warning(str(error)) for chain_id in _rules: (ipv, table, chain) = chain_id for (priority, args) in _rules[chain_id]: if not self.query_rule(ipv, table, chain, priority, args): try: self.add_rule(ipv, table, chain, priority, args) except FirewallError as error: log.warning(str(error))
def ipset_reader(filename, path): ipset = IPSet() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % filename) ipset.name = filename[:-4] ipset.check_name(ipset.name) ipset.filename = filename ipset.path = path ipset.builtin = False if path.startswith(ETC_FIREWALLD) else True ipset.default = ipset.builtin handler = ipset_ContentHandler(ipset) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "r") as f: try: parser.parse(f) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_IPSET, "not a valid ipset file: %s" % \ msg.getException()) del handler del parser if "timeout" in ipset.options and len(ipset.entries) > 0: # no entries visible for ipsets with timeout log.warning("ipset '%s': timeout option is set, entries are ignored", ipset.name) del ipset.entries[:] i = 0 while i < len(ipset.entries): try: ipset.check_entry(ipset.entries[i], ipset.options, ipset.type) except FirewallError as e: log.warning("%s, ignoring.", e) ipset.entries.pop(i) else: i += 1 if PY2: ipset.encode_strings() return ipset
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 _impl(*args, **kwargs): try: return func(*args, **kwargs) except FirewallError as error: code = FirewallError.get_code(str(error)) if code in [ errors.ALREADY_ENABLED, errors.NOT_ENABLED, errors.ZONE_ALREADY_SET, errors.ALREADY_SET ]: log.warning(str(error)) else: log.debug1(traceback.format_exc()) log.error(str(error)) raise FirewallDBusException(str(error)) except DBusException as ex: # only log DBusExceptions once raise ex except Exception as ex: log.exception() raise FirewallDBusException(str(ex))
def endElement(self, name): IO_Object_ContentHandler.endElement(self, name) if name == "rule": if not self._rule_error: try: self._rule.check() except Exception as e: log.warning("%s: %s", e, str(self._rule)) else: if str(self._rule) not in \ [ str(x) for x in self.item.rules ]: self.item.rules.append(self._rule) else: log.warning("Rule '%s' already set, ignoring.", str(self._rule)) self._rule = None self._rule_error = False elif name in ["accept", "reject", "drop", "mark", "log", "audit"]: self._limit_ok = None
def dbus_handle_exceptions(func, *args, **kwargs): """Decorator to handle exceptions, log and report them into D-Bus :Raises DBusException: on a firewall error code problems. """ try: return func(*args, **kwargs) except FirewallError as error: code = FirewallError.get_code(str(error)) if code in [ errors.ALREADY_ENABLED, errors.NOT_ENABLED, errors.ZONE_ALREADY_SET, errors.ALREADY_SET ]: log.warning(str(error)) else: log.error(str(error)) raise FirewallDBusException(str(error)) except DBusException as ex: # only log DBusExceptions once raise ex except Exception as ex: log.exception() raise FirewallDBusException(str(ex))
def _check_tables(self): # check if iptables, ip6tables and ebtables are usable, else disable if self.ip4tables_enabled and \ "filter" not in self.get_available_tables("ipv4"): log.warning("iptables not usable, disabling IPv4 firewall.") self.ip4tables_enabled = False if self.ip6tables_enabled and \ "filter" not in self.get_available_tables("ipv6"): log.warning("ip6tables not usable, disabling IPv6 firewall.") self.ip6tables_enabled = False if self.ebtables_enabled and \ "filter" not in self.get_available_tables("eb"): log.error("ebtables not usable, disabling ethernet bridge firewall.") self.ebtables_enabled = False # is there at least support for ipv4 or ipv6 if not self.ip4tables_enabled and not self.ip6tables_enabled: log.fatal("No IPv4 and IPv6 firewall.") sys.exit(1)
def set_config(self, conf, use_transaction=None): if use_transaction is None: transaction = self.new_transaction() else: transaction = use_transaction (_chains, _rules, _passthroughs) = conf for table_id in _chains: (ipv, table) = table_id for chain in _chains[table_id]: if not self.query_chain(ipv, table, chain): try: self.add_chain(ipv, table, chain, use_transaction=transaction) except FirewallError as error: log.warning(str(error)) for chain_id in _rules: (ipv, table, chain) = chain_id for (priority, args) in _rules[chain_id]: if not self.query_rule(ipv, table, chain, priority, args): try: self.add_rule(ipv, table, chain, priority, args, use_transaction=transaction) except FirewallError as error: log.warning(str(error)) for ipv in _passthroughs: for args in _passthroughs[ipv]: if not self.query_passthrough(ipv, args): try: self.add_passthrough(ipv, args, use_transaction=transaction) except FirewallError as error: log.warning(str(error)) if use_transaction is None: transaction.execute(True)
def _check_tables(self): # check if iptables, ip6tables and ebtables are usable, else disable if self.ip4tables_enabled and \ "filter" not in self.get_available_tables("ipv4"): log.warning("iptables not usable, disabling IPv4 firewall.") self.ip4tables_enabled = False if self.ip6tables_enabled and \ "filter" not in self.get_available_tables("ipv6"): log.warning("ip6tables not usable, disabling IPv6 firewall.") self.ip6tables_enabled = False if self.ebtables_enabled and \ "filter" not in self.get_available_tables("eb"): log.error( "ebtables not usable, disabling ethernet bridge firewall.") self.ebtables_enabled = False # is there at least support for ipv4 or ipv6 if not self.ip4tables_enabled and not self.ip6tables_enabled: log.fatal("No IPv4 and IPv6 firewall.") sys.exit(1)
def _zone_settings(self, enable, zone, transaction): for key in ["interfaces", "sources", "forward", "icmp_block_inversion"]: args_list = getattr(self.get_zone(zone), key) if isinstance(args_list, bool): args_list = [args_list] for args in args_list: if key == "interfaces": self._interface(enable, zone, args, transaction) elif key == "sources": ipv = self.check_source(args) self._source(enable, zone, ipv, args, 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 ipset_reader(filename, path): ipset = IPSet() if not filename.endswith(".xml"): raise FirewallError(errors.INVALID_NAME, "'%s' is missing .xml suffix" % filename) ipset.name = filename[:-4] ipset.check_name(ipset.name) ipset.filename = filename ipset.path = path ipset.builtin = False if path.startswith(config.ETC_FIREWALLD) else True ipset.default = ipset.builtin handler = ipset_ContentHandler(ipset) parser = sax.make_parser() parser.setContentHandler(handler) name = "%s/%s" % (path, filename) with open(name, "rb") as f: source = sax.InputSource(None) source.setByteStream(f) try: parser.parse(source) except sax.SAXParseException as msg: raise FirewallError(errors.INVALID_IPSET, "not a valid ipset file: %s" % \ msg.getException()) del handler del parser if "timeout" in ipset.options and ipset.options["timeout"] != "0" and \ len(ipset.entries) > 0: # no entries visible for ipsets with timeout log.warning("ipset '%s': timeout option is set, entries are ignored", ipset.name) del ipset.entries[:] i = 0 entries_set = set() while i < len(ipset.entries): if ipset.entries[i] in entries_set: log.warning("Entry %s already set, ignoring.", ipset.entries[i]) ipset.entries.pop(i) else: try: ipset.check_entry(ipset.entries[i], ipset.options, ipset.type) except FirewallError as e: log.warning("%s, ignoring.", e) ipset.entries.pop(i) else: entries_set.add(ipset.entries[i]) i += 1 del entries_set return ipset
def zone_writer(zone, path=None): _path = path if path else zone.path if zone.filename: name = "%s/%s" % (_path, zone.filename) else: name = "%s/%s.xml" % (_path, zone.name) if os.path.exists(name): try: shutil.copy2(name, "%s.old" % name) except Exception as msg: raise IOError("Backup of '%s' failed: %s" % (name, msg)) dirpath = os.path.dirname(name) if dirpath.startswith(ETC_FIREWALLD) and not os.path.exists(dirpath): if not os.path.exists(ETC_FIREWALLD): os.mkdir(ETC_FIREWALLD, 0o750) os.mkdir(dirpath, 0o750) f = io.open(name, mode='wt', encoding='UTF-8') handler = IO_Object_XMLGenerator(f) handler.startDocument() # start zone element attrs = {} if zone.version and zone.version != "": attrs["version"] = zone.version if zone.target != DEFAULT_ZONE_TARGET: attrs["target"] = zone.target handler.startElement("zone", attrs) handler.ignorableWhitespace("\n") # short if zone.short and zone.short != "": handler.ignorableWhitespace(" ") handler.startElement("short", { }) handler.characters(zone.short) handler.endElement("short") handler.ignorableWhitespace("\n") # description if zone.description and zone.description != "": handler.ignorableWhitespace(" ") handler.startElement("description", { }) handler.characters(zone.description) handler.endElement("description") handler.ignorableWhitespace("\n") # interfaces for interface in uniqify(zone.interfaces): handler.ignorableWhitespace(" ") handler.simpleElement("interface", { "name": interface }) handler.ignorableWhitespace("\n") # source for source in uniqify(zone.sources): handler.ignorableWhitespace(" ") if "ipset:" in source: handler.simpleElement("source", { "ipset": source[6:] }) else: handler.simpleElement("source", { "address": source }) handler.ignorableWhitespace("\n") # services for service in uniqify(zone.services): handler.ignorableWhitespace(" ") handler.simpleElement("service", { "name": service }) handler.ignorableWhitespace("\n") # ports for port in uniqify(zone.ports): handler.ignorableWhitespace(" ") handler.simpleElement("port", { "port": port[0], "protocol": port[1] }) handler.ignorableWhitespace("\n") # protocols for protocol in uniqify(zone.protocols): handler.ignorableWhitespace(" ") handler.simpleElement("protocol", { "value": protocol }) handler.ignorableWhitespace("\n") # icmp-blocks for icmp in uniqify(zone.icmp_blocks): handler.ignorableWhitespace(" ") handler.simpleElement("icmp-block", { "name": icmp }) handler.ignorableWhitespace("\n") # masquerade if zone.masquerade: handler.ignorableWhitespace(" ") handler.simpleElement("masquerade", { }) handler.ignorableWhitespace("\n") # forward-ports for forward in uniqify(zone.forward_ports): handler.ignorableWhitespace(" ") attrs = { "port": forward[0], "protocol": forward[1] } if forward[2] and forward[2] != "" : attrs["to-port"] = forward[2] if forward[3] and forward[3] != "" : attrs["to-addr"] = forward[3] handler.simpleElement("forward-port", attrs) handler.ignorableWhitespace("\n") # rules for rule in zone.rules: attrs = { } if rule.family: attrs["family"] = rule.family handler.ignorableWhitespace(" ") handler.startElement("rule", attrs) handler.ignorableWhitespace("\n") # source if rule.source: attrs = { } if rule.source.addr: attrs["address"] = rule.source.addr if rule.source.mac: attrs["mac"] = rule.source.mac if rule.source.ipset: attrs["ipset"] = rule.source.ipset if rule.source.invert: attrs["invert"] = "True" handler.ignorableWhitespace(" ") handler.simpleElement("source", attrs) handler.ignorableWhitespace("\n") # destination if rule.destination: attrs = { "address": rule.destination.addr } if rule.destination.invert: attrs["invert"] = "True" handler.ignorableWhitespace(" ") handler.simpleElement("destination", attrs) handler.ignorableWhitespace("\n") # element if rule.element: element = "" attrs = { } if type(rule.element) == Rich_Service: element = "service" attrs["name"] = rule.element.name elif type(rule.element) == Rich_Port: element = "port" attrs["port"] = rule.element.port attrs["protocol"] = rule.element.protocol elif type(rule.element) == Rich_Protocol: element = "protocol" attrs["value"] = rule.element.value elif type(rule.element) == Rich_Masquerade: element = "masquerade" elif type(rule.element) == Rich_IcmpBlock: element = "icmp-block" attrs["name"] = rule.element.name elif type(rule.element) == Rich_ForwardPort: element = "forward-port" attrs["port"] = rule.element.port attrs["protocol"] = rule.element.protocol if rule.element.to_port != "": attrs["to-port"] = rule.element.to_port if rule.element.to_address != "": attrs["to-addr"] = rule.element.to_address else: log.warning("Unknown element '%s'", type(rule.element)) handler.ignorableWhitespace(" ") handler.simpleElement(element, attrs) handler.ignorableWhitespace("\n") # rule.element # log if rule.log: attrs = { } if rule.log.prefix: attrs["prefix"] = rule.log.prefix if rule.log.level: attrs["level"] = rule.log.level if rule.log.limit: handler.ignorableWhitespace(" ") handler.startElement("log", attrs) handler.ignorableWhitespace("\n ") handler.simpleElement("limit", { "value": rule.log.limit.value }) handler.ignorableWhitespace("\n ") handler.endElement("log") else: handler.ignorableWhitespace(" ") handler.simpleElement("log", attrs) handler.ignorableWhitespace("\n") # audit if rule.audit: attrs = {} if rule.audit.limit: handler.ignorableWhitespace(" ") handler.startElement("audit", { }) handler.ignorableWhitespace("\n ") handler.simpleElement("limit", { "value": rule.audit.limit.value }) handler.ignorableWhitespace("\n ") handler.endElement("audit") else: handler.ignorableWhitespace(" ") handler.simpleElement("audit", attrs) handler.ignorableWhitespace("\n") # action if rule.action: action = "" attrs = { } if type(rule.action) == Rich_Accept: action = "accept" elif type(rule.action) == Rich_Reject: action = "reject" if rule.action.type: attrs["type"] = rule.action.type elif type(rule.action) == Rich_Drop: action = "drop" else: log.warning("Unknown action '%s'", type(rule.action)) if rule.action.limit: handler.ignorableWhitespace(" ") handler.startElement(action, attrs) handler.ignorableWhitespace("\n ") handler.simpleElement("limit", { "value": rule.action.limit.value }) handler.ignorableWhitespace("\n ") handler.endElement(action) else: handler.ignorableWhitespace(" ") handler.simpleElement(action, attrs) handler.ignorableWhitespace("\n") handler.ignorableWhitespace(" ") handler.endElement("rule") handler.ignorableWhitespace("\n") # end zone element handler.endElement("zone") handler.ignorableWhitespace("\n") handler.endDocument() f.close() del handler
def execute(self, enable): log.debug4("%s.execute(%s)" % (type(self), enable)) rules, modules = self.prepare(enable) # pre self.pre() # stage 1: apply rules error = False done = [ ] for ipv in rules: try: self.fw.rules(ipv, rules[ipv]) except Exception as msg: error = True if not self.generous_mode: log.warning(msg) else: done.append(ipv) if error and self.generous_mode: for ipv in rules: if ipv in done: continue for rule in rules[ipv]: try: self.fw.rule(ipv, rule) except Exception as msg: log.warning(msg) done.append(ipv) error = False # stage 2: load modules if not error: module_return = self.fw.handle_modules(modules, enable) if module_return: (cleanup_modules, msg) = module_return if cleanup_modules is not None: error = True self.fw.handle_modules(cleanup_modules, not enable) # error case: revert rules if error: undo_rules = { } for ipv in done: undo_rules[ipv] = [ ] for rule in reversed(rules[ipv]): undo_rules[ipv].append(reverse_rule(rule)) for ipv in undo_rules: try: self.fw.rules(ipv, undo_rules[ipv]) except Exception as msg: log.error(msg) # call failure functions for (func, args) in self.fail_funcs: try: func(*args) except Exception as msg: log.error("Calling fail func %s(%s) failed: %s" % \ (func, args, msg)) raise FirewallError(errors.COMMAND_FAILED) # post self.post()
def _start(self): # initialize firewall default_zone = FALLBACK_ZONE # load firewalld config log.debug1("Loading firewalld config file '%s'", FIREWALLD_CONF) try: self._firewalld_conf.read() except Exception as msg: log.warning("Using fallback firewalld configuration settings.") else: if self._firewalld_conf.get("DefaultZone"): default_zone = self._firewalld_conf.get("DefaultZone") if self._firewalld_conf.get("MinimalMark"): self._min_mark = int(self._firewalld_conf.get("MinimalMark")) if self._firewalld_conf.get("CleanupOnExit"): value = self._firewalld_conf.get("CleanupOnExit") if value is not None and value.lower() in [ "no", "false" ]: self.cleanup_on_exit = False if self._firewalld_conf.get("Lockdown"): value = self._firewalld_conf.get("Lockdown") if value is not None and value.lower() in [ "yes", "true" ]: log.debug1("Lockdown is enabled") try: self.policies.enable_lockdown() except FirewallError: # already enabled, this is probably reload pass if self._firewalld_conf.get("IPv6_rpfilter"): value = self._firewalld_conf.get("IPv6_rpfilter") if value is not None: if value.lower() in [ "no", "false" ]: self.ipv6_rpfilter_enabled = False if value.lower() in [ "yes", "true" ]: self.ipv6_rpfilter_enabled = True if self.ipv6_rpfilter_enabled: log.debug1("IPv6 rpfilter is enabled") else: log.debug1("IPV6 rpfilter is disabled") self.config.set_firewalld_conf(copy.deepcopy(self._firewalld_conf)) # apply default rules self._apply_default_rules() # load lockdown whitelist log.debug1("Loading lockdown whitelist") try: self.policies.lockdown_whitelist.read() except Exception as msg: if self.policies.query_lockdown(): log.error("Failed to load lockdown whitelist '%s': %s", self.policies.lockdown_whitelist.filename, msg) else: log.debug1("Failed to load lockdown whitelist '%s': %s", self.policies.lockdown_whitelist.filename, msg) # copy policies to config interface self.config.set_policies(copy.deepcopy(self.policies)) # load icmptype files self._loader(FIREWALLD_ICMPTYPES, "icmptype") self._loader(ETC_FIREWALLD_ICMPTYPES, "icmptype") if len(self.icmptype.get_icmptypes()) == 0: log.error("No icmptypes found.") # load service files self._loader(FIREWALLD_SERVICES, "service") self._loader(ETC_FIREWALLD_SERVICES, "service") if len(self.service.get_services()) == 0: log.error("No services found.") # load zone files self._loader(FIREWALLD_ZONES, "zone") self._loader(ETC_FIREWALLD_ZONES, "zone") if len(self.zone.get_zones()) == 0: log.fatal("No zones found.") sys.exit(1) # check minimum required zones error = False for z in [ "block", "drop", "trusted" ]: if z not in self.zone.get_zones(): log.fatal("Zone '%s' is not available.", z) error = True if error: sys.exit(1) # apply settings for loaded zones self.zone.apply_zones() # load direct rules obj = Direct(FIREWALLD_DIRECT) if os.path.exists(FIREWALLD_DIRECT): log.debug1("Loading direct rules file '%s'" % FIREWALLD_DIRECT) try: obj.read() except Exception as msg: log.debug1("Failed to load direct rules file '%s': %s", FIREWALLD_DIRECT, msg) else: self.direct.set_permanent_config(obj) self.config.set_direct(copy.deepcopy(obj)) # check if default_zone is a valid zone if default_zone not in self.zone.get_zones(): if "public" in self.zone.get_zones(): zone = "public" elif "external" in self.zone.get_zones(): zone = "external" else: zone = "block" # block is a base zone, therefore it has to exist log.error("Default zone '%s' is not valid. Using '%s'.", default_zone, zone) default_zone = zone else: log.debug1("Using default zone '%s'", default_zone) self._default_zone = self.check_zone(default_zone) self.zone.change_default_zone(None, self._default_zone) self._state = "RUNNING"
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name) self.item.parser_check_element_attrs(name, attrs) if name == "service": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'", attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] elif name == "short": pass elif name == "description": pass elif name == "port": if attrs["port"] != "": check_port(attrs["port"]) check_tcpudp(attrs["protocol"]) entry = (attrs["port"], attrs["protocol"]) if entry not in self.item.ports: self.item.ports.append(entry) else: log.warning("Port '%s/%s' already set, ignoring.", attrs["port"], attrs["protocol"]) else: check_protocol(attrs["protocol"]) if attrs["protocol"] not in self.item.protocols: self.item.protocols.append(attrs["protocol"]) else: log.warning("Protocol '%s' already set, ignoring.", attrs["protocol"]) elif name == "protocol": check_protocol(attrs["value"]) if attrs["value"] not in self.item.protocols: self.item.protocols.append(attrs["value"]) else: log.warning("Protocol '%s' already set, ignoring.", attrs["value"]) elif name == "destination": for x in [ "ipv4", "ipv6" ]: if x in attrs: check_address(x, attrs[x]) if x in self.item.destination: log.warning("Destination address for '%s' already set, ignoring", x) else: self.item.destination[x] = attrs[x] elif name == "module": if attrs["name"].startswith("nf_conntrack_") and \ len(attrs["name"].replace("nf_conntrack_", "")) > 0: if attrs["name"] not in self.item.modules: self.item.modules.append(attrs["name"]) else: log.warning("Module '%s' already set, ignoring.", attrs["name"]) else: log.warning("Invalid module '%s'", attrs["name"])
def _start_check(self): try: self.ipset_backend.list() except ValueError: log.warning("ipset not usable, disabling ipset usage in firewall.") # ipset is not usable, no supported types self.ipset_enabled = False self.ipset_supported_types = [ ] else: # ipset is usable, get all supported types self.ipset_supported_types = self.ipset_backend.supported_types() self.ip4tables_backend.fill_exists() if not self.ip4tables_backend.restore_command_exists: if self.ip4tables_backend.command_exists: log.warning("iptables-restore is missing, using " "individual calls for IPv4 firewall.") else: log.warning("iptables-restore and iptables are missing, " "disabling IPv4 firewall.") self.ip4tables_enabled = False self.ip6tables_backend.fill_exists() if not self.ip6tables_backend.restore_command_exists: if self.ip6tables_backend.command_exists: log.warning("ip6tables-restore is missing, using " "individual calls for IPv6 firewall.") else: log.warning("ip6tables-restore and ip6tables are missing, " "disabling IPv6 firewall.") self.ip6tables_enabled = False self.ebtables_backend.fill_exists() if not self.ebtables_backend.restore_command_exists: if self.ebtables_backend.command_exists: log.warning("ebtables-restore is missing, using " "individual calls for bridge firewall.") else: log.warning("ebtables-restore and ebtables are missing, " "disabling bridge firewall.") self.ebtables_enabled = False if self.ebtables_enabled and not self._individual_calls and \ not self.ebtables_backend.restore_noflush_option: log.debug1("ebtables-restore is not supporting the --noflush " "option, will therefore not be used") if os.path.exists(config.COMMANDS["modinfo"]): self.nf_conntrack_helpers = functions.get_nf_conntrack_helpers() if len(self.nf_conntrack_helpers) > 0: log.debug1("Conntrack helpers supported by the kernel:") for key,values in self.nf_conntrack_helpers.items(): log.debug1(" %s: %s", key, ", ".join(values)) else: log.debug1("No conntrack helpers supported by the kernel.") else: self.nf_conntrack_helpers = { } log.warning("modinfo command is missing, not able to detect conntrack helpers.")
def _start(self, reload=False, complete_reload=False): # initialize firewall default_zone = config.FALLBACK_ZONE # load firewalld config log.debug1("Loading firewalld config file '%s'", config.FIREWALLD_CONF) try: self._firewalld_conf.read() except Exception as msg: log.warning(msg) log.warning("Using fallback firewalld configuration settings.") else: if self._firewalld_conf.get("DefaultZone"): default_zone = self._firewalld_conf.get("DefaultZone") if self._firewalld_conf.get("MinimalMark"): self._min_mark = int(self._firewalld_conf.get("MinimalMark")) if self._firewalld_conf.get("CleanupOnExit"): value = self._firewalld_conf.get("CleanupOnExit") if value is not None and value.lower() in [ "no", "false" ]: self.cleanup_on_exit = False if self._firewalld_conf.get("Lockdown"): value = self._firewalld_conf.get("Lockdown") if value is not None and value.lower() in [ "yes", "true" ]: log.debug1("Lockdown is enabled") try: self.policies.enable_lockdown() except FirewallError: # already enabled, this is probably reload pass if self._firewalld_conf.get("IPv6_rpfilter"): value = self._firewalld_conf.get("IPv6_rpfilter") if value is not None: if value.lower() in [ "no", "false" ]: self.ipv6_rpfilter_enabled = False if value.lower() in [ "yes", "true" ]: self.ipv6_rpfilter_enabled = True if self.ipv6_rpfilter_enabled: log.debug1("IPv6 rpfilter is enabled") else: log.debug1("IPV6 rpfilter is disabled") if self._firewalld_conf.get("IndividualCalls"): value = self._firewalld_conf.get("IndividualCalls") if value is not None and value.lower() in [ "yes", "true" ]: log.debug1("IndividualCalls is enabled") self._individual_calls = True if self._firewalld_conf.get("LogDenied"): value = self._firewalld_conf.get("LogDenied") if value is None or value.lower() == "no": self._log_denied = "off" else: self._log_denied = value.lower() log.debug1("LogDenied is set to '%s'", self._log_denied) if self._firewalld_conf.get("AutomaticHelpers"): value = self._firewalld_conf.get("AutomaticHelpers") if value is not None: if value.lower() in [ "no", "false" ]: self._automatic_helpers = "no" elif value.lower() in [ "yes", "true" ]: self._automatic_helpers = "yes" else: self._automatic_helpers = value.lower() log.debug1("AutomaticHelpers is set to '%s'", self._automatic_helpers) self.config.set_firewalld_conf(copy.deepcopy(self._firewalld_conf)) self._start_check() # load lockdown whitelist log.debug1("Loading lockdown whitelist") try: self.policies.lockdown_whitelist.read() except Exception as msg: if self.policies.query_lockdown(): log.error("Failed to load lockdown whitelist '%s': %s", self.policies.lockdown_whitelist.filename, msg) else: log.debug1("Failed to load lockdown whitelist '%s': %s", self.policies.lockdown_whitelist.filename, msg) # copy policies to config interface self.config.set_policies(copy.deepcopy(self.policies)) # load ipset files self._loader(config.FIREWALLD_IPSETS, "ipset") self._loader(config.ETC_FIREWALLD_IPSETS, "ipset") # load icmptype files self._loader(config.FIREWALLD_ICMPTYPES, "icmptype") self._loader(config.ETC_FIREWALLD_ICMPTYPES, "icmptype") if len(self.icmptype.get_icmptypes()) == 0: log.error("No icmptypes found.") # load helper files self._loader(config.FIREWALLD_HELPERS, "helper") self._loader(config.ETC_FIREWALLD_HELPERS, "helper") # load service files self._loader(config.FIREWALLD_SERVICES, "service") self._loader(config.ETC_FIREWALLD_SERVICES, "service") if len(self.service.get_services()) == 0: log.error("No services found.") # load zone files self._loader(config.FIREWALLD_ZONES, "zone") self._loader(config.ETC_FIREWALLD_ZONES, "zone") if len(self.zone.get_zones()) == 0: log.fatal("No zones found.") sys.exit(1) # check minimum required zones error = False for z in [ "block", "drop", "trusted" ]: if z not in self.zone.get_zones(): log.fatal("Zone '%s' is not available.", z) error = True if error: sys.exit(1) # check if default_zone is a valid zone if default_zone not in self.zone.get_zones(): if "public" in self.zone.get_zones(): zone = "public" elif "external" in self.zone.get_zones(): zone = "external" else: zone = "block" # block is a base zone, therefore it has to exist log.error("Default zone '%s' is not valid. Using '%s'.", default_zone, zone) default_zone = zone else: log.debug1("Using default zone '%s'", default_zone) # load direct rules obj = Direct(config.FIREWALLD_DIRECT) if os.path.exists(config.FIREWALLD_DIRECT): log.debug1("Loading direct rules file '%s'" % \ config.FIREWALLD_DIRECT) try: obj.read() except Exception as msg: log.debug1("Failed to load direct rules file '%s': %s", config.FIREWALLD_DIRECT, msg) self.direct.set_permanent_config(obj) self.config.set_direct(copy.deepcopy(obj)) # automatic helpers if self._automatic_helpers != "system": functions.set_nf_conntrack_helper_setting(self._automatic_helpers == "yes") self.nf_conntrack_helper_setting = \ functions.get_nf_conntrack_helper_setting() # check if needed tables are there self._check_tables() if log.getDebugLogLevel() > 0: # get time before flushing and applying tm1 = time.time() # Start transaction transaction = FirewallTransaction(self) if reload: self.set_policy("DROP", use_transaction=transaction) # flush rules self.flush(use_transaction=transaction) # If modules need to be unloaded in complete reload or if there are # ipsets to get applied, limit the transaction to set_policy and flush. # # Future optimization for the ipset case in reload: The transaction # only needs to be split here if there are conflicting ipset types in # exsting ipsets and the configuration in firewalld. if (reload and complete_reload) or \ (self.ipset_enabled and self.ipset.has_ipsets()): transaction.execute(True) transaction.clear() # complete reload: unload modules also if reload and complete_reload: log.debug1("Unloading firewall modules") self.modules_backend.unload_firewall_modules() # apply settings for loaded ipsets while reloading here if self.ipset_enabled and self.ipset.has_ipsets(): log.debug1("Applying ipsets") self.ipset.apply_ipsets() # Start or continue with transaction # apply default rules log.debug1("Applying default rule set") self.apply_default_rules(use_transaction=transaction) # apply settings for loaded zones log.debug1("Applying used zones") self.zone.apply_zones(use_transaction=transaction) self._default_zone = self.check_zone(default_zone) self.zone.change_default_zone(None, self._default_zone, use_transaction=transaction) # Execute transaction transaction.execute(True) # Start new transaction for direct rules transaction.clear() # apply direct chains, rules and passthrough rules if self.direct.has_configuration(): transaction.enable_generous_mode() log.debug1("Applying direct chains rules and passthrough rules") self.direct.apply_direct(transaction) # Execute transaction transaction.execute(True) transaction.disable_generous_mode() transaction.clear() del transaction if log.getDebugLogLevel() > 1: # get time after flushing and applying tm2 = time.time() log.debug2("Flushing and applying took %f seconds" % (tm2 - tm1)) self._state = "RUNNING"
def startElement(self, name, attrs): IO_Object_ContentHandler.startElement(self, name) if self._rule_error: return self.item.parser_check_element_attrs(name, attrs) if name == "zone": if "name" in attrs: log.warning("Ignoring deprecated attribute name='%s'" % attrs["name"]) if "version" in attrs: self.item.version = attrs["version"] if "immutable" in attrs: log.warning("Ignoring deprecated attribute immutable='%s'" % attrs["immutable"]) if "target" in attrs: target = attrs["target"] if target not in ZONE_TARGETS: raise FirewallError(INVALID_TARGET, target) if target != "" and target != DEFAULT_ZONE_TARGET: self.item.target = target elif name == "short": pass elif name == "description": pass elif name == "service": if self._rule: if self._rule.element: log.error('Invalid rule: More than one element, ignoring.') self._rule_error = True return self._rule.element = Rich_Service(attrs["name"]) return if attrs["name"] not in self.item.services: self.item.services.append(attrs["name"]) elif name == "port": if self._rule: if self._rule.element: log.error('Invalid rule: More than one element, ignoring.') self._rule_error = True return self._rule.element = Rich_Port(attrs["port"], attrs["protocol"]) return # TODO: fix port string according to fw_zone.__port_id() entry = (attrs["port"], attrs["protocol"]) if entry not in self.item.ports: self.item.ports.append(entry) elif name == "protocol": if self._rule: if self._rule.element: log.error('Invalid rule: More than one element, ignoring.') self._rule_error = True return self._rule.element = Rich_Protocol(attrs["value"]) else: log.error('Protocol allowed only in rule.') if attrs["value"] not in self.item.protocols: self.item.protocols.append(attrs["value"]) elif name == "icmp-block": if self._rule: if self._rule.element: log.error('Invalid rule: More than one element, ignoring.') self._rule_error = True return self._rule.element = Rich_IcmpBlock(attrs["name"]) return if attrs["name"] not in self.item.icmp_blocks: self.item.icmp_blocks.append(attrs["name"]) elif name == "masquerade": if "enabled" in attrs: log.warning("Ignoring deprecated attribute enabled='%s'" % attrs["enabled"]) if self._rule: if self._rule.element: log.error('Invalid rule: More than one element, ignoring.') self._rule_error = True return self._rule.element = Rich_Masquerade() else: self.item.masquerade = True elif name == "forward-port": to_port = "" if "to-port" in attrs: to_port = attrs["to-port"] to_addr = "" if "to-addr" in attrs: to_addr = attrs["to-addr"] if self._rule: if self._rule.element: log.error('Invalid rule: More than one element, ignoring.') self._rule_error = True return self._rule.element = Rich_ForwardPort(attrs["port"], attrs["protocol"], to_port, to_addr) return # TODO: fix port string according to fw_zone.__forward_port_id() entry = (attrs["port"], attrs["protocol"], to_port, to_addr) if entry not in self.item.forward_ports: self.item.forward_ports.append(entry) elif name == "interface": if self._rule: log.error('Invalid rule: interface use in rule.') self._rule_error = True return # zone bound to interface if "name" not in attrs: log.error('Invalid interface: Name missing.') self._rule_error = True return name = attrs["name"] if name not in self.item.interfaces: self.item.interfaces.append(name) elif name == "source": if self._rule: if self._rule.source: log.error('Invalid rule: More than one source') self._rule_error = True return invert = False if "invert" in attrs and \ attrs["invert"].lower() in [ "yes", "true" ]: invert = True self._rule.source = Rich_Source(attrs["address"], invert) return # zone bound to source if "address" not in attrs: log.error('Invalid source: Address missing.') return if "family" in attrs: log.warning("Ignoring deprecated attribute family='%s'" % attrs["family"]) if "invert" in attrs: log.error('Invalid source: Invertion not allowed here.') return entry = attrs["address"] if entry not in self.item.sources: self.item.sources.append(entry) elif name == "destination": if not self._rule: log.error('Invalid rule: Destination outside of rule') self._rule_error = True return if self._rule.destination: log.error('Invalid rule: More than one destination') return invert = False if "invert" in attrs and \ attrs["invert"].lower() in [ "yes", "true" ]: invert = True self._rule.destination = Rich_Destination(attrs["address"], invert) elif name in [ "accept", "reject", "drop" ]: if not self._rule: log.error('Invalid rule: Action outside of rule') self._rule_error = True return if self._rule.action: log.error('Invalid rule: More than one action') self._rule_error = True return if name == "accept": self._rule.action = Rich_Accept() if name == "reject": _type = None if "type" in attrs: _type = attrs["type"] self._rule.action = Rich_Reject(_type) if name == "drop": self._rule.action = Rich_Drop() self._limit_ok = self._rule.action elif name == "log": if not self._rule: log.error('Invalid rule: Log outside of rule') return if self._rule.log: log.error('Invalid rule: More than one log') return level = None if "level" in attrs: level = attrs["level"] if level not in [ "emerg", "alert", "crit", "error", "warning", "notice", "info", "debug" ]: log.error('Invalid rule: Invalid log level') self._rule_error = True return prefix = attrs["prefix"] if "prefix" in attrs else None self._rule.log = Rich_Log(prefix, level) self._limit_ok = self._rule.log elif name == "audit": if not self._rule: log.error('Invalid rule: Audit outside of rule') return if self._rule.audit: log.error('Invalid rule: More than one audit') self._rule_error = True return self._rule.audit = Rich_Audit() self._limit_ok = self._rule.audit elif name == "rule": family = None if "family" in attrs: family = attrs["family"] if family not in [ "ipv4", "ipv6" ]: log.error('Invalid rule: Rule family "%s" invalid' % attrs["family"]) self._rule_error = True return self._rule = Rich_Rule(family) self.item.rules.append(self._rule) elif name == "limit": if not self._limit_ok: log.error('Invalid rule: Limit outside of action, log and audit') self._rule_error = True return if self._limit_ok.limit: log.error('Invalid rule: More than one limit') self._rule_error = True return value = attrs["value"] self._limit_ok.limit = Rich_Limit(value) else: log.error('Unknown XML element %s' % name) return