5994 lines
253 KiB
Diff
5994 lines
253 KiB
Diff
From 34bdee40aa61f6f973a76233742d41c20d6c6e0b Mon Sep 17 00:00:00 2001
|
|
From: Eric Garver <eric@garver.life>
|
|
Date: Fri, 4 Oct 2019 15:23:59 -0400
|
|
Subject: [PATCH] feat: implement policy objects internally
|
|
|
|
Introduce policy objects internally. There are no user visible changes.
|
|
Rule layout for zones has not changed. Zones are translated into
|
|
multiple policies.
|
|
|
|
This is the first step in introducing policy objects to users and
|
|
therefore supporting output/forward filtering.
|
|
---
|
|
src/firewall-config.in | 4 +-
|
|
src/firewall/core/base.py | 5 +-
|
|
src/firewall/core/ebtables.py | 2 +-
|
|
src/firewall/core/fw.py | 16 +-
|
|
src/firewall/core/fw_ipset.py | 8 +-
|
|
src/firewall/core/fw_policy.py | 1552 +++++++++++++++++++++++++++
|
|
src/firewall/core/fw_transaction.py | 20 +-
|
|
src/firewall/core/fw_zone.py | 1880 +++++++--------------------------
|
|
src/firewall/core/io/policy.py | 830 +++++++++++++++
|
|
src/firewall/core/ipXtables.py | 315 +++---
|
|
src/firewall/core/nftables.py | 301 +++---
|
|
src/firewall/errors.py | 1 +
|
|
src/firewall/functions.py | 11 +-
|
|
src/tests/features/service_include.at | 2 -
|
|
14 files changed, 3118 insertions(+), 1829 deletions(-)
|
|
create mode 100644 src/firewall/core/fw_policy.py
|
|
create mode 100644 src/firewall/core/io/policy.py
|
|
|
|
diff --git a/src/firewall-config.in b/src/firewall-config.in
|
|
index f5f69fc..1d88b29 100755
|
|
--- a/src/firewall-config.in
|
|
+++ b/src/firewall-config.in
|
|
@@ -46,7 +46,7 @@ from firewall import config
|
|
from firewall import client
|
|
from firewall import functions
|
|
from firewall.core.base import DEFAULT_ZONE_TARGET, REJECT_TYPES, \
|
|
- ZONE_SOURCE_IPSET_TYPES
|
|
+ SOURCE_IPSET_TYPES
|
|
from firewall.core.ipset import IPSET_MAXNAMELEN
|
|
from firewall.core.helper import HELPER_MAXNAMELEN
|
|
from firewall.core.io.zone import Zone
|
|
@@ -4188,7 +4188,7 @@ class FirewallConfig(object):
|
|
continue
|
|
raise
|
|
self.activate_exception_handler()
|
|
- if settings.getType() not in ZONE_SOURCE_IPSET_TYPES:
|
|
+ if settings.getType() not in SOURCE_IPSET_TYPES:
|
|
continue
|
|
ipsets[x] = settings
|
|
else:
|
|
diff --git a/src/firewall/core/base.py b/src/firewall/core/base.py
|
|
index e2691b5..45522a9 100644
|
|
--- a/src/firewall/core/base.py
|
|
+++ b/src/firewall/core/base.py
|
|
@@ -22,10 +22,13 @@
|
|
"""Base firewall settings"""
|
|
|
|
DEFAULT_ZONE_TARGET = "{chain}_{zone}"
|
|
+DEFAULT_POLICY_TARGET = "CONTINUE"
|
|
|
|
ZONE_TARGETS = [ "ACCEPT", "%%REJECT%%", "DROP", DEFAULT_ZONE_TARGET,
|
|
"default" ]
|
|
|
|
+POLICY_TARGETS = [ "ACCEPT", "%%REJECT%%", "DROP", "CONTINUE" ]
|
|
+
|
|
SHORTCUTS = {
|
|
"PREROUTING": "PRE",
|
|
"POSTROUTING": "POST",
|
|
@@ -49,7 +52,7 @@ REJECT_TYPES = {
|
|
# ipset types that can be used as a source in zones
|
|
# The match-set option will be src or src,src according to the
|
|
# dimension of the ipset.
|
|
-ZONE_SOURCE_IPSET_TYPES = [
|
|
+SOURCE_IPSET_TYPES = [
|
|
"hash:ip", "hash:ip,port", "hash:ip,mark",
|
|
"hash:net", "hash:net,port", "hash:net,iface",
|
|
"hash:mac"
|
|
diff --git a/src/firewall/core/ebtables.py b/src/firewall/core/ebtables.py
|
|
index a5860bd..8938b75 100644
|
|
--- a/src/firewall/core/ebtables.py
|
|
+++ b/src/firewall/core/ebtables.py
|
|
@@ -52,7 +52,7 @@ for table in BUILT_IN_CHAINS.keys():
|
|
class ebtables(object):
|
|
ipv = "eb"
|
|
name = "ebtables"
|
|
- zones_supported = False # ebtables only supported with direct interface
|
|
+ policies_supported = False # ebtables only supported with direct interface
|
|
|
|
def __init__(self):
|
|
self._command = COMMANDS[self.ipv]
|
|
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
|
index f3f1adf..a9d56a9 100644
|
|
--- a/src/firewall/core/fw.py
|
|
+++ b/src/firewall/core/fw.py
|
|
@@ -42,6 +42,7 @@ from firewall.core.fw_policies import FirewallPolicies
|
|
from firewall.core.fw_ipset import FirewallIPSet
|
|
from firewall.core.fw_transaction import FirewallTransaction
|
|
from firewall.core.fw_helper import FirewallHelper
|
|
+from firewall.core.fw_policy import FirewallPolicy
|
|
from firewall.core.fw_nm import nm_get_bus_name, nm_get_interfaces_in_zone
|
|
from firewall.core.logger import log
|
|
from firewall.core.io.firewalld_conf import firewalld_conf
|
|
@@ -98,6 +99,7 @@ class Firewall(object):
|
|
self.policies = FirewallPolicies()
|
|
self.ipset = FirewallIPSet(self)
|
|
self.helper = FirewallHelper(self)
|
|
+ self.policy = FirewallPolicy(self)
|
|
|
|
self.__init_vars()
|
|
|
|
@@ -634,6 +636,7 @@ class Firewall(object):
|
|
self.config.cleanup()
|
|
self.direct.cleanup()
|
|
self.policies.cleanup()
|
|
+ self.policy.cleanup()
|
|
self._firewalld_conf.cleanup()
|
|
self.__init_vars()
|
|
|
|
@@ -876,6 +879,12 @@ class Firewall(object):
|
|
if self._panic:
|
|
raise FirewallError(errors.PANIC_MODE)
|
|
|
|
+ def check_policy(self, policy):
|
|
+ _policy = policy
|
|
+ if _policy not in self.policy.get_policies():
|
|
+ raise FirewallError(errors.INVALID_POLICY, _policy)
|
|
+ return _policy
|
|
+
|
|
def check_zone(self, zone):
|
|
_zone = zone
|
|
if not _zone or _zone == "":
|
|
@@ -996,8 +1005,11 @@ class Firewall(object):
|
|
# add interfaces to zones again
|
|
for zone in self.zone.get_zones():
|
|
if zone in _zone_interfaces:
|
|
- self.zone.set_settings(zone, { "interfaces":
|
|
- _zone_interfaces[zone] })
|
|
+
|
|
+ for interface_id in _zone_interfaces[zone]:
|
|
+ self.zone.change_zone_of_interface(zone, interface_id,
|
|
+ _zone_interfaces[zone][interface_id]["sender"])
|
|
+
|
|
del _zone_interfaces[zone]
|
|
else:
|
|
log.info1("New zone '%s'.", zone)
|
|
diff --git a/src/firewall/core/fw_ipset.py b/src/firewall/core/fw_ipset.py
|
|
index 6136b47..102c7f6 100644
|
|
--- a/src/firewall/core/fw_ipset.py
|
|
+++ b/src/firewall/core/fw_ipset.py
|
|
@@ -155,8 +155,8 @@ class FirewallIPSet(object):
|
|
|
|
# TYPE
|
|
|
|
- def get_type(self, name):
|
|
- return self.get_ipset(name, applied=True).type
|
|
+ def get_type(self, name, applied=True):
|
|
+ return self.get_ipset(name, applied=applied).type
|
|
|
|
# DIMENSION
|
|
def get_dimension(self, name):
|
|
@@ -173,8 +173,8 @@ class FirewallIPSet(object):
|
|
|
|
# OPTIONS
|
|
|
|
- def get_family(self, name):
|
|
- obj = self.get_ipset(name, applied=True)
|
|
+ def get_family(self, name, applied=True):
|
|
+ obj = self.get_ipset(name, applied=applied)
|
|
if "family" in obj.options:
|
|
if obj.options["family"] == "inet6":
|
|
return "ipv6"
|
|
diff --git a/src/firewall/core/fw_policy.py b/src/firewall/core/fw_policy.py
|
|
new file mode 100644
|
|
index 0000000..d22e126
|
|
--- /dev/null
|
|
+++ b/src/firewall/core/fw_policy.py
|
|
@@ -0,0 +1,1552 @@
|
|
+# -*- coding: utf-8 -*-
|
|
+#
|
|
+# SPDX-License-Identifier: GPL-2.0-or-later
|
|
+
|
|
+import time
|
|
+from firewall.core.logger import log
|
|
+from firewall.functions import portStr, checkIPnMask, checkIP6nMask, \
|
|
+ checkProtocol, enable_ip_forwarding, check_single_address, \
|
|
+ portInPortRange, get_nf_conntrack_short_name, coalescePortRange, breakPortRange
|
|
+from firewall.core.rich import Rich_Rule, Rich_Accept, \
|
|
+ Rich_Mark, Rich_Service, Rich_Port, Rich_Protocol, \
|
|
+ Rich_Masquerade, Rich_ForwardPort, Rich_SourcePort, Rich_IcmpBlock, \
|
|
+ Rich_IcmpType
|
|
+from firewall.core.fw_transaction import FirewallTransaction
|
|
+from firewall import errors
|
|
+from firewall.errors import FirewallError
|
|
+from firewall.fw_types import LastUpdatedOrderedDict
|
|
+from firewall.core.base import SOURCE_IPSET_TYPES
|
|
+
|
|
+class FirewallPolicy(object):
|
|
+ def __init__(self, fw):
|
|
+ self._fw = fw
|
|
+ self._chains = { }
|
|
+ self._policies = { }
|
|
+
|
|
+ def __repr__(self):
|
|
+ return '%s(%r, %r)' % (self.__class__, self._chains, self._policies)
|
|
+
|
|
+ def cleanup(self):
|
|
+ self._chains.clear()
|
|
+ self._policies.clear()
|
|
+
|
|
+ # transaction
|
|
+
|
|
+ def new_transaction(self):
|
|
+ return FirewallTransaction(self._fw)
|
|
+
|
|
+ # policies
|
|
+
|
|
+ def get_policies(self):
|
|
+ return sorted(self._policies.keys())
|
|
+
|
|
+ def get_policy(self, policy):
|
|
+ p = self._fw.check_policy(policy)
|
|
+ return self._policies[p]
|
|
+
|
|
+ def _first_except(self, e, f, name, *args, **kwargs):
|
|
+ try:
|
|
+ f(name, *args, **kwargs)
|
|
+ except FirewallError as error:
|
|
+ if not e:
|
|
+ return error
|
|
+ return e
|
|
+
|
|
+ def add_policy(self, obj):
|
|
+ obj.settings = { x : LastUpdatedOrderedDict()
|
|
+ for x in [ "services", "ports",
|
|
+ "masquerade", "forward_ports",
|
|
+ "source_ports",
|
|
+ "icmp_blocks", "rules",
|
|
+ "protocols", "icmp_block_inversion" ] }
|
|
+
|
|
+ self._policies[obj.name] = obj
|
|
+ self.copy_permanent_to_runtime(obj.name)
|
|
+
|
|
+ def remove_policy(self, policy):
|
|
+ obj = self._policies[policy]
|
|
+ if obj.applied:
|
|
+ self.unapply_policy_settings(policy)
|
|
+ obj.settings.clear()
|
|
+ del self._policies[policy]
|
|
+
|
|
+ def copy_permanent_to_runtime(self, policy):
|
|
+ obj = self._policies[policy]
|
|
+
|
|
+ if obj.applied:
|
|
+ return
|
|
+
|
|
+ for args in obj.icmp_blocks:
|
|
+ self.add_icmp_block(policy, args)
|
|
+ for args in obj.forward_ports:
|
|
+ self.add_forward_port(policy, *args)
|
|
+ for args in obj.services:
|
|
+ self.add_service(policy, args)
|
|
+ for args in obj.ports:
|
|
+ self.add_port(policy, *args)
|
|
+ for args in obj.protocols:
|
|
+ self.add_protocol(policy, args)
|
|
+ for args in obj.source_ports:
|
|
+ self.add_source_port(policy, *args)
|
|
+ for args in obj.rules:
|
|
+ self.add_rule(policy, args)
|
|
+ if obj.masquerade:
|
|
+ self.add_masquerade(policy)
|
|
+
|
|
+ def set_policy_applied(self, policy, applied):
|
|
+ obj = self._policies[policy]
|
|
+ obj.applied = applied
|
|
+
|
|
+ # 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, policy):
|
|
+ return self.get_policy(policy).settings
|
|
+
|
|
+ def _policy_settings(self, enable, policy, use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ obj = self._policies[_policy]
|
|
+ if (enable and obj.applied) or (not enable and not obj.applied):
|
|
+ return
|
|
+ if enable:
|
|
+ obj.applied = True
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ settings = self.get_settings(policy)
|
|
+ for key in settings:
|
|
+ for args in settings[key]:
|
|
+ if key == "icmp_blocks":
|
|
+ self._icmp_block(enable, _policy, args, transaction)
|
|
+ elif key == "icmp_block_inversion":
|
|
+ continue
|
|
+ elif key == "forward_ports":
|
|
+ self._forward_port(enable, _policy, transaction,
|
|
+ *args)
|
|
+ elif key == "services":
|
|
+ self._service(enable, _policy, args, transaction)
|
|
+ elif key == "ports":
|
|
+ self._port(enable, _policy, args[0], args[1],
|
|
+ transaction)
|
|
+ elif key == "protocols":
|
|
+ self._protocol(enable, _policy, args, transaction)
|
|
+ elif key == "source_ports":
|
|
+ self._source_port(enable, _policy, args[0], args[1],
|
|
+ transaction)
|
|
+ elif key == "masquerade":
|
|
+ self._masquerade(enable, _policy, transaction)
|
|
+ elif key == "rules":
|
|
+ self.__rule(enable, _policy, Rich_Rule(rule_str=args),
|
|
+ transaction)
|
|
+ else:
|
|
+ log.warning("Policy '%s': Unknown setting '%s:%s', "
|
|
+ "unable to apply", policy, key, args)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(enable)
|
|
+
|
|
+ def apply_policy_settings(self, policy, use_transaction=None):
|
|
+ self._policy_settings(True, policy, use_transaction=use_transaction)
|
|
+
|
|
+ def unapply_policy_settings(self, policy, use_transaction=None):
|
|
+ self._policy_settings(False, policy, use_transaction=use_transaction)
|
|
+
|
|
+ def get_config_with_settings(self, policy):
|
|
+ """
|
|
+ :return: exported config updated with runtime settings
|
|
+ """
|
|
+ conf = list(self.get_policy(policy).export_config())
|
|
+ conf[5] = self.list_services(policy)
|
|
+ conf[6] = self.list_ports(policy)
|
|
+ conf[7] = self.list_icmp_blocks(policy)
|
|
+ conf[8] = self.query_masquerade(policy)
|
|
+ conf[9] = self.list_forward_ports(policy)
|
|
+ conf[12] = self.list_rules(policy)
|
|
+ conf[13] = self.list_protocols(policy)
|
|
+ conf[14] = self.list_source_ports(policy)
|
|
+ conf[15] = self.query_icmp_block_inversion(policy)
|
|
+ return tuple(conf)
|
|
+
|
|
+ # RICH LANGUAGE
|
|
+
|
|
+ def check_rule(self, rule):
|
|
+ rule.check()
|
|
+
|
|
+ def __rule_id(self, rule):
|
|
+ self.check_rule(rule)
|
|
+ return str(rule)
|
|
+
|
|
+ def _rule_source_ipv(self, source):
|
|
+ if not source:
|
|
+ return None
|
|
+
|
|
+ if source.addr:
|
|
+ if checkIPnMask(source.addr):
|
|
+ return "ipv4"
|
|
+ elif checkIP6nMask(source.addr):
|
|
+ return "ipv6"
|
|
+ elif hasattr(source, "mac") and source.mac:
|
|
+ return ""
|
|
+ elif hasattr(source, "ipset") and source.ipset:
|
|
+ self._check_ipset_type_for_source(source.ipset)
|
|
+ self._check_ipset_applied(source.ipset)
|
|
+ return self._ipset_family(source.ipset)
|
|
+
|
|
+ return None
|
|
+
|
|
+ def __rule(self, enable, policy, rule, transaction):
|
|
+ self._rule_prepare(enable, policy, rule, transaction)
|
|
+
|
|
+ def add_rule(self, policy, rule, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ rule_id = self.__rule_id(rule)
|
|
+ if rule_id in _obj.settings["rules"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "'%s' already in '%s'" % (rule, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self.__rule(True, _policy, rule, transaction)
|
|
+
|
|
+ self.__register_rule(_obj, rule_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_rule, _obj, rule_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_rule(self, _obj, rule_id, timeout, sender):
|
|
+ _obj.settings["rules"][rule_id] = self.__gen_settings(
|
|
+ timeout, sender)
|
|
+
|
|
+ def remove_rule(self, policy, rule,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ rule_id = self.__rule_id(rule)
|
|
+ if rule_id not in _obj.settings["rules"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "'%s' not in '%s'" % (rule, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self.__rule(False, _policy, rule, transaction)
|
|
+
|
|
+ transaction.add_post(self.__unregister_rule, _obj, rule_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_rule(self, _obj, rule_id):
|
|
+ if rule_id in _obj.settings["rules"]:
|
|
+ del _obj.settings["rules"][rule_id]
|
|
+
|
|
+ def query_rule(self, policy, rule):
|
|
+ return self.__rule_id(rule) in self.get_settings(policy)["rules"]
|
|
+
|
|
+ def list_rules(self, policy):
|
|
+ return list(self.get_settings(policy)["rules"].keys())
|
|
+
|
|
+ # SERVICES
|
|
+
|
|
+ def check_service(self, service):
|
|
+ self._fw.check_service(service)
|
|
+
|
|
+ def __service_id(self, service):
|
|
+ self.check_service(service)
|
|
+ return service
|
|
+
|
|
+ def add_service(self, policy, service, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ service_id = self.__service_id(service)
|
|
+ if service_id in _obj.settings["services"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "'%s' already in '%s'" % (service, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._service(True, _policy, service, transaction)
|
|
+
|
|
+ self.__register_service(_obj, service_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_service, _obj, service_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_service(self, _obj, service_id, timeout, sender):
|
|
+ _obj.settings["services"][service_id] = \
|
|
+ self.__gen_settings(timeout, sender)
|
|
+
|
|
+ def remove_service(self, policy, service,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ service_id = self.__service_id(service)
|
|
+ if service_id not in _obj.settings["services"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "'%s' not in '%s'" % (service, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._service(False, _policy, service, transaction)
|
|
+
|
|
+ transaction.add_post(self.__unregister_service, _obj, service_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_service(self, _obj, service_id):
|
|
+ if service_id in _obj.settings["services"]:
|
|
+ del _obj.settings["services"][service_id]
|
|
+
|
|
+ def query_service(self, policy, service):
|
|
+ return self.__service_id(service) in self.get_settings(policy)["services"]
|
|
+
|
|
+ def list_services(self, policy):
|
|
+ return self.get_settings(policy)["services"].keys()
|
|
+
|
|
+ def get_helpers_for_service_helpers(self, helpers):
|
|
+ _helpers = [ ]
|
|
+ for helper in helpers:
|
|
+ try:
|
|
+ _helper = self._fw.helper.get_helper(helper)
|
|
+ except FirewallError:
|
|
+ raise FirewallError(errors.INVALID_HELPER, helper)
|
|
+ _helpers.append(_helper)
|
|
+ return _helpers
|
|
+
|
|
+ def get_helpers_for_service_modules(self, modules, enable):
|
|
+ # If automatic helper assignment is turned off, helpers that
|
|
+ # do not have ports defined will be replaced by the helpers
|
|
+ # that the helper.module defines.
|
|
+ _helpers = [ ]
|
|
+ for module in modules:
|
|
+ try:
|
|
+ helper = self._fw.helper.get_helper(module)
|
|
+ except FirewallError:
|
|
+ raise FirewallError(errors.INVALID_HELPER, module)
|
|
+ if len(helper.ports) < 1:
|
|
+ _module_short_name = get_nf_conntrack_short_name(helper.module)
|
|
+ try:
|
|
+ _helper = self._fw.helper.get_helper(_module_short_name)
|
|
+ _helpers.append(_helper)
|
|
+ except FirewallError:
|
|
+ if enable:
|
|
+ log.warning("Helper '%s' is not available" % _module_short_name)
|
|
+ continue
|
|
+ else:
|
|
+ _helpers.append(helper)
|
|
+ return _helpers
|
|
+
|
|
+ # PORTS
|
|
+
|
|
+ def check_port(self, port, protocol):
|
|
+ self._fw.check_port(port)
|
|
+ self._fw.check_tcpudp(protocol)
|
|
+
|
|
+ def __port_id(self, port, protocol):
|
|
+ self.check_port(port, protocol)
|
|
+ return (portStr(port, "-"), protocol)
|
|
+
|
|
+ def add_port(self, policy, port, protocol, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"]))
|
|
+ for port_id in existing_port_ids:
|
|
+ if portInPortRange(port, port_id[0]):
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "'%s:%s' already in '%s'" % (port, protocol, _name))
|
|
+
|
|
+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ for range in added_ranges:
|
|
+ self._port(True, _policy, portStr(range, "-"), protocol, transaction)
|
|
+ for range in removed_ranges:
|
|
+ self._port(False, _policy, portStr(range, "-"), protocol, transaction)
|
|
+
|
|
+ for range in added_ranges:
|
|
+ port_id = self.__port_id(range, protocol)
|
|
+ self.__register_port(_obj, port_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_port, _obj, port_id)
|
|
+ for range in removed_ranges:
|
|
+ port_id = self.__port_id(range, protocol)
|
|
+ transaction.add_post(self.__unregister_port, _obj, port_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_port(self, _obj, port_id, timeout, sender):
|
|
+ _obj.settings["ports"][port_id] = \
|
|
+ self.__gen_settings(timeout, sender)
|
|
+
|
|
+ def remove_port(self, policy, port, protocol,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"]))
|
|
+ for port_id in existing_port_ids:
|
|
+ if portInPortRange(port, port_id[0]):
|
|
+ break
|
|
+ else:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "'%s:%s' not in '%s'" % (port, protocol, _name))
|
|
+
|
|
+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ for range in added_ranges:
|
|
+ self._port(True, _policy, portStr(range, "-"), protocol, transaction)
|
|
+ for range in removed_ranges:
|
|
+ self._port(False, _policy, portStr(range, "-"), protocol, transaction)
|
|
+
|
|
+ for range in added_ranges:
|
|
+ port_id = self.__port_id(range, protocol)
|
|
+ self.__register_port(_obj, port_id, 0, None)
|
|
+ transaction.add_fail(self.__unregister_port, _obj, port_id)
|
|
+ for range in removed_ranges:
|
|
+ port_id = self.__port_id(range, protocol)
|
|
+ transaction.add_post(self.__unregister_port, _obj, port_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_port(self, _obj, port_id):
|
|
+ if port_id in _obj.settings["ports"]:
|
|
+ del _obj.settings["ports"][port_id]
|
|
+
|
|
+ def query_port(self, policy, port, protocol):
|
|
+ for (_port, _protocol) in self.get_settings(policy)["ports"]:
|
|
+ if portInPortRange(port, _port) and protocol == _protocol:
|
|
+ return True
|
|
+
|
|
+ return False
|
|
+
|
|
+ def list_ports(self, policy):
|
|
+ return list(self.get_settings(policy)["ports"].keys())
|
|
+
|
|
+ # PROTOCOLS
|
|
+
|
|
+ def check_protocol(self, protocol):
|
|
+ if not checkProtocol(protocol):
|
|
+ raise FirewallError(errors.INVALID_PROTOCOL, protocol)
|
|
+
|
|
+ def __protocol_id(self, protocol):
|
|
+ self.check_protocol(protocol)
|
|
+ return protocol
|
|
+
|
|
+ def add_protocol(self, policy, protocol, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ protocol_id = self.__protocol_id(protocol)
|
|
+ if protocol_id in _obj.settings["protocols"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "'%s' already in '%s'" % (protocol, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._protocol(True, _policy, protocol, transaction)
|
|
+
|
|
+ self.__register_protocol(_obj, protocol_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_protocol, _obj, protocol_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_protocol(self, _obj, protocol_id, timeout, sender):
|
|
+ _obj.settings["protocols"][protocol_id] = \
|
|
+ self.__gen_settings(timeout, sender)
|
|
+
|
|
+ def remove_protocol(self, policy, protocol,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ protocol_id = self.__protocol_id(protocol)
|
|
+ if protocol_id not in _obj.settings["protocols"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "'%s' not in '%s'" % (protocol, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._protocol(False, _policy, protocol, transaction)
|
|
+
|
|
+ transaction.add_post(self.__unregister_protocol, _obj,
|
|
+ protocol_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_protocol(self, _obj, protocol_id):
|
|
+ if protocol_id in _obj.settings["protocols"]:
|
|
+ del _obj.settings["protocols"][protocol_id]
|
|
+
|
|
+ def query_protocol(self, policy, protocol):
|
|
+ return self.__protocol_id(protocol) in self.get_settings(policy)["protocols"]
|
|
+
|
|
+ def list_protocols(self, policy):
|
|
+ return list(self.get_settings(policy)["protocols"].keys())
|
|
+
|
|
+ # SOURCE PORTS
|
|
+
|
|
+ def __source_port_id(self, port, protocol):
|
|
+ self.check_port(port, protocol)
|
|
+ return (portStr(port, "-"), protocol)
|
|
+
|
|
+ def add_source_port(self, policy, port, protocol, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"]))
|
|
+ for port_id in existing_port_ids:
|
|
+ if portInPortRange(port, port_id[0]):
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "'%s:%s' already in '%s'" % (port, protocol, _name))
|
|
+
|
|
+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ for range in added_ranges:
|
|
+ self._source_port(True, _policy, portStr(range, "-"), protocol, transaction)
|
|
+ for range in removed_ranges:
|
|
+ self._source_port(False, _policy, portStr(range, "-"), protocol, transaction)
|
|
+
|
|
+ for range in added_ranges:
|
|
+ port_id = self.__source_port_id(range, protocol)
|
|
+ self.__register_source_port(_obj, port_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_source_port, _obj, port_id)
|
|
+ for range in removed_ranges:
|
|
+ port_id = self.__source_port_id(range, protocol)
|
|
+ transaction.add_post(self.__unregister_source_port, _obj, port_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_source_port(self, _obj, port_id, timeout, sender):
|
|
+ _obj.settings["source_ports"][port_id] = \
|
|
+ self.__gen_settings(timeout, sender)
|
|
+
|
|
+ def remove_source_port(self, policy, port, protocol,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"]))
|
|
+ for port_id in existing_port_ids:
|
|
+ if portInPortRange(port, port_id[0]):
|
|
+ break
|
|
+ else:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "'%s:%s' not in '%s'" % (port, protocol, _name))
|
|
+
|
|
+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ for range in added_ranges:
|
|
+ self._source_port(True, _policy, portStr(range, "-"), protocol, transaction)
|
|
+ for range in removed_ranges:
|
|
+ self._source_port(False, _policy, portStr(range, "-"), protocol, transaction)
|
|
+
|
|
+ for range in added_ranges:
|
|
+ port_id = self.__source_port_id(range, protocol)
|
|
+ self.__register_source_port(_obj, port_id, 0, None)
|
|
+ transaction.add_fail(self.__unregister_source_port, _obj, port_id)
|
|
+ for range in removed_ranges:
|
|
+ port_id = self.__source_port_id(range, protocol)
|
|
+ transaction.add_post(self.__unregister_source_port, _obj, port_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_source_port(self, _obj, port_id):
|
|
+ if port_id in _obj.settings["source_ports"]:
|
|
+ del _obj.settings["source_ports"][port_id]
|
|
+
|
|
+ def query_source_port(self, policy, port, protocol):
|
|
+ for (_port, _protocol) in self.get_settings(policy)["source_ports"]:
|
|
+ if portInPortRange(port, _port) and protocol == _protocol:
|
|
+ return True
|
|
+
|
|
+ return False
|
|
+
|
|
+ def list_source_ports(self, policy):
|
|
+ return list(self.get_settings(policy)["source_ports"].keys())
|
|
+
|
|
+ # MASQUERADE
|
|
+
|
|
+ def __masquerade_id(self):
|
|
+ return True
|
|
+
|
|
+ def add_masquerade(self, policy, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ masquerade_id = self.__masquerade_id()
|
|
+ if masquerade_id in _obj.settings["masquerade"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "masquerade already enabled in '%s'" % _name)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._masquerade(True, _policy, transaction)
|
|
+
|
|
+ self.__register_masquerade(_obj, masquerade_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_masquerade, _obj, masquerade_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_masquerade(self, _obj, masquerade_id, timeout, sender):
|
|
+ _obj.settings["masquerade"][masquerade_id] = \
|
|
+ self.__gen_settings(timeout, sender)
|
|
+
|
|
+ def remove_masquerade(self, policy, use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ masquerade_id = self.__masquerade_id()
|
|
+ if masquerade_id not in _obj.settings["masquerade"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "masquerade not enabled in '%s'" % _name)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._masquerade(False, _policy, transaction)
|
|
+
|
|
+ transaction.add_post(self.__unregister_masquerade, _obj, masquerade_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_masquerade(self, _obj, masquerade_id):
|
|
+ if masquerade_id in _obj.settings["masquerade"]:
|
|
+ del _obj.settings["masquerade"][masquerade_id]
|
|
+
|
|
+ def query_masquerade(self, policy):
|
|
+ return self.__masquerade_id() in self.get_settings(policy)["masquerade"]
|
|
+
|
|
+ # PORT FORWARDING
|
|
+
|
|
+ def check_forward_port(self, ipv, port, protocol, toport=None, toaddr=None):
|
|
+ self._fw.check_port(port)
|
|
+ self._fw.check_tcpudp(protocol)
|
|
+ if toport:
|
|
+ self._fw.check_port(toport)
|
|
+ if toaddr:
|
|
+ if not check_single_address(ipv, toaddr):
|
|
+ raise FirewallError(errors.INVALID_ADDR, toaddr)
|
|
+ if not toport and not toaddr:
|
|
+ raise FirewallError(
|
|
+ errors.INVALID_FORWARD,
|
|
+ "port-forwarding is missing to-port AND to-addr")
|
|
+
|
|
+ def __forward_port_id(self, port, protocol, toport=None, toaddr=None):
|
|
+ if check_single_address("ipv6", toaddr):
|
|
+ self.check_forward_port("ipv6", port, protocol, toport, toaddr)
|
|
+ else:
|
|
+ self.check_forward_port("ipv4", port, protocol, toport, toaddr)
|
|
+ return (portStr(port, "-"), protocol,
|
|
+ portStr(toport, "-"), str(toaddr))
|
|
+
|
|
+ def add_forward_port(self, policy, port, protocol, toport=None,
|
|
+ toaddr=None, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ forward_id = self.__forward_port_id(port, protocol, toport, toaddr)
|
|
+ if forward_id in _obj.settings["forward_ports"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "'%s:%s:%s:%s' already in '%s'" % \
|
|
+ (port, protocol, toport, toaddr, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._forward_port(True, _policy, transaction, port, protocol,
|
|
+ toport, toaddr)
|
|
+
|
|
+ self.__register_forward_port(_obj, forward_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_forward_port, _obj, forward_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_forward_port(self, _obj, forward_id, timeout, sender):
|
|
+ _obj.settings["forward_ports"][forward_id] = \
|
|
+ self.__gen_settings(timeout, sender)
|
|
+
|
|
+ def remove_forward_port(self, policy, port, protocol, toport=None,
|
|
+ toaddr=None, use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ forward_id = self.__forward_port_id(port, protocol, toport, toaddr)
|
|
+ if forward_id not in _obj.settings["forward_ports"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "'%s:%s:%s:%s' not in '%s'" % \
|
|
+ (port, protocol, toport, toaddr, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._forward_port(False, _policy, transaction, port, protocol,
|
|
+ toport, toaddr)
|
|
+
|
|
+ transaction.add_post(self.__unregister_forward_port, _obj, forward_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_forward_port(self, _obj, forward_id):
|
|
+ if forward_id in _obj.settings["forward_ports"]:
|
|
+ del _obj.settings["forward_ports"][forward_id]
|
|
+
|
|
+ def query_forward_port(self, policy, port, protocol, toport=None,
|
|
+ toaddr=None):
|
|
+ forward_id = self.__forward_port_id(port, protocol, toport, toaddr)
|
|
+ return forward_id in self.get_settings(policy)["forward_ports"]
|
|
+
|
|
+ def list_forward_ports(self, policy):
|
|
+ return list(self.get_settings(policy)["forward_ports"].keys())
|
|
+
|
|
+ # ICMP BLOCK
|
|
+
|
|
+ def check_icmp_block(self, icmp):
|
|
+ self._fw.check_icmptype(icmp)
|
|
+
|
|
+ def __icmp_block_id(self, icmp):
|
|
+ self.check_icmp_block(icmp)
|
|
+ return icmp
|
|
+
|
|
+ def add_icmp_block(self, policy, icmp, timeout=0, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_timeout(timeout)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ icmp_id = self.__icmp_block_id(icmp)
|
|
+ if icmp_id in _obj.settings["icmp_blocks"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.ALREADY_ENABLED,
|
|
+ "'%s' already in '%s'" % (icmp, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._icmp_block(True, _policy, icmp, transaction)
|
|
+
|
|
+ self.__register_icmp_block(_obj, icmp_id, timeout, sender)
|
|
+ transaction.add_fail(self.__unregister_icmp_block, _obj, icmp_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_icmp_block(self, _obj, icmp_id, timeout, sender):
|
|
+ _obj.settings["icmp_blocks"][icmp_id] = \
|
|
+ self.__gen_settings(timeout, sender)
|
|
+
|
|
+ def remove_icmp_block(self, policy, icmp, use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ icmp_id = self.__icmp_block_id(icmp)
|
|
+ if icmp_id not in _obj.settings["icmp_blocks"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(errors.NOT_ENABLED,
|
|
+ "'%s' not in '%s'" % (icmp, _name))
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ self._icmp_block(False, _policy, icmp, transaction)
|
|
+
|
|
+ transaction.add_post(self.__unregister_icmp_block, _obj, icmp_id)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_icmp_block(self, _obj, icmp_id):
|
|
+ if icmp_id in _obj.settings["icmp_blocks"]:
|
|
+ del _obj.settings["icmp_blocks"][icmp_id]
|
|
+
|
|
+ def query_icmp_block(self, policy, icmp):
|
|
+ return self.__icmp_block_id(icmp) in self.get_settings(policy)["icmp_blocks"]
|
|
+
|
|
+ def list_icmp_blocks(self, policy):
|
|
+ return self.get_settings(policy)["icmp_blocks"].keys()
|
|
+
|
|
+ # ICMP BLOCK INVERSION
|
|
+
|
|
+ def __icmp_block_inversion_id(self):
|
|
+ return True
|
|
+
|
|
+ def add_icmp_block_inversion(self, policy, sender=None,
|
|
+ use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ icmp_block_inversion_id = self.__icmp_block_inversion_id()
|
|
+ if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(
|
|
+ errors.ALREADY_ENABLED,
|
|
+ "icmp-block-inversion already enabled in '%s'" % _name)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ # undo icmp blocks
|
|
+ for args in self.get_settings(_policy)["icmp_blocks"]:
|
|
+ self._icmp_block(False, _policy, args, transaction)
|
|
+
|
|
+ self._icmp_block_inversion(False, _policy, transaction)
|
|
+
|
|
+ self.__register_icmp_block_inversion(_obj, icmp_block_inversion_id,
|
|
+ sender)
|
|
+ transaction.add_fail(self.__undo_icmp_block_inversion, _policy, _obj,
|
|
+ icmp_block_inversion_id)
|
|
+
|
|
+ # redo icmp blocks
|
|
+ if _obj.applied:
|
|
+ for args in self.get_settings(_policy)["icmp_blocks"]:
|
|
+ self._icmp_block(True, _policy, args, transaction)
|
|
+
|
|
+ self._icmp_block_inversion(True, _policy, transaction)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __register_icmp_block_inversion(self, _obj, icmp_block_inversion_id,
|
|
+ sender):
|
|
+ _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] = \
|
|
+ self.__gen_settings(0, sender)
|
|
+
|
|
+ def __undo_icmp_block_inversion(self, _policy, _obj, icmp_block_inversion_id):
|
|
+ transaction = self.new_transaction()
|
|
+
|
|
+ # undo icmp blocks
|
|
+ if _obj.applied:
|
|
+ for args in self.get_settings(_policy)["icmp_blocks"]:
|
|
+ self._icmp_block(False, _policy, args, transaction)
|
|
+
|
|
+ if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]:
|
|
+ del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id]
|
|
+
|
|
+ # redo icmp blocks
|
|
+ if _obj.applied:
|
|
+ for args in self.get_settings(_policy)["icmp_blocks"]:
|
|
+ self._icmp_block(True, _policy, args, transaction)
|
|
+
|
|
+ transaction.execute(True)
|
|
+
|
|
+ def remove_icmp_block_inversion(self, policy, use_transaction=None):
|
|
+ _policy = self._fw.check_policy(policy)
|
|
+ self._fw.check_panic()
|
|
+ _obj = self._policies[_policy]
|
|
+
|
|
+ icmp_block_inversion_id = self.__icmp_block_inversion_id()
|
|
+ if icmp_block_inversion_id not in _obj.settings["icmp_block_inversion"]:
|
|
+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy
|
|
+ raise FirewallError(
|
|
+ errors.NOT_ENABLED,
|
|
+ "icmp-block-inversion not enabled in '%s'" % _name)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction = self.new_transaction()
|
|
+ else:
|
|
+ transaction = use_transaction
|
|
+
|
|
+ if _obj.applied:
|
|
+ # undo icmp blocks
|
|
+ for args in self.get_settings(_policy)["icmp_blocks"]:
|
|
+ self._icmp_block(False, _policy, args, transaction)
|
|
+
|
|
+ self._icmp_block_inversion(False, _policy, transaction)
|
|
+
|
|
+ self.__unregister_icmp_block_inversion(_obj,
|
|
+ icmp_block_inversion_id)
|
|
+ transaction.add_fail(self.__register_icmp_block_inversion, _obj,
|
|
+ icmp_block_inversion_id, None)
|
|
+
|
|
+ # redo icmp blocks
|
|
+ if _obj.applied:
|
|
+ for args in self.get_settings(_policy)["icmp_blocks"]:
|
|
+ self._icmp_block(True, _policy, args, transaction)
|
|
+
|
|
+ self._icmp_block_inversion(True, _policy, transaction)
|
|
+
|
|
+ if use_transaction is None:
|
|
+ transaction.execute(True)
|
|
+
|
|
+ return _policy
|
|
+
|
|
+ def __unregister_icmp_block_inversion(self, _obj, icmp_block_inversion_id):
|
|
+ if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]:
|
|
+ del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id]
|
|
+
|
|
+ def query_icmp_block_inversion(self, policy):
|
|
+ return self.__icmp_block_inversion_id() in \
|
|
+ self.get_settings(policy)["icmp_block_inversion"]
|
|
+
|
|
+ def gen_chain_rules(self, policy, create, table, chain, transaction):
|
|
+ # HACK: iptables backend has to support the (raw, PREROUTING) chain in
|
|
+ # multiple policies derived for zones - this is due to conntrack
|
|
+ # helpers. As such, track it in the policy matching zone --> HOST.
|
|
+ obj = self._fw.policy.get_policy(policy)
|
|
+ if obj.derived_from_zone and table == "raw" and chain == "PREROUTING":
|
|
+ for p in self._fw.zone._zone_policies[obj.derived_from_zone]:
|
|
+ p_obj = self._fw.policy.get_policy(p)
|
|
+ if p_obj.egress_zones[0] == "HOST":
|
|
+ tracking_policy = p
|
|
+ break
|
|
+ else:
|
|
+ tracking_policy = policy
|
|
+
|
|
+ if create:
|
|
+ if tracking_policy in self._chains and \
|
|
+ table in self._chains[tracking_policy]:
|
|
+ return
|
|
+ else:
|
|
+ if tracking_policy not in self._chains or \
|
|
+ table not in self._chains[tracking_policy]:
|
|
+ return
|
|
+
|
|
+ for backend in self._fw.enabled_backends():
|
|
+ if backend.policies_supported and \
|
|
+ table in backend.get_available_tables():
|
|
+ rules = backend.build_policy_chain_rules(policy, table)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ self._register_chains(tracking_policy, create, [table])
|
|
+ transaction.add_fail(self._register_chains, tracking_policy, not create, [table])
|
|
+
|
|
+ def _register_chains(self, policy, create, tables):
|
|
+ for table in tables:
|
|
+ if create:
|
|
+ self._chains.setdefault(policy, []).append(table)
|
|
+ else:
|
|
+ self._chains[policy].remove(table)
|
|
+ if len(self._chains[policy]) == 0:
|
|
+ del self._chains[policy]
|
|
+
|
|
+ # IPSETS
|
|
+
|
|
+ def _ipset_family(self, name):
|
|
+ if self._fw.ipset.get_type(name) == "hash:mac":
|
|
+ return None
|
|
+ return self._fw.ipset.get_family(name)
|
|
+
|
|
+ def __ipset_type(self, name):
|
|
+ return self._fw.ipset.get_type(name)
|
|
+
|
|
+ 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 _rule_prepare(self, enable, policy, rule, transaction):
|
|
+ if rule.family is not None:
|
|
+ ipvs = [ rule.family ]
|
|
+ else:
|
|
+ ipvs = [ipv for ipv in ["ipv4", "ipv6"] if self._fw.is_ipv_enabled(ipv)]
|
|
+
|
|
+ source_ipv = self._rule_source_ipv(rule.source)
|
|
+ if source_ipv is not None and source_ipv != "":
|
|
+ if rule.family is not None:
|
|
+ # rule family is defined by user, no way to change it
|
|
+ if rule.family != source_ipv:
|
|
+ raise FirewallError(errors.INVALID_RULE,
|
|
+ "Source address family '%s' conflicts with rule family '%s'." % (source_ipv, rule.family))
|
|
+ else:
|
|
+ # use the source family as rule family
|
|
+ ipvs = [ source_ipv ]
|
|
+
|
|
+ # add an element to object to allow backends to know what ipvs this applies to
|
|
+ rule.ipvs = ipvs
|
|
+
|
|
+ for backend in set([self._fw.get_backend_by_ipv(x) for x in ipvs]):
|
|
+ # SERVICE
|
|
+ if type(rule.element) == Rich_Service:
|
|
+ svc = self._fw.service.get_service(rule.element.name)
|
|
+
|
|
+ destinations = []
|
|
+ if len(svc.destination) > 0:
|
|
+ if rule.destination:
|
|
+ # we can not use two destinations at the same time
|
|
+ raise FirewallError(errors.INVALID_RULE,
|
|
+ "Destination conflict with service.")
|
|
+ for ipv in ipvs:
|
|
+ if ipv in svc.destination and backend.is_ipv_supported(ipv):
|
|
+ destinations.append(svc.destination[ipv])
|
|
+ else:
|
|
+ # dummy for the following for loop
|
|
+ destinations.append(None)
|
|
+
|
|
+ for destination in destinations:
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+ transaction.add_chain(policy, "raw", "PREROUTING")
|
|
+
|
|
+ if type(rule.action) == Rich_Accept:
|
|
+ # only load modules for accept action
|
|
+ helpers = self.get_helpers_for_service_modules(svc.modules,
|
|
+ enable)
|
|
+ helpers += self.get_helpers_for_service_helpers(svc.helpers)
|
|
+ helpers = sorted(set(helpers), key=lambda x: x.name)
|
|
+
|
|
+ modules = [ ]
|
|
+ for helper in helpers:
|
|
+ module = helper.module
|
|
+ _module_short_name = get_nf_conntrack_short_name(module)
|
|
+ nat_module = module.replace("conntrack", "nat")
|
|
+ modules.append(nat_module)
|
|
+ if helper.family != "" and not backend.is_ipv_supported(helper.family):
|
|
+ # no support for family ipv, continue
|
|
+ continue
|
|
+ if len(helper.ports) < 1:
|
|
+ modules.append(module)
|
|
+ else:
|
|
+ for (port,proto) in helper.ports:
|
|
+ rules = backend.build_policy_helper_ports_rules(
|
|
+ enable, policy, proto, port,
|
|
+ destination, helper.name, _module_short_name)
|
|
+ transaction.add_rules(backend, rules)
|
|
+ transaction.add_modules(modules)
|
|
+
|
|
+ # create rules
|
|
+ for (port,proto) in svc.ports:
|
|
+ if enable and type(rule.action) == Rich_Mark:
|
|
+ transaction.add_chain(policy, "mangle", "PREROUTING")
|
|
+ rules = backend.build_policy_ports_rules(
|
|
+ enable, policy, proto, port, destination, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ for proto in svc.protocols:
|
|
+ if enable and type(rule.action) == Rich_Mark:
|
|
+ transaction.add_chain(policy, "mangle", "PREROUTING")
|
|
+ rules = backend.build_policy_protocol_rules(
|
|
+ enable, policy, proto, destination, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # create rules
|
|
+ for (port,proto) in svc.source_ports:
|
|
+ if enable and type(rule.action) == Rich_Mark:
|
|
+ transaction.add_chain(policy, "mangle", "PREROUTING")
|
|
+ rules = backend.build_policy_source_ports_rules(
|
|
+ enable, policy, proto, port, destination, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # PORT
|
|
+ elif type(rule.element) == Rich_Port:
|
|
+ port = rule.element.port
|
|
+ protocol = rule.element.protocol
|
|
+ self.check_port(port, protocol)
|
|
+
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+ if enable and type(rule.action) == Rich_Mark:
|
|
+ transaction.add_chain(policy, "mangle", "PREROUTING")
|
|
+
|
|
+ rules = backend.build_policy_ports_rules(
|
|
+ enable, policy, protocol, port, None, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # PROTOCOL
|
|
+ elif type(rule.element) == Rich_Protocol:
|
|
+ protocol = rule.element.value
|
|
+ self.check_protocol(protocol)
|
|
+
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+ if enable and type(rule.action) == Rich_Mark:
|
|
+ transaction.add_chain(policy, "mangle", "PREROUTING")
|
|
+
|
|
+ rules = backend.build_policy_protocol_rules(
|
|
+ enable, policy, protocol, None, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # MASQUERADE
|
|
+ elif type(rule.element) == Rich_Masquerade:
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "nat", "POSTROUTING")
|
|
+ transaction.add_chain(policy, "filter", "FORWARD_OUT")
|
|
+ for ipv in ipvs:
|
|
+ if backend.is_ipv_supported(ipv):
|
|
+ transaction.add_post(enable_ip_forwarding, ipv)
|
|
+
|
|
+ rules = backend.build_policy_masquerade_rules(enable, policy, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # FORWARD PORT
|
|
+ elif type(rule.element) == Rich_ForwardPort:
|
|
+ port = rule.element.port
|
|
+ protocol = rule.element.protocol
|
|
+ toport = rule.element.to_port
|
|
+ toaddr = rule.element.to_address
|
|
+ for ipv in ipvs:
|
|
+ if backend.is_ipv_supported(ipv):
|
|
+ self.check_forward_port(ipv, port, protocol, toport, toaddr)
|
|
+ if toaddr and enable:
|
|
+ transaction.add_post(enable_ip_forwarding, ipv)
|
|
+
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "nat", "PREROUTING")
|
|
+
|
|
+ rules = backend.build_policy_forward_port_rules(
|
|
+ enable, policy, port, protocol, toport,
|
|
+ toaddr, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # SOURCE PORT
|
|
+ elif type(rule.element) == Rich_SourcePort:
|
|
+ port = rule.element.port
|
|
+ protocol = rule.element.protocol
|
|
+ self.check_port(port, protocol)
|
|
+
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+ if enable and type(rule.action) == Rich_Mark:
|
|
+ transaction.add_chain(policy, "mangle", "PREROUTING")
|
|
+
|
|
+ rules = backend.build_policy_source_ports_rules(
|
|
+ enable, policy, protocol, port, None, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # ICMP BLOCK and ICMP TYPE
|
|
+ elif type(rule.element) == Rich_IcmpBlock or \
|
|
+ type(rule.element) == Rich_IcmpType:
|
|
+ ict = self._fw.icmptype.get_icmptype(rule.element.name)
|
|
+
|
|
+ if type(rule.element) == Rich_IcmpBlock and \
|
|
+ rule.action and type(rule.action) == Rich_Accept:
|
|
+ # icmp block might have reject or drop action, but not accept
|
|
+ raise FirewallError(errors.INVALID_RULE,
|
|
+ "IcmpBlock not usable with accept action")
|
|
+ if ict.destination:
|
|
+ for ipv in ipvs:
|
|
+ if ipv in ict.destination \
|
|
+ and not backend.is_ipv_supported(ipv):
|
|
+ raise FirewallError(
|
|
+ errors.INVALID_RULE,
|
|
+ "Icmp%s %s not usable with %s" % \
|
|
+ ("Block" if type(rule.element) == \
|
|
+ Rich_IcmpBlock else "Type",
|
|
+ rule.element.name, backend.name))
|
|
+
|
|
+ table = "filter"
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, table, "INPUT")
|
|
+ transaction.add_chain(policy, table, "FORWARD_IN")
|
|
+
|
|
+ rules = backend.build_policy_icmp_block_rules(enable, policy, ict, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ elif rule.element is None:
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+ if enable and type(rule.action) == Rich_Mark:
|
|
+ transaction.add_chain(policy, "mangle", "PREROUTING")
|
|
+
|
|
+ rules = backend.build_policy_rich_source_destination_rules(
|
|
+ enable, policy, rule)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ # EVERYTHING ELSE
|
|
+ else:
|
|
+ raise FirewallError(errors.INVALID_RULE, "Unknown element %s" %
|
|
+ type(rule.element))
|
|
+
|
|
+ def _service(self, enable, policy, service, transaction, included_services=None):
|
|
+ svc = self._fw.service.get_service(service)
|
|
+ helpers = self.get_helpers_for_service_modules(svc.modules, enable)
|
|
+ helpers += self.get_helpers_for_service_helpers(svc.helpers)
|
|
+ helpers = sorted(set(helpers), key=lambda x: x.name)
|
|
+
|
|
+ # First apply any services this service may include
|
|
+ if included_services is None:
|
|
+ included_services = [service]
|
|
+ for include in svc.includes:
|
|
+ if include in included_services:
|
|
+ continue
|
|
+ self.check_service(include)
|
|
+ included_services.append(include)
|
|
+ self._service(enable, policy, include, transaction, included_services=included_services)
|
|
+
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "raw", "PREROUTING")
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+
|
|
+ # build a list of (backend, destination). The destination may be ipv4,
|
|
+ # ipv6 or None
|
|
+ #
|
|
+ backends_ipv = []
|
|
+ for ipv in ["ipv4", "ipv6"]:
|
|
+ if not self._fw.is_ipv_enabled(ipv):
|
|
+ continue
|
|
+ backend = self._fw.get_backend_by_ipv(ipv)
|
|
+ if len(svc.destination) > 0:
|
|
+ if ipv in svc.destination:
|
|
+ backends_ipv.append((backend, svc.destination[ipv]))
|
|
+ else:
|
|
+ if (backend, None) not in backends_ipv:
|
|
+ backends_ipv.append((backend, None))
|
|
+
|
|
+ for (backend,destination) in backends_ipv:
|
|
+ for helper in helpers:
|
|
+ module = helper.module
|
|
+ _module_short_name = get_nf_conntrack_short_name(module)
|
|
+ nat_module = helper.module.replace("conntrack", "nat")
|
|
+ transaction.add_module(nat_module)
|
|
+ if helper.family != "" and not backend.is_ipv_supported(helper.family):
|
|
+ # no support for family ipv, continue
|
|
+ continue
|
|
+ if len(helper.ports) < 1:
|
|
+ transaction.add_module(module)
|
|
+ else:
|
|
+ for (port,proto) in helper.ports:
|
|
+ rules = backend.build_policy_helper_ports_rules(
|
|
+ enable, policy, proto, port,
|
|
+ destination, helper.name, _module_short_name)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ for (port,proto) in svc.ports:
|
|
+ rules = backend.build_policy_ports_rules(enable, policy, proto,
|
|
+ port, destination)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ for protocol in svc.protocols:
|
|
+ rules = backend.build_policy_protocol_rules(
|
|
+ enable, policy, protocol, destination)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ for (port,proto) in svc.source_ports:
|
|
+ rules = backend.build_policy_source_ports_rules(
|
|
+ enable, policy, proto, port, destination)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _port(self, enable, policy, port, protocol, transaction):
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+
|
|
+ for backend in self._fw.enabled_backends():
|
|
+ if not backend.policies_supported:
|
|
+ continue
|
|
+
|
|
+ rules = backend.build_policy_ports_rules(enable, policy, protocol,
|
|
+ port)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _protocol(self, enable, policy, protocol, transaction):
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+
|
|
+ for backend in self._fw.enabled_backends():
|
|
+ if not backend.policies_supported:
|
|
+ continue
|
|
+
|
|
+ rules = backend.build_policy_protocol_rules(enable, policy, protocol)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _source_port(self, enable, policy, port, protocol, transaction):
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+
|
|
+ for backend in self._fw.enabled_backends():
|
|
+ if not backend.policies_supported:
|
|
+ continue
|
|
+
|
|
+ rules = backend.build_policy_source_ports_rules(enable, policy, protocol, port)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _masquerade(self, enable, policy, transaction):
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "nat", "POSTROUTING")
|
|
+ transaction.add_chain(policy, "filter", "FORWARD_OUT")
|
|
+
|
|
+ ipv = "ipv4"
|
|
+ transaction.add_post(enable_ip_forwarding, ipv)
|
|
+
|
|
+ backend = self._fw.get_backend_by_ipv(ipv)
|
|
+ rules = backend.build_policy_masquerade_rules(enable, policy)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _forward_port(self, enable, policy, transaction, port, protocol,
|
|
+ toport=None, toaddr=None):
|
|
+ if check_single_address("ipv6", toaddr):
|
|
+ ipv = "ipv6"
|
|
+ else:
|
|
+ ipv = "ipv4"
|
|
+
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "nat", "PREROUTING")
|
|
+
|
|
+ if toaddr and enable:
|
|
+ transaction.add_post(enable_ip_forwarding, ipv)
|
|
+ backend = self._fw.get_backend_by_ipv(ipv)
|
|
+ rules = backend.build_policy_forward_port_rules(
|
|
+ enable, policy, port, protocol, toport,
|
|
+ toaddr)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _icmp_block(self, enable, policy, icmp, transaction):
|
|
+ ict = self._fw.icmptype.get_icmptype(icmp)
|
|
+
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+ transaction.add_chain(policy, "filter", "FORWARD_IN")
|
|
+
|
|
+ for backend in self._fw.enabled_backends():
|
|
+ if not backend.policies_supported:
|
|
+ continue
|
|
+ skip_backend = False
|
|
+
|
|
+ if ict.destination:
|
|
+ for ipv in ["ipv4", "ipv6"]:
|
|
+ if ipv in ict.destination:
|
|
+ if not backend.is_ipv_supported(ipv):
|
|
+ skip_backend = True
|
|
+ break
|
|
+
|
|
+ if skip_backend:
|
|
+ continue
|
|
+
|
|
+ rules = backend.build_policy_icmp_block_rules(enable, policy, ict)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _icmp_block_inversion(self, enable, policy, transaction):
|
|
+ target = self._policies[policy].target
|
|
+
|
|
+ # Do not add general icmp accept rules into a trusted, block or drop
|
|
+ # policy.
|
|
+ if target in [ "DROP", "%%REJECT%%", "REJECT" ]:
|
|
+ return
|
|
+ if not self.query_icmp_block_inversion(policy) and target == "ACCEPT":
|
|
+ # ibi target and policy target are ACCEPT, no need to add an extra
|
|
+ # rule
|
|
+ return
|
|
+
|
|
+ transaction.add_chain(policy, "filter", "INPUT")
|
|
+ transaction.add_chain(policy, "filter", "FORWARD_IN")
|
|
+
|
|
+ for backend in self._fw.enabled_backends():
|
|
+ if not backend.policies_supported:
|
|
+ continue
|
|
+
|
|
+ rules = backend.build_policy_icmp_block_inversion_rules(enable, policy)
|
|
+ transaction.add_rules(backend, rules)
|
|
+
|
|
+ def _get_table_chains_for_zone_dispatch(self, policy):
|
|
+ """Create a list of (table, chain) needed for zone dispatch"""
|
|
+ obj = self._policies[policy]
|
|
+ if obj.egress_zones[0] == "HOST":
|
|
+ # zone --> Host
|
|
+ return [("filter", "INPUT")]
|
|
+ elif obj.egress_zones[0] == "ANY":
|
|
+ # zone --> any
|
|
+ return [("filter", "FORWARD_IN"), ("nat", "PREROUTING"),
|
|
+ ("mangle", "PREROUTING"), ("raw", "PREROUTING")]
|
|
+ elif obj.ingress_zones[0] == "ANY":
|
|
+ # any --> zone
|
|
+ return [("filter", "FORWARD_OUT"), ("nat", "POSTROUTING")]
|
|
+ else:
|
|
+ return FirewallError("Invalid policy: %s" % (policy))
|
|
+
|
|
+ def policy_base_chain_name(self, policy, table, policy_prefix):
|
|
+ obj = self._fw.policy.get_policy(policy)
|
|
+ if obj.derived_from_zone:
|
|
+ if obj.egress_zones[0] == "HOST":
|
|
+ # zone --> Host
|
|
+ if table == "filter":
|
|
+ return "IN_" + obj.derived_from_zone
|
|
+ if table == "raw":
|
|
+ # FIXME: nftables doesn't actually use this. Only iptables
|
|
+ return "PRE_" + obj.derived_from_zone
|
|
+ elif obj.egress_zones[0] == "ANY":
|
|
+ # zone --> any
|
|
+ if table == "filter":
|
|
+ return "FWDI_" + obj.derived_from_zone
|
|
+ elif table in ["nat", "mangle", "raw"]:
|
|
+ return "PRE_" + obj.derived_from_zone
|
|
+ elif obj.ingress_zones[0] == "ANY":
|
|
+ # any --> zone
|
|
+ if table == "filter":
|
|
+ return "FWDO_" + obj.derived_from_zone
|
|
+ elif table == "nat":
|
|
+ return "POST_" + obj.derived_from_zone
|
|
+ return FirewallError("Can't convert policy to chain name: %s" % (policy))
|
|
+ else:
|
|
+ return policy_prefix + policy
|
|
diff --git a/src/firewall/core/fw_transaction.py b/src/firewall/core/fw_transaction.py
|
|
index cc8b1e1..7639cdb 100644
|
|
--- a/src/firewall/core/fw_transaction.py
|
|
+++ b/src/firewall/core/fw_transaction.py
|
|
@@ -36,7 +36,7 @@ class FirewallTransaction(object):
|
|
self.pre_funcs = [ ] # [ (func, args),.. ]
|
|
self.post_funcs = [ ] # [ (func, args),.. ]
|
|
self.fail_funcs = [ ] # [ (func, args),.. ]
|
|
- self.chains = [ ] # [ (zone, table, chain),.. ]
|
|
+ self.chains = [ ] # [ (table, policy),.. ]
|
|
self.modules = [ ] # [ module,.. ]
|
|
|
|
def clear(self):
|
|
@@ -68,16 +68,16 @@ class FirewallTransaction(object):
|
|
def add_fail(self, func, *args):
|
|
self.fail_funcs.append((func, args))
|
|
|
|
- def add_chain(self, zone, table, chain):
|
|
- ztc = (zone, table, chain)
|
|
- if ztc not in self.chains:
|
|
- self.fw.zone.gen_chain_rules(zone, True, table, chain, self)
|
|
- self.chains.append(ztc)
|
|
+ def add_chain(self, policy, table, chain):
|
|
+ tp = (table, policy)
|
|
+ if tp not in self.chains:
|
|
+ self.fw.policy.gen_chain_rules(policy, True, table, chain, self)
|
|
+ self.chains.append(tp)
|
|
|
|
- def remove_chain(self, zone, table, chain):
|
|
- ztc = (zone, table, chain)
|
|
- if ztc in self.chains:
|
|
- self.chains.remove(ztc)
|
|
+ def remove_chain(self, policy, table, chain):
|
|
+ tp = (table, policy)
|
|
+ if tp in self.chains:
|
|
+ self.chains.remove(tp)
|
|
|
|
def add_module(self, module):
|
|
if module not in self.modules:
|
|
diff --git a/src/firewall/core/fw_zone.py b/src/firewall/core/fw_zone.py
|
|
index d32d7a8..2f47e33 100644
|
|
--- a/src/firewall/core/fw_zone.py
|
|
+++ b/src/firewall/core/fw_zone.py
|
|
@@ -20,39 +20,39 @@
|
|
#
|
|
|
|
import time
|
|
-from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET, \
|
|
- ZONE_SOURCE_IPSET_TYPES
|
|
-from firewall.core.logger import log
|
|
-from firewall.functions import portStr, checkIPnMask, checkIP6nMask, \
|
|
- checkProtocol, enable_ip_forwarding, check_single_address, check_mac, \
|
|
- portInPortRange, get_nf_conntrack_short_name, coalescePortRange, breakPortRange
|
|
-from firewall.core.rich import Rich_Rule, Rich_Accept, \
|
|
- Rich_Mark, Rich_Service, Rich_Port, Rich_Protocol, \
|
|
- Rich_Masquerade, Rich_ForwardPort, Rich_SourcePort, Rich_IcmpBlock, \
|
|
- Rich_IcmpType
|
|
+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._chains = { }
|
|
self._zones = { }
|
|
+ self._zone_policies = { }
|
|
|
|
def __repr__(self):
|
|
- return '%s(%r, %r)' % (self.__class__, self._chains, self._zones)
|
|
+ return '%s(%r)' % (self.__class__, self._zones)
|
|
|
|
def cleanup(self):
|
|
- self._chains.clear()
|
|
self._zones.clear()
|
|
-
|
|
- # transaction
|
|
+ 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):
|
|
@@ -78,24 +78,77 @@ class FirewallZone(object):
|
|
z = self._fw.check_zone(zone)
|
|
return self._zones[z]
|
|
|
|
- def _first_except(self, e, f, name, *args, **kwargs):
|
|
- try:
|
|
- f(name, *args, **kwargs)
|
|
- except FirewallError as error:
|
|
- if not e:
|
|
- return error
|
|
- return e
|
|
+ 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",
|
|
- "services", "ports",
|
|
- "masquerade", "forward_ports",
|
|
- "source_ports",
|
|
- "icmp_blocks", "rules",
|
|
- "protocols", "icmp_block_inversion" ] }
|
|
-
|
|
+ for x in ["interfaces", "sources",
|
|
+ "icmp_block_inversion"] }
|
|
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.icmp_block_inversion:
|
|
+ self.add_icmp_block_inversion(zone)
|
|
|
|
def remove_zone(self, zone):
|
|
obj = self._zones[zone]
|
|
@@ -103,68 +156,14 @@ class FirewallZone(object):
|
|
self.unapply_zone_settings(zone)
|
|
obj.settings.clear()
|
|
del self._zones[zone]
|
|
+ del self._zone_policies[zone]
|
|
|
|
def apply_zones(self, use_transaction=None):
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- error = None
|
|
for zone in self.get_zones():
|
|
- obj = self._zones[zone]
|
|
-
|
|
- # register icmp block inversion setting but don't apply
|
|
- if obj.icmp_block_inversion:
|
|
- error = self._first_except(error, self.add_icmp_block_inversion, obj.name,
|
|
- use_transaction=transaction)
|
|
-
|
|
- if len(obj.interfaces) > 0 or len(obj.sources) > 0:
|
|
- obj.applied = True
|
|
-
|
|
- log.debug1("Applying zone '%s'", obj.name)
|
|
-
|
|
- # load zone in case of missing services, icmptypes etc.
|
|
- for args in obj.icmp_blocks:
|
|
- error = self._first_except(error, self.add_icmp_block, obj.name, args,
|
|
- use_transaction=transaction)
|
|
- for args in obj.forward_ports:
|
|
- error = self._first_except(error, self.add_forward_port, obj.name, *args,
|
|
- use_transaction=transaction)
|
|
- for args in obj.services:
|
|
- error = self._first_except(error, self.add_service, obj.name, args,
|
|
- use_transaction=transaction)
|
|
- for args in obj.ports:
|
|
- error = self._first_except(error, self.add_port, obj.name, *args,
|
|
- use_transaction=transaction)
|
|
- for args in obj.protocols:
|
|
- error = self._first_except(error, self.add_protocol, obj.name, args,
|
|
- use_transaction=transaction)
|
|
- for args in obj.source_ports:
|
|
- error = self._first_except(error, self.add_source_port, obj.name, *args,
|
|
- use_transaction=transaction)
|
|
- if obj.masquerade:
|
|
- error = self._first_except(error, self.add_masquerade, obj.name,
|
|
- use_transaction=transaction)
|
|
- for args in obj.rules:
|
|
- error = self._first_except(error, self.add_rule, obj.name, args,
|
|
- use_transaction=transaction)
|
|
- for args in obj.interfaces:
|
|
- error = self._first_except(error, self.add_interface, obj.name, args,
|
|
- use_transaction=transaction)
|
|
- for args in obj.sources:
|
|
- error = self._first_except(error, self.add_source, obj.name, args,
|
|
- use_transaction=transaction)
|
|
- # apply icmp accept/reject rule always
|
|
- if obj.applied:
|
|
- error = self._first_except(error, self._icmp_block_inversion, True,
|
|
- obj.name, transaction)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- if error:
|
|
- raise error
|
|
+ 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]
|
|
@@ -212,19 +211,6 @@ class FirewallZone(object):
|
|
if use_transaction is None:
|
|
transaction.execute(True)
|
|
|
|
- # dynamic chain handling
|
|
-
|
|
- def _register_chains(self, zone, create, chains):
|
|
- for (table, chain) in chains:
|
|
- if create:
|
|
- self._chains.setdefault(zone, { }).setdefault(table, [ ]).append(chain)
|
|
- else:
|
|
- self._chains[zone][table].remove(chain)
|
|
- if len(self._chains[zone][table]) == 0:
|
|
- del self._chains[zone][table]
|
|
- if len(self._chains[zone]) == 0:
|
|
- del self._chains[zone]
|
|
-
|
|
# settings
|
|
|
|
# generate settings record with sender, timeout
|
|
@@ -239,114 +225,62 @@ class FirewallZone(object):
|
|
def get_settings(self, zone):
|
|
return self.get_zone(zone).settings
|
|
|
|
- def set_settings(self, zone, settings):
|
|
- _obj = self.get_zone(zone)
|
|
-
|
|
- try:
|
|
- for key in settings:
|
|
- for args in settings[key]:
|
|
- if args in _obj.settings[key]:
|
|
- # do not add things, that are already active in the
|
|
- # zone configuration, also do not restore date,
|
|
- # sender and timeout
|
|
- continue
|
|
- if key == "icmp_blocks":
|
|
- self.add_icmp_block(zone, args)
|
|
- elif key == "forward_ports":
|
|
- self.add_forward_port(zone, *args)
|
|
- elif key == "services":
|
|
- self.add_service(zone, args)
|
|
- elif key == "ports":
|
|
- self.add_port(zone, *args)
|
|
- elif key == "protocols":
|
|
- self.add_protocol(zone, *args)
|
|
- elif key == "source_ports":
|
|
- self.add_source_port(zone, *args)
|
|
- elif key == "masquerade":
|
|
- self.add_masquerade(zone)
|
|
- elif key == "rules":
|
|
- self.add_rule(zone, Rich_Rule(rule_str=args))
|
|
- elif key == "interfaces":
|
|
- self.change_zone_of_interface(zone, args)
|
|
- elif key == "sources":
|
|
- self.change_zone_of_source(zone, args)
|
|
- else:
|
|
- log.warning("Zone '%s': Unknown setting '%s:%s', "
|
|
- "unable to restore.", zone, key, args)
|
|
- # restore old date, sender and timeout
|
|
- if args in _obj.settings[key]:
|
|
- _obj.settings[key][args] = settings[key][args]
|
|
-
|
|
- except FirewallError as msg:
|
|
- log.warning(str(msg))
|
|
-
|
|
- def __zone_settings(self, enable, zone, use_transaction=None):
|
|
+ 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 apply_zone_settings(self, zone, use_transaction=None):
|
|
_zone = self._fw.check_zone(zone)
|
|
obj = self._zones[_zone]
|
|
- if (enable and obj.applied) or (not enable and not obj.applied):
|
|
+ if obj.applied:
|
|
return
|
|
- if enable:
|
|
- obj.applied = True
|
|
+ obj.applied = True
|
|
|
|
if use_transaction is None:
|
|
transaction = self.new_transaction()
|
|
else:
|
|
transaction = use_transaction
|
|
|
|
- settings = self.get_settings(zone)
|
|
- for key in settings:
|
|
- for args in settings[key]:
|
|
- try:
|
|
- if key == "icmp_blocks":
|
|
- self._icmp_block(enable, _zone, args, transaction)
|
|
- elif key == "icmp_block_inversion":
|
|
- continue
|
|
- elif key == "forward_ports":
|
|
- self._forward_port(enable, _zone, transaction,
|
|
- *args)
|
|
- elif key == "services":
|
|
- self._service(enable, _zone, args, transaction)
|
|
- elif key == "ports":
|
|
- self._port(enable, _zone, args[0], args[1],
|
|
- transaction)
|
|
- elif key == "protocols":
|
|
- self._protocol(enable, _zone, args, transaction)
|
|
- elif key == "source_ports":
|
|
- self._source_port(enable, _zone, args[0], args[1],
|
|
- transaction)
|
|
- elif key == "masquerade":
|
|
- self._masquerade(enable, _zone, transaction)
|
|
- elif key == "rules":
|
|
- self.__rule(enable, _zone, Rich_Rule(rule_str=args),
|
|
- transaction)
|
|
- elif key == "interfaces":
|
|
- self._interface(enable, _zone, args, transaction)
|
|
- elif key == "sources":
|
|
- self._source(enable, _zone, args[0], args[1],
|
|
- transaction)
|
|
- else:
|
|
- log.warning("Zone '%s': Unknown setting '%s:%s', "
|
|
- "unable to apply", zone, key, args)
|
|
- except FirewallError as msg:
|
|
- log.warning(str(msg))
|
|
+ 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)
|
|
|
|
- if enable:
|
|
- # add icmp rule(s) always
|
|
- self._icmp_block_inversion(True, obj.name, transaction)
|
|
+ self._zone_settings(True, _zone, transaction)
|
|
|
|
if use_transaction is None:
|
|
- transaction.execute(enable)
|
|
-
|
|
- def apply_zone_settings(self, zone, use_transaction=None):
|
|
- self.__zone_settings(True, zone, use_transaction)
|
|
+ transaction.execute(True)
|
|
|
|
def unapply_zone_settings(self, zone, use_transaction=None):
|
|
- self.__zone_settings(False, zone, use_transaction)
|
|
+ _zone = self._fw.check_zone(zone)
|
|
+ obj = self._zones[_zone]
|
|
+ if not obj.applied:
|
|
+ return
|
|
|
|
- def unapply_zone_settings_if_unused(self, zone):
|
|
- obj = self._zones[zone]
|
|
- if len(obj.interfaces) == 0 and len(obj.sources) == 0:
|
|
- self.unapply_zone_settings(zone)
|
|
+ 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):
|
|
"""
|
|
@@ -390,7 +324,7 @@ class FirewallZone(object):
|
|
return interface
|
|
|
|
def add_interface(self, zone, interface, sender=None,
|
|
- use_transaction=None):
|
|
+ use_transaction=None, allow_apply=True):
|
|
self._fw.check_panic()
|
|
_zone = self._fw.check_zone(zone)
|
|
_obj = self._zones[_zone]
|
|
@@ -413,12 +347,13 @@ class FirewallZone(object):
|
|
else:
|
|
transaction = use_transaction
|
|
|
|
- if not _obj.applied:
|
|
+ if not _obj.applied and allow_apply:
|
|
self.apply_zone_settings(zone,
|
|
use_transaction=transaction)
|
|
transaction.add_fail(self.set_zone_applied, _zone, False)
|
|
|
|
- self._interface(True, _zone, interface, transaction)
|
|
+ if allow_apply:
|
|
+ self._interface(True, _zone, interface, transaction)
|
|
|
|
self.__register_interface(_obj, interface_id, zone, sender)
|
|
transaction.add_fail(self.__unregister_interface, _obj,
|
|
@@ -494,7 +429,6 @@ class FirewallZone(object):
|
|
if use_transaction is None:
|
|
transaction.execute(True)
|
|
|
|
-# self.unapply_zone_settings_if_unused(_zone)
|
|
return _zone
|
|
|
|
def __unregister_interface(self, _obj, interface_id):
|
|
@@ -509,7 +443,7 @@ class FirewallZone(object):
|
|
|
|
# SOURCES
|
|
|
|
- def check_source(self, source):
|
|
+ def check_source(self, source, applied=False):
|
|
if checkIPnMask(source):
|
|
return "ipv4"
|
|
elif checkIP6nMask(source):
|
|
@@ -518,16 +452,18 @@ class FirewallZone(object):
|
|
return ""
|
|
elif source.startswith("ipset:"):
|
|
self._check_ipset_type_for_source(source[6:])
|
|
- self._check_ipset_applied(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):
|
|
- ipv = self.check_source(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):
|
|
+ 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]
|
|
@@ -535,7 +471,7 @@ class FirewallZone(object):
|
|
if check_mac(source):
|
|
source = source.upper()
|
|
|
|
- source_id = self.__source_id(source)
|
|
+ source_id = self.__source_id(source, applied=allow_apply)
|
|
|
|
if source_id in _obj.settings["sources"]:
|
|
raise FirewallError(errors.ZONE_ALREADY_SET,
|
|
@@ -549,12 +485,13 @@ class FirewallZone(object):
|
|
else:
|
|
transaction = use_transaction
|
|
|
|
- if not _obj.applied:
|
|
+ if not _obj.applied and allow_apply:
|
|
self.apply_zone_settings(zone,
|
|
use_transaction=transaction)
|
|
transaction.add_fail(self.set_zone_applied, _zone, False)
|
|
|
|
- self._source(True, _zone, source_id[0], source_id[1], transaction)
|
|
+ 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)
|
|
@@ -617,7 +554,6 @@ class FirewallZone(object):
|
|
if use_transaction is None:
|
|
transaction.execute(True)
|
|
|
|
-# self.unapply_zone_settings_if_unused(_zone)
|
|
return _zone
|
|
|
|
def __unregister_source(self, _obj, source_id):
|
|
@@ -632,1328 +568,296 @@ class FirewallZone(object):
|
|
def list_sources(self, zone):
|
|
return [ k[1] for k in self.get_settings(zone)["sources"].keys() ]
|
|
|
|
- # RICH LANGUAGE
|
|
+ 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):
|
|
+ # create needed chains if not done already
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, table, chain)
|
|
|
|
- def check_rule(self, rule):
|
|
- rule.check()
|
|
+ rules = backend.build_zone_source_interface_rules(enable,
|
|
+ zone, policy, interface, table, chain, append)
|
|
+ transaction.add_rules(backend, rules)
|
|
|
|
- def __rule_id(self, rule):
|
|
- self.check_rule(rule)
|
|
- return str(rule)
|
|
+ # IPSETS
|
|
|
|
- def _rule_source_ipv(self, source):
|
|
- if not source:
|
|
+ def _ipset_family(self, name):
|
|
+ if self._ipset_type(name) == "hash:mac":
|
|
return None
|
|
+ return self._fw.ipset.get_family(name, applied=False)
|
|
|
|
- if source.addr:
|
|
- if checkIPnMask(source.addr):
|
|
- return "ipv4"
|
|
- elif checkIP6nMask(source.addr):
|
|
- return "ipv6"
|
|
- elif hasattr(source, "mac") and source.mac:
|
|
- return ""
|
|
- elif hasattr(source, "ipset") and source.ipset:
|
|
- self._check_ipset_type_for_source(source.ipset)
|
|
- self._check_ipset_applied(source.ipset)
|
|
- return self._ipset_family(source.ipset)
|
|
-
|
|
- return None
|
|
-
|
|
- def __rule(self, enable, zone, rule, transaction):
|
|
- self._rule_prepare(enable, zone, rule, transaction)
|
|
-
|
|
- def add_rule(self, zone, rule, 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]
|
|
-
|
|
- rule_id = self.__rule_id(rule)
|
|
- if rule_id in _obj.settings["rules"]:
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "'%s' already in '%s'" % (rule, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self.__rule(True, _zone, rule, transaction)
|
|
-
|
|
- self.__register_rule(_obj, rule_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_rule, _obj, rule_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_rule(self, _obj, rule_id, timeout, sender):
|
|
- _obj.settings["rules"][rule_id] = self.__gen_settings(
|
|
- timeout, sender)
|
|
-
|
|
- def remove_rule(self, zone, rule,
|
|
- use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- rule_id = self.__rule_id(rule)
|
|
- if rule_id not in _obj.settings["rules"]:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "'%s' not in '%s'" % (rule, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self.__rule(False, _zone, rule, transaction)
|
|
-
|
|
- transaction.add_post(self.__unregister_rule, _obj, rule_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __unregister_rule(self, _obj, rule_id):
|
|
- if rule_id in _obj.settings["rules"]:
|
|
- del _obj.settings["rules"][rule_id]
|
|
-
|
|
- def query_rule(self, zone, rule):
|
|
- return self.__rule_id(rule) in self.get_settings(zone)["rules"]
|
|
-
|
|
- def list_rules(self, zone):
|
|
- return list(self.get_settings(zone)["rules"].keys())
|
|
-
|
|
- # SERVICES
|
|
-
|
|
- def check_service(self, service):
|
|
- self._fw.check_service(service)
|
|
-
|
|
- def __service_id(self, service):
|
|
- self.check_service(service)
|
|
- return service
|
|
-
|
|
- def add_service(self, zone, service, 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]
|
|
-
|
|
- service_id = self.__service_id(service)
|
|
- if service_id in _obj.settings["services"]:
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "'%s' already in '%s'" % (service, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self._service(True, _zone, service, transaction)
|
|
-
|
|
- self.__register_service(_obj, service_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_service, _obj, service_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_service(self, _obj, service_id, timeout, sender):
|
|
- _obj.settings["services"][service_id] = \
|
|
- self.__gen_settings(timeout, sender)
|
|
-
|
|
- def remove_service(self, zone, service,
|
|
- use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
+ def _ipset_type(self, name):
|
|
+ return self._fw.ipset.get_type(name, applied=False)
|
|
|
|
- service_id = self.__service_id(service)
|
|
- if service_id not in _obj.settings["services"]:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "'%s' not in '%s'" % (service, _zone))
|
|
+ def _ipset_match_flags(self, name, flag):
|
|
+ return ",".join([flag] * self._fw.ipset.get_dimension(name))
|
|
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
+ def _check_ipset_applied(self, name):
|
|
+ return self._fw.ipset.check_applied(name)
|
|
|
|
- if _obj.applied:
|
|
- self._service(False, _zone, service, transaction)
|
|
+ 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))
|
|
|
|
- transaction.add_post(self.__unregister_service, _obj, service_id)
|
|
+ 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):
|
|
+ # create needed chains if not done already
|
|
+ if enable:
|
|
+ transaction.add_chain(policy, table, chain)
|
|
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
+ rules = backend.build_zone_source_address_rules(enable, zone,
|
|
+ policy, source, table, chain)
|
|
+ transaction.add_rules(backend, rules)
|
|
|
|
- return _zone
|
|
+ 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 __unregister_service(self, _obj, service_id):
|
|
- if service_id in _obj.settings["services"]:
|
|
- del _obj.settings["services"][service_id]
|
|
+ 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):
|
|
- return self.__service_id(service) in self.get_settings(zone)["services"]
|
|
+ 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):
|
|
- return self.get_settings(zone)["services"].keys()
|
|
-
|
|
- def get_helpers_for_service_helpers(self, helpers):
|
|
- _helpers = [ ]
|
|
- for helper in helpers:
|
|
- try:
|
|
- _helper = self._fw.helper.get_helper(helper)
|
|
- except FirewallError:
|
|
- raise FirewallError(errors.INVALID_HELPER, helper)
|
|
- _helpers.append(_helper)
|
|
- return _helpers
|
|
-
|
|
- def get_helpers_for_service_modules(self, modules, enable):
|
|
- # If automatic helper assignment is turned off, helpers that
|
|
- # do not have ports defined will be replaced by the helpers
|
|
- # that the helper.module defines.
|
|
- _helpers = [ ]
|
|
- for module in modules:
|
|
- try:
|
|
- helper = self._fw.helper.get_helper(module)
|
|
- except FirewallError:
|
|
- raise FirewallError(errors.INVALID_HELPER, module)
|
|
- if len(helper.ports) < 1:
|
|
- _module_short_name = get_nf_conntrack_short_name(helper.module)
|
|
- try:
|
|
- _helper = self._fw.helper.get_helper(_module_short_name)
|
|
- _helpers.append(_helper)
|
|
- except FirewallError:
|
|
- if enable:
|
|
- log.warning("Helper '%s' is not available" % _module_short_name)
|
|
- continue
|
|
- else:
|
|
- _helpers.append(helper)
|
|
- return _helpers
|
|
-
|
|
- # PORTS
|
|
-
|
|
- def check_port(self, port, protocol):
|
|
- self._fw.check_port(port)
|
|
- self._fw.check_tcpudp(protocol)
|
|
-
|
|
- def __port_id(self, port, protocol):
|
|
- self.check_port(port, protocol)
|
|
- return (portStr(port, "-"), protocol)
|
|
-
|
|
- def add_port(self, zone, port, protocol, 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]
|
|
-
|
|
- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"]))
|
|
- for port_id in existing_port_ids:
|
|
- if portInPortRange(port, port_id[0]):
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "'%s:%s' already in '%s'" % (port, protocol, _zone))
|
|
-
|
|
- added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- for range in added_ranges:
|
|
- self._port(True, _zone, portStr(range, "-"), protocol, transaction)
|
|
- for range in removed_ranges:
|
|
- self._port(False, _zone, portStr(range, "-"), protocol, transaction)
|
|
-
|
|
- for range in added_ranges:
|
|
- port_id = self.__port_id(range, protocol)
|
|
- self.__register_port(_obj, port_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_port, _obj, port_id)
|
|
- for range in removed_ranges:
|
|
- port_id = self.__port_id(range, protocol)
|
|
- transaction.add_post(self.__unregister_port, _obj, port_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_port(self, _obj, port_id, timeout, sender):
|
|
- _obj.settings["ports"][port_id] = \
|
|
- self.__gen_settings(timeout, sender)
|
|
-
|
|
- def remove_port(self, zone, port, protocol,
|
|
- use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"]))
|
|
- for port_id in existing_port_ids:
|
|
- if portInPortRange(port, port_id[0]):
|
|
- break
|
|
- else:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "'%s:%s' not in '%s'" % (port, protocol, _zone))
|
|
-
|
|
- added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- for range in added_ranges:
|
|
- self._port(True, _zone, portStr(range, "-"), protocol, transaction)
|
|
- for range in removed_ranges:
|
|
- self._port(False, _zone, portStr(range, "-"), protocol, transaction)
|
|
-
|
|
- for range in added_ranges:
|
|
- port_id = self.__port_id(range, protocol)
|
|
- self.__register_port(_obj, port_id, 0, None)
|
|
- transaction.add_fail(self.__unregister_port, _obj, port_id)
|
|
- for range in removed_ranges:
|
|
- port_id = self.__port_id(range, protocol)
|
|
- transaction.add_post(self.__unregister_port, _obj, port_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __unregister_port(self, _obj, port_id):
|
|
- if port_id in _obj.settings["ports"]:
|
|
- del _obj.settings["ports"][port_id]
|
|
+ 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):
|
|
- for (_port, _protocol) in self.get_settings(zone)["ports"]:
|
|
- if portInPortRange(port, _port) and protocol == _protocol:
|
|
- return True
|
|
-
|
|
- return False
|
|
+ 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):
|
|
- return list(self.get_settings(zone)["ports"].keys())
|
|
-
|
|
- # PROTOCOLS
|
|
-
|
|
- def check_protocol(self, protocol):
|
|
- if not checkProtocol(protocol):
|
|
- raise FirewallError(errors.INVALID_PROTOCOL, protocol)
|
|
-
|
|
- def __protocol_id(self, protocol):
|
|
- self.check_protocol(protocol)
|
|
- return protocol
|
|
-
|
|
- def add_protocol(self, zone, protocol, 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]
|
|
-
|
|
- protocol_id = self.__protocol_id(protocol)
|
|
- if protocol_id in _obj.settings["protocols"]:
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "'%s' already in '%s'" % (protocol, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
+ 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)
|
|
|
|
- if _obj.applied:
|
|
- self._protocol(True, _zone, protocol, transaction)
|
|
-
|
|
- self.__register_protocol(_obj, protocol_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_protocol, _obj, protocol_id)
|
|
+ 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
|
|
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
+ 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
|
|
|
|
- return _zone
|
|
+ def list_rules(self, 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 __register_protocol(self, _obj, protocol_id, timeout, sender):
|
|
- _obj.settings["protocols"][protocol_id] = \
|
|
- self.__gen_settings(timeout, sender)
|
|
+ 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 remove_protocol(self, zone, protocol,
|
|
- use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
+ 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
|
|
|
|
- protocol_id = self.__protocol_id(protocol)
|
|
- if protocol_id not in _obj.settings["protocols"]:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "'%s' not in '%s'" % (protocol, _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)
|
|
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
+ 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
|
|
|
|
- if _obj.applied:
|
|
- self._protocol(False, _zone, protocol, transaction)
|
|
+ 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
|
|
|
|
- transaction.add_post(self.__unregister_protocol, _obj,
|
|
- protocol_id)
|
|
+ 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)
|
|
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
+ 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)
|
|
|
|
- return _zone
|
|
+ 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)
|
|
|
|
- def __unregister_protocol(self, _obj, protocol_id):
|
|
- if protocol_id in _obj.settings["protocols"]:
|
|
- del _obj.settings["protocols"][protocol_id]
|
|
+ p_name = self.policy_name_from_zones(zone, "ANY")
|
|
+ self._fw.policy.add_icmp_block(p_name, icmp, timeout, sender)
|
|
+ return zone
|
|
|
|
- def query_protocol(self, zone, protocol):
|
|
- return self.__protocol_id(protocol) in self.get_settings(zone)["protocols"]
|
|
+ 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)
|
|
|
|
- def list_protocols(self, zone):
|
|
- return list(self.get_settings(zone)["protocols"].keys())
|
|
+ p_name = self.policy_name_from_zones(zone, "ANY")
|
|
+ self._fw.policy.remove_icmp_block(p_name, icmp)
|
|
+ return zone
|
|
|
|
- # SOURCE PORTS
|
|
+ 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 __source_port_id(self, port, protocol):
|
|
- self.check_port(port, protocol)
|
|
- return (portStr(port, "-"), protocol)
|
|
+ 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_source_port(self, zone, port, protocol, 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]
|
|
+ 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)
|
|
|
|
- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"]))
|
|
- for port_id in existing_port_ids:
|
|
- if portInPortRange(port, port_id[0]):
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "'%s:%s' already in '%s'" % (port, protocol, _zone))
|
|
+ p_name = self.policy_name_from_zones(zone, "ANY")
|
|
+ self._fw.policy.add_icmp_block_inversion(p_name, sender)
|
|
+ return zone
|
|
|
|
- added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
+ 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)
|
|
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
+ p_name = self.policy_name_from_zones(zone, "ANY")
|
|
+ self._fw.policy._icmp_block_inversion(enable, p_name, transaction)
|
|
|
|
- if _obj.applied:
|
|
- for range in added_ranges:
|
|
- self._source_port(True, _zone, portStr(range, "-"), protocol, transaction)
|
|
- for range in removed_ranges:
|
|
- self._source_port(False, _zone, portStr(range, "-"), protocol, 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)
|
|
|
|
- for range in added_ranges:
|
|
- port_id = self.__source_port_id(range, protocol)
|
|
- self.__register_source_port(_obj, port_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_source_port, _obj, port_id)
|
|
- for range in removed_ranges:
|
|
- port_id = self.__source_port_id(range, protocol)
|
|
- transaction.add_post(self.__unregister_source_port, _obj, port_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_source_port(self, _obj, port_id, timeout, sender):
|
|
- _obj.settings["source_ports"][port_id] = \
|
|
- self.__gen_settings(timeout, sender)
|
|
-
|
|
- def remove_source_port(self, zone, port, protocol,
|
|
- use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"]))
|
|
- for port_id in existing_port_ids:
|
|
- if portInPortRange(port, port_id[0]):
|
|
- break
|
|
- else:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "'%s:%s' not in '%s'" % (port, protocol, _zone))
|
|
-
|
|
- added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids])
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- for range in added_ranges:
|
|
- self._source_port(True, _zone, portStr(range, "-"), protocol, transaction)
|
|
- for range in removed_ranges:
|
|
- self._source_port(False, _zone, portStr(range, "-"), protocol, transaction)
|
|
-
|
|
- for range in added_ranges:
|
|
- port_id = self.__source_port_id(range, protocol)
|
|
- self.__register_source_port(_obj, port_id, 0, None)
|
|
- transaction.add_fail(self.__unregister_source_port, _obj, port_id)
|
|
- for range in removed_ranges:
|
|
- port_id = self.__source_port_id(range, protocol)
|
|
- transaction.add_post(self.__unregister_source_port, _obj, port_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __unregister_source_port(self, _obj, port_id):
|
|
- if port_id in _obj.settings["source_ports"]:
|
|
- del _obj.settings["source_ports"][port_id]
|
|
-
|
|
- def query_source_port(self, zone, port, protocol):
|
|
- for (_port, _protocol) in self.get_settings(zone)["source_ports"]:
|
|
- if portInPortRange(port, _port) and protocol == _protocol:
|
|
- return True
|
|
-
|
|
- return False
|
|
-
|
|
- def list_source_ports(self, zone):
|
|
- return list(self.get_settings(zone)["source_ports"].keys())
|
|
-
|
|
- # MASQUERADE
|
|
-
|
|
- def __masquerade_id(self):
|
|
- return True
|
|
-
|
|
- def add_masquerade(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]
|
|
-
|
|
- masquerade_id = self.__masquerade_id()
|
|
- if masquerade_id in _obj.settings["masquerade"]:
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "masquerade already enabled in '%s'" % _zone)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self._masquerade(True, _zone, transaction)
|
|
-
|
|
- self.__register_masquerade(_obj, masquerade_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_masquerade, _obj, masquerade_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_masquerade(self, _obj, masquerade_id, timeout, sender):
|
|
- _obj.settings["masquerade"][masquerade_id] = \
|
|
- self.__gen_settings(timeout, sender)
|
|
-
|
|
- def remove_masquerade(self, zone, use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- masquerade_id = self.__masquerade_id()
|
|
- if masquerade_id not in _obj.settings["masquerade"]:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "masquerade not enabled in '%s'" % _zone)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self._masquerade(False, _zone, transaction)
|
|
-
|
|
- transaction.add_post(self.__unregister_masquerade, _obj, masquerade_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __unregister_masquerade(self, _obj, masquerade_id):
|
|
- if masquerade_id in _obj.settings["masquerade"]:
|
|
- del _obj.settings["masquerade"][masquerade_id]
|
|
-
|
|
- def query_masquerade(self, zone):
|
|
- return self.__masquerade_id() in self.get_settings(zone)["masquerade"]
|
|
-
|
|
- # PORT FORWARDING
|
|
-
|
|
- def check_forward_port(self, ipv, port, protocol, toport=None, toaddr=None):
|
|
- self._fw.check_port(port)
|
|
- self._fw.check_tcpudp(protocol)
|
|
- if toport:
|
|
- self._fw.check_port(toport)
|
|
- if toaddr:
|
|
- if not check_single_address(ipv, toaddr):
|
|
- raise FirewallError(errors.INVALID_ADDR, toaddr)
|
|
- if not toport and not toaddr:
|
|
- raise FirewallError(
|
|
- errors.INVALID_FORWARD,
|
|
- "port-forwarding is missing to-port AND to-addr")
|
|
-
|
|
- def __forward_port_id(self, port, protocol, toport=None, toaddr=None):
|
|
- if check_single_address("ipv6", toaddr):
|
|
- self.check_forward_port("ipv6", port, protocol, toport, toaddr)
|
|
- else:
|
|
- self.check_forward_port("ipv4", port, protocol, toport, toaddr)
|
|
- return (portStr(port, "-"), protocol,
|
|
- portStr(toport, "-"), str(toaddr))
|
|
-
|
|
- def add_forward_port(self, zone, port, protocol, toport=None,
|
|
- toaddr=None, 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_port_id(port, protocol, toport, toaddr)
|
|
- if forward_id in _obj.settings["forward_ports"]:
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "'%s:%s:%s:%s' already in '%s'" % \
|
|
- (port, protocol, toport, toaddr, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self._forward_port(True, _zone, transaction, port, protocol,
|
|
- toport, toaddr)
|
|
-
|
|
- self.__register_forward_port(_obj, forward_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_forward_port, _obj, forward_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_forward_port(self, _obj, forward_id, timeout, sender):
|
|
- _obj.settings["forward_ports"][forward_id] = \
|
|
- self.__gen_settings(timeout, sender)
|
|
-
|
|
- def remove_forward_port(self, zone, port, protocol, toport=None,
|
|
- toaddr=None, use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- forward_id = self.__forward_port_id(port, protocol, toport, toaddr)
|
|
- if forward_id not in _obj.settings["forward_ports"]:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "'%s:%s:%s:%s' not in '%s'" % \
|
|
- (port, protocol, toport, toaddr, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self._forward_port(False, _zone, transaction, port, protocol,
|
|
- toport, toaddr)
|
|
-
|
|
- transaction.add_post(self.__unregister_forward_port, _obj, forward_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __unregister_forward_port(self, _obj, forward_id):
|
|
- if forward_id in _obj.settings["forward_ports"]:
|
|
- del _obj.settings["forward_ports"][forward_id]
|
|
-
|
|
- def query_forward_port(self, zone, port, protocol, toport=None,
|
|
- toaddr=None):
|
|
- forward_id = self.__forward_port_id(port, protocol, toport, toaddr)
|
|
- return forward_id in self.get_settings(zone)["forward_ports"]
|
|
-
|
|
- def list_forward_ports(self, zone):
|
|
- return list(self.get_settings(zone)["forward_ports"].keys())
|
|
-
|
|
- # ICMP BLOCK
|
|
-
|
|
- def check_icmp_block(self, icmp):
|
|
- self._fw.check_icmptype(icmp)
|
|
-
|
|
- def __icmp_block_id(self, icmp):
|
|
- self.check_icmp_block(icmp)
|
|
- return icmp
|
|
-
|
|
- def add_icmp_block(self, zone, icmp, 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]
|
|
-
|
|
- icmp_id = self.__icmp_block_id(icmp)
|
|
- if icmp_id in _obj.settings["icmp_blocks"]:
|
|
- raise FirewallError(errors.ALREADY_ENABLED,
|
|
- "'%s' already in '%s'" % (icmp, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self._icmp_block(True, _zone, icmp, transaction)
|
|
-
|
|
- self.__register_icmp_block(_obj, icmp_id, timeout, sender)
|
|
- transaction.add_fail(self.__unregister_icmp_block, _obj, icmp_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_icmp_block(self, _obj, icmp_id, timeout, sender):
|
|
- _obj.settings["icmp_blocks"][icmp_id] = \
|
|
- self.__gen_settings(timeout, sender)
|
|
-
|
|
- def remove_icmp_block(self, zone, icmp, use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- icmp_id = self.__icmp_block_id(icmp)
|
|
- if icmp_id not in _obj.settings["icmp_blocks"]:
|
|
- raise FirewallError(errors.NOT_ENABLED,
|
|
- "'%s' not in '%s'" % (icmp, _zone))
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- self._icmp_block(False, _zone, icmp, transaction)
|
|
-
|
|
- transaction.add_post(self.__unregister_icmp_block, _obj, icmp_id)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __unregister_icmp_block(self, _obj, icmp_id):
|
|
- if icmp_id in _obj.settings["icmp_blocks"]:
|
|
- del _obj.settings["icmp_blocks"][icmp_id]
|
|
-
|
|
- def query_icmp_block(self, zone, icmp):
|
|
- return self.__icmp_block_id(icmp) in self.get_settings(zone)["icmp_blocks"]
|
|
-
|
|
- def list_icmp_blocks(self, zone):
|
|
- return self.get_settings(zone)["icmp_blocks"].keys()
|
|
-
|
|
- # ICMP BLOCK INVERSION
|
|
-
|
|
- def __icmp_block_inversion_id(self):
|
|
- return True
|
|
-
|
|
- def add_icmp_block_inversion(self, zone, sender=None,
|
|
- use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- icmp_block_inversion_id = self.__icmp_block_inversion_id()
|
|
- if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]:
|
|
- raise FirewallError(
|
|
- errors.ALREADY_ENABLED,
|
|
- "icmp-block-inversion already enabled in '%s'" % _zone)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- # undo icmp blocks
|
|
- for args in self.get_settings(_zone)["icmp_blocks"]:
|
|
- self._icmp_block(False, _zone, args, transaction)
|
|
-
|
|
- self._icmp_block_inversion(False, _zone, transaction)
|
|
-
|
|
- self.__register_icmp_block_inversion(_obj, icmp_block_inversion_id,
|
|
- sender)
|
|
- transaction.add_fail(self.__undo_icmp_block_inversion, _zone, _obj,
|
|
- icmp_block_inversion_id)
|
|
-
|
|
- # redo icmp blocks
|
|
- if _obj.applied:
|
|
- for args in self.get_settings(_zone)["icmp_blocks"]:
|
|
- self._icmp_block(True, _zone, args, transaction)
|
|
-
|
|
- self._icmp_block_inversion(True, _zone, transaction)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __register_icmp_block_inversion(self, _obj, icmp_block_inversion_id,
|
|
- sender):
|
|
- _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] = \
|
|
- self.__gen_settings(0, sender)
|
|
-
|
|
- def __undo_icmp_block_inversion(self, _zone, _obj, icmp_block_inversion_id):
|
|
- transaction = self.new_transaction()
|
|
-
|
|
- # undo icmp blocks
|
|
- if _obj.applied:
|
|
- for args in self.get_settings(_zone)["icmp_blocks"]:
|
|
- self._icmp_block(False, _zone, args, transaction)
|
|
-
|
|
- if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]:
|
|
- del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id]
|
|
-
|
|
- # redo icmp blocks
|
|
- if _obj.applied:
|
|
- for args in self.get_settings(_zone)["icmp_blocks"]:
|
|
- self._icmp_block(True, _zone, args, transaction)
|
|
-
|
|
- transaction.execute(True)
|
|
-
|
|
- def remove_icmp_block_inversion(self, zone, use_transaction=None):
|
|
- _zone = self._fw.check_zone(zone)
|
|
- self._fw.check_panic()
|
|
- _obj = self._zones[_zone]
|
|
-
|
|
- icmp_block_inversion_id = self.__icmp_block_inversion_id()
|
|
- if icmp_block_inversion_id not in _obj.settings["icmp_block_inversion"]:
|
|
- raise FirewallError(
|
|
- errors.NOT_ENABLED,
|
|
- "icmp-block-inversion not enabled in '%s'" % _zone)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction = self.new_transaction()
|
|
- else:
|
|
- transaction = use_transaction
|
|
-
|
|
- if _obj.applied:
|
|
- # undo icmp blocks
|
|
- for args in self.get_settings(_zone)["icmp_blocks"]:
|
|
- self._icmp_block(False, _zone, args, transaction)
|
|
-
|
|
- self._icmp_block_inversion(False, _zone, transaction)
|
|
-
|
|
- self.__unregister_icmp_block_inversion(_obj,
|
|
- icmp_block_inversion_id)
|
|
- transaction.add_fail(self.__register_icmp_block_inversion, _obj,
|
|
- icmp_block_inversion_id, None)
|
|
-
|
|
- # redo icmp blocks
|
|
- if _obj.applied:
|
|
- for args in self.get_settings(_zone)["icmp_blocks"]:
|
|
- self._icmp_block(True, _zone, args, transaction)
|
|
-
|
|
- self._icmp_block_inversion(True, _zone, transaction)
|
|
-
|
|
- if use_transaction is None:
|
|
- transaction.execute(True)
|
|
-
|
|
- return _zone
|
|
-
|
|
- def __unregister_icmp_block_inversion(self, _obj, icmp_block_inversion_id):
|
|
- if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]:
|
|
- del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id]
|
|
+ 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):
|
|
- return self.__icmp_block_inversion_id() in \
|
|
- self.get_settings(zone)["icmp_block_inversion"]
|
|
-
|
|
- # dynamic chain handling
|
|
-
|
|
- def gen_chain_rules(self, zone, create, table, chain, transaction):
|
|
- if create:
|
|
- if zone in self._chains and \
|
|
- table in self._chains[zone] and \
|
|
- chain in self._chains[zone][table]:
|
|
- return
|
|
- else:
|
|
- if zone not in self._chains or \
|
|
- table not in self._chains[zone] or \
|
|
- chain not in self._chains[zone][table]:
|
|
- return
|
|
-
|
|
- for backend in self._fw.enabled_backends():
|
|
- if backend.zones_supported and \
|
|
- table in backend.get_available_tables():
|
|
- rules = backend.build_zone_chain_rules(zone, table, chain)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- self._register_chains(zone, create, [(table, chain)])
|
|
- transaction.add_fail(self._register_chains, zone, create, [(table, chain)])
|
|
-
|
|
- def _interface(self, enable, zone, interface, transaction,
|
|
- append=False):
|
|
- for backend in self._fw.enabled_backends():
|
|
- if not backend.zones_supported:
|
|
- continue
|
|
- for table in backend.get_available_tables():
|
|
- for chain in backend.get_zone_table_chains(table):
|
|
- # create needed chains if not done already
|
|
- if enable:
|
|
- transaction.add_chain(zone, table, chain)
|
|
-
|
|
- rules = backend.build_zone_source_interface_rules(enable,
|
|
- zone, interface, table, chain, append)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # IPSETS
|
|
-
|
|
- def _ipset_family(self, name):
|
|
- if self._fw.ipset.get_type(name) == "hash:mac":
|
|
- return None
|
|
- return self._fw.ipset.get_family(name)
|
|
-
|
|
- def __ipset_type(self, name):
|
|
- return self._fw.ipset.get_type(name)
|
|
-
|
|
- 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 ZONE_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.zones_supported:
|
|
- continue
|
|
- for table in backend.get_available_tables():
|
|
- for chain in backend.get_zone_table_chains(table):
|
|
- # create needed chains if not done already
|
|
- if enable:
|
|
- transaction.add_chain(zone, table, chain)
|
|
-
|
|
- rules = backend.build_zone_source_address_rules(enable, zone,
|
|
- source, table, chain)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _rule_prepare(self, enable, zone, rule, transaction):
|
|
- if rule.family is not None:
|
|
- ipvs = [ rule.family ]
|
|
- else:
|
|
- ipvs = [ipv for ipv in ["ipv4", "ipv6"] if self._fw.is_ipv_enabled(ipv)]
|
|
-
|
|
- source_ipv = self._rule_source_ipv(rule.source)
|
|
- if source_ipv is not None and source_ipv != "":
|
|
- if rule.family is not None:
|
|
- # rule family is defined by user, no way to change it
|
|
- if rule.family != source_ipv:
|
|
- raise FirewallError(errors.INVALID_RULE,
|
|
- "Source address family '%s' conflicts with rule family '%s'." % (source_ipv, rule.family))
|
|
- else:
|
|
- # use the source family as rule family
|
|
- ipvs = [ source_ipv ]
|
|
-
|
|
- # add an element to object to allow backends to know what ipvs this applies to
|
|
- rule.ipvs = ipvs
|
|
-
|
|
- for backend in set([self._fw.get_backend_by_ipv(x) for x in ipvs]):
|
|
- # SERVICE
|
|
- if type(rule.element) == Rich_Service:
|
|
- svc = self._fw.service.get_service(rule.element.name)
|
|
-
|
|
- destinations = []
|
|
- if len(svc.destination) > 0:
|
|
- if rule.destination:
|
|
- # we can not use two destinations at the same time
|
|
- raise FirewallError(errors.INVALID_RULE,
|
|
- "Destination conflict with service.")
|
|
- for ipv in ipvs:
|
|
- if ipv in svc.destination and backend.is_ipv_supported(ipv):
|
|
- destinations.append(svc.destination[ipv])
|
|
- else:
|
|
- # dummy for the following for loop
|
|
- destinations.append(None)
|
|
-
|
|
- for destination in destinations:
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
- transaction.add_chain(zone, "raw", "PREROUTING")
|
|
-
|
|
- if type(rule.action) == Rich_Accept:
|
|
- # only load modules for accept action
|
|
- helpers = self.get_helpers_for_service_modules(svc.modules,
|
|
- enable)
|
|
- helpers += self.get_helpers_for_service_helpers(svc.helpers)
|
|
- helpers = sorted(set(helpers), key=lambda x: x.name)
|
|
-
|
|
- modules = [ ]
|
|
- for helper in helpers:
|
|
- module = helper.module
|
|
- _module_short_name = get_nf_conntrack_short_name(module)
|
|
- nat_module = module.replace("conntrack", "nat")
|
|
- modules.append(nat_module)
|
|
- if helper.family != "" and not backend.is_ipv_supported(helper.family):
|
|
- # no support for family ipv, continue
|
|
- continue
|
|
- if len(helper.ports) < 1:
|
|
- modules.append(module)
|
|
- else:
|
|
- for (port,proto) in helper.ports:
|
|
- rules = backend.build_zone_helper_ports_rules(
|
|
- enable, zone, proto, port,
|
|
- destination, helper.name, _module_short_name)
|
|
- transaction.add_rules(backend, rules)
|
|
- transaction.add_modules(modules)
|
|
-
|
|
- # create rules
|
|
- for (port,proto) in svc.ports:
|
|
- if enable and type(rule.action) == Rich_Mark:
|
|
- transaction.add_chain(zone, "mangle", "PREROUTING")
|
|
- rules = backend.build_zone_ports_rules(
|
|
- enable, zone, proto, port, destination, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- for proto in svc.protocols:
|
|
- if enable and type(rule.action) == Rich_Mark:
|
|
- transaction.add_chain(zone, "mangle", "PREROUTING")
|
|
- rules = backend.build_zone_protocol_rules(
|
|
- enable, zone, proto, destination, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # create rules
|
|
- for (port,proto) in svc.source_ports:
|
|
- if enable and type(rule.action) == Rich_Mark:
|
|
- transaction.add_chain(zone, "mangle", "PREROUTING")
|
|
- rules = backend.build_zone_source_ports_rules(
|
|
- enable, zone, proto, port, destination, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # PORT
|
|
- elif type(rule.element) == Rich_Port:
|
|
- port = rule.element.port
|
|
- protocol = rule.element.protocol
|
|
- self.check_port(port, protocol)
|
|
-
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
- if enable and type(rule.action) == Rich_Mark:
|
|
- transaction.add_chain(zone, "mangle", "PREROUTING")
|
|
-
|
|
- rules = backend.build_zone_ports_rules(
|
|
- enable, zone, protocol, port, None, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # PROTOCOL
|
|
- elif type(rule.element) == Rich_Protocol:
|
|
- protocol = rule.element.value
|
|
- self.check_protocol(protocol)
|
|
-
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
- if enable and type(rule.action) == Rich_Mark:
|
|
- transaction.add_chain(zone, "mangle", "PREROUTING")
|
|
-
|
|
- rules = backend.build_zone_protocol_rules(
|
|
- enable, zone, protocol, None, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # MASQUERADE
|
|
- elif type(rule.element) == Rich_Masquerade:
|
|
- if enable:
|
|
- transaction.add_chain(zone, "nat", "POSTROUTING")
|
|
- transaction.add_chain(zone, "filter", "FORWARD_OUT")
|
|
- for ipv in ipvs:
|
|
- if backend.is_ipv_supported(ipv):
|
|
- transaction.add_post(enable_ip_forwarding, ipv)
|
|
-
|
|
- rules = backend.build_zone_masquerade_rules(enable, zone, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # FORWARD PORT
|
|
- elif type(rule.element) == Rich_ForwardPort:
|
|
- port = rule.element.port
|
|
- protocol = rule.element.protocol
|
|
- toport = rule.element.to_port
|
|
- toaddr = rule.element.to_address
|
|
- for ipv in ipvs:
|
|
- if backend.is_ipv_supported(ipv):
|
|
- self.check_forward_port(ipv, port, protocol, toport, toaddr)
|
|
- if toaddr and enable:
|
|
- transaction.add_post(enable_ip_forwarding, ipv)
|
|
-
|
|
- if enable:
|
|
- transaction.add_chain(zone, "nat", "PREROUTING")
|
|
-
|
|
- rules = backend.build_zone_forward_port_rules(
|
|
- enable, zone, port, protocol, toport,
|
|
- toaddr, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # SOURCE PORT
|
|
- elif type(rule.element) == Rich_SourcePort:
|
|
- port = rule.element.port
|
|
- protocol = rule.element.protocol
|
|
- self.check_port(port, protocol)
|
|
-
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
- if enable and type(rule.action) == Rich_Mark:
|
|
- transaction.add_chain(zone, "mangle", "PREROUTING")
|
|
-
|
|
- rules = backend.build_zone_source_ports_rules(
|
|
- enable, zone, protocol, port, None, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # ICMP BLOCK and ICMP TYPE
|
|
- elif type(rule.element) == Rich_IcmpBlock or \
|
|
- type(rule.element) == Rich_IcmpType:
|
|
- ict = self._fw.icmptype.get_icmptype(rule.element.name)
|
|
-
|
|
- if type(rule.element) == Rich_IcmpBlock and \
|
|
- rule.action and type(rule.action) == Rich_Accept:
|
|
- # icmp block might have reject or drop action, but not accept
|
|
- raise FirewallError(errors.INVALID_RULE,
|
|
- "IcmpBlock not usable with accept action")
|
|
- if ict.destination:
|
|
- for ipv in ipvs:
|
|
- if ipv in ict.destination \
|
|
- and not backend.is_ipv_supported(ipv):
|
|
- raise FirewallError(
|
|
- errors.INVALID_RULE,
|
|
- "Icmp%s %s not usable with %s" % \
|
|
- ("Block" if type(rule.element) == \
|
|
- Rich_IcmpBlock else "Type",
|
|
- rule.element.name, backend.name))
|
|
-
|
|
- table = "filter"
|
|
- if enable:
|
|
- transaction.add_chain(zone, table, "INPUT")
|
|
- transaction.add_chain(zone, table, "FORWARD_IN")
|
|
-
|
|
- rules = backend.build_zone_icmp_block_rules(enable, zone, ict, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- elif rule.element is None:
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
- if enable and type(rule.action) == Rich_Mark:
|
|
- transaction.add_chain(zone, "mangle", "PREROUTING")
|
|
-
|
|
- rules = backend.build_zone_rich_source_destination_rules(
|
|
- enable, zone, rule)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- # EVERYTHING ELSE
|
|
- else:
|
|
- raise FirewallError(errors.INVALID_RULE, "Unknown element %s" %
|
|
- type(rule.element))
|
|
-
|
|
- def _service(self, enable, zone, service, transaction, included_services=None):
|
|
- svc = self._fw.service.get_service(service)
|
|
- helpers = self.get_helpers_for_service_modules(svc.modules, enable)
|
|
- helpers += self.get_helpers_for_service_helpers(svc.helpers)
|
|
- helpers = sorted(set(helpers), key=lambda x: x.name)
|
|
-
|
|
- # First apply any services this service may include
|
|
- if included_services is None:
|
|
- included_services = [service]
|
|
- for include in svc.includes:
|
|
- if include in included_services:
|
|
- continue
|
|
- self.check_service(include)
|
|
- included_services.append(include)
|
|
- self._service(enable, zone, include, transaction, included_services=included_services)
|
|
-
|
|
- if enable:
|
|
- transaction.add_chain(zone, "raw", "PREROUTING")
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
-
|
|
- # build a list of (backend, destination). The destination may be ipv4,
|
|
- # ipv6 or None
|
|
- #
|
|
- backends_ipv = []
|
|
- for ipv in ["ipv4", "ipv6"]:
|
|
- if not self._fw.is_ipv_enabled(ipv):
|
|
- continue
|
|
- backend = self._fw.get_backend_by_ipv(ipv)
|
|
- if len(svc.destination) > 0:
|
|
- if ipv in svc.destination:
|
|
- backends_ipv.append((backend, svc.destination[ipv]))
|
|
- else:
|
|
- if (backend, None) not in backends_ipv:
|
|
- backends_ipv.append((backend, None))
|
|
-
|
|
- for (backend,destination) in backends_ipv:
|
|
- for helper in helpers:
|
|
- module = helper.module
|
|
- _module_short_name = get_nf_conntrack_short_name(module)
|
|
- nat_module = helper.module.replace("conntrack", "nat")
|
|
- transaction.add_module(nat_module)
|
|
- if helper.family != "" and not backend.is_ipv_supported(helper.family):
|
|
- # no support for family ipv, continue
|
|
- continue
|
|
- if len(helper.ports) < 1:
|
|
- transaction.add_module(module)
|
|
- else:
|
|
- for (port,proto) in helper.ports:
|
|
- rules = backend.build_zone_helper_ports_rules(
|
|
- enable, zone, proto, port,
|
|
- destination, helper.name, _module_short_name)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- for (port,proto) in svc.ports:
|
|
- rules = backend.build_zone_ports_rules(enable, zone, proto,
|
|
- port, destination)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- for protocol in svc.protocols:
|
|
- rules = backend.build_zone_protocol_rules(
|
|
- enable, zone, protocol, destination)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- for (port,proto) in svc.source_ports:
|
|
- rules = backend.build_zone_source_ports_rules(
|
|
- enable, zone, proto, port, destination)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _port(self, enable, zone, port, protocol, transaction):
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
-
|
|
- for backend in self._fw.enabled_backends():
|
|
- if not backend.zones_supported:
|
|
- continue
|
|
-
|
|
- rules = backend.build_zone_ports_rules(enable, zone, protocol,
|
|
- port)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _protocol(self, enable, zone, protocol, transaction):
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
-
|
|
- for backend in self._fw.enabled_backends():
|
|
- if not backend.zones_supported:
|
|
- continue
|
|
-
|
|
- rules = backend.build_zone_protocol_rules(enable, zone, protocol)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _source_port(self, enable, zone, port, protocol, transaction):
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
-
|
|
- for backend in self._fw.enabled_backends():
|
|
- if not backend.zones_supported:
|
|
- continue
|
|
-
|
|
- rules = backend.build_zone_source_ports_rules(enable, zone, protocol, port)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _masquerade(self, enable, zone, transaction):
|
|
- if enable:
|
|
- transaction.add_chain(zone, "nat", "POSTROUTING")
|
|
- transaction.add_chain(zone, "filter", "FORWARD_OUT")
|
|
-
|
|
- ipv = "ipv4"
|
|
- transaction.add_post(enable_ip_forwarding, ipv)
|
|
-
|
|
- backend = self._fw.get_backend_by_ipv(ipv)
|
|
- rules = backend.build_zone_masquerade_rules(enable, zone)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _forward_port(self, enable, zone, transaction, port, protocol,
|
|
- toport=None, toaddr=None):
|
|
- if check_single_address("ipv6", toaddr):
|
|
- ipv = "ipv6"
|
|
- else:
|
|
- ipv = "ipv4"
|
|
-
|
|
- if enable:
|
|
- transaction.add_chain(zone, "nat", "PREROUTING")
|
|
-
|
|
- if toaddr and enable:
|
|
- transaction.add_post(enable_ip_forwarding, ipv)
|
|
- backend = self._fw.get_backend_by_ipv(ipv)
|
|
- rules = backend.build_zone_forward_port_rules(
|
|
- enable, zone, port, protocol, toport,
|
|
- toaddr)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _icmp_block(self, enable, zone, icmp, transaction):
|
|
- ict = self._fw.icmptype.get_icmptype(icmp)
|
|
-
|
|
- if enable:
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
- transaction.add_chain(zone, "filter", "FORWARD_IN")
|
|
-
|
|
- for backend in self._fw.enabled_backends():
|
|
- if not backend.zones_supported:
|
|
- continue
|
|
- skip_backend = False
|
|
-
|
|
- if ict.destination:
|
|
- for ipv in ["ipv4", "ipv6"]:
|
|
- if ipv in ict.destination:
|
|
- if not backend.is_ipv_supported(ipv):
|
|
- skip_backend = True
|
|
- break
|
|
-
|
|
- if skip_backend:
|
|
- continue
|
|
-
|
|
- rules = backend.build_zone_icmp_block_rules(enable, zone, ict)
|
|
- transaction.add_rules(backend, rules)
|
|
-
|
|
- def _icmp_block_inversion(self, enable, zone, transaction):
|
|
- target = self._zones[zone].target
|
|
-
|
|
- # Do not add general icmp accept rules into a trusted, block or drop
|
|
- # zone.
|
|
- if target in [ "DROP", "%%REJECT%%", "REJECT" ]:
|
|
- return
|
|
- if not self.query_icmp_block_inversion(zone) and target == "ACCEPT":
|
|
- # ibi target and zone target are ACCEPT, no need to add an extra
|
|
- # rule
|
|
- return
|
|
-
|
|
- transaction.add_chain(zone, "filter", "INPUT")
|
|
- transaction.add_chain(zone, "filter", "FORWARD_IN")
|
|
-
|
|
- for backend in self._fw.enabled_backends():
|
|
- if not backend.zones_supported:
|
|
- continue
|
|
-
|
|
- rules = backend.build_zone_icmp_block_inversion_rules(enable, zone)
|
|
- transaction.add_rules(backend, rules)
|
|
+ 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)
|
|
diff --git a/src/firewall/core/io/policy.py b/src/firewall/core/io/policy.py
|
|
new file mode 100644
|
|
index 0000000..c0171a6
|
|
--- /dev/null
|
|
+++ b/src/firewall/core/io/policy.py
|
|
@@ -0,0 +1,830 @@
|
|
+# -*- coding: utf-8 -*-
|
|
+#
|
|
+# SPDX-License-Identifier: GPL-2.0-or-later
|
|
+
|
|
+__all__ = [ "Policy", "policy_reader", "policy_writer" ]
|
|
+
|
|
+import xml.sax as sax
|
|
+import os
|
|
+import io
|
|
+import shutil
|
|
+import copy
|
|
+from collections import OrderedDict
|
|
+
|
|
+from firewall import config
|
|
+from firewall.functions import checkIP, checkIP6
|
|
+from firewall.functions import uniqify, max_policy_name_len, portStr
|
|
+from firewall.core.base import DEFAULT_POLICY_TARGET, POLICY_TARGETS
|
|
+from firewall.core.io.io_object import IO_Object, \
|
|
+ IO_Object_ContentHandler, IO_Object_XMLGenerator, check_port, \
|
|
+ check_tcpudp, check_protocol
|
|
+from firewall.core import rich
|
|
+from firewall.core.logger import log
|
|
+from firewall import errors
|
|
+from firewall.errors import FirewallError
|
|
+
|
|
+class Policy(IO_Object):
|
|
+ priority_min = -32768
|
|
+ priority_max = 32767
|
|
+ priority_default = -1
|
|
+ ADDITIONAL_ALNUM_CHARS = [ "_", "-", "/" ]
|
|
+ PARSER_REQUIRED_ELEMENT_ATTRS = {
|
|
+ "short": None,
|
|
+ "description": None,
|
|
+ "policy": ["target"],
|
|
+ "service": [ "name" ],
|
|
+ "port": [ "port", "protocol" ],
|
|
+ "icmp-block": [ "name" ],
|
|
+ "icmp-type": [ "name" ],
|
|
+ "forward-port": [ "port", "protocol" ],
|
|
+ "rule": None,
|
|
+ "destination": [ "address" ],
|
|
+ "protocol": [ "value" ],
|
|
+ "source-port": [ "port", "protocol" ],
|
|
+ "log": None,
|
|
+ "audit": None,
|
|
+ "accept": None,
|
|
+ "reject": None,
|
|
+ "drop": None,
|
|
+ "mark": [ "set" ],
|
|
+ "limit": [ "value" ],
|
|
+ "ingress-zone": None,
|
|
+ "egress-zone": None,
|
|
+ }
|
|
+ PARSER_OPTIONAL_ELEMENT_ATTRS = {
|
|
+ "policy": [ "version", "priority" ],
|
|
+ "forward-port": [ "to-port", "to-addr" ],
|
|
+ "rule": [ "family", "priority" ],
|
|
+ "destination": [ "invert" ],
|
|
+ "log": [ "prefix", "level" ],
|
|
+ "reject": [ "type" ],
|
|
+ }
|
|
+
|
|
+ def __init__(self):
|
|
+ super(Policy, self).__init__()
|
|
+ self.version = ""
|
|
+ self.short = ""
|
|
+ self.description = ""
|
|
+ self.target = DEFAULT_POLICY_TARGET
|
|
+ self.services = [ ]
|
|
+ self.ports = [ ]
|
|
+ self.protocols = [ ]
|
|
+ self.icmp_blocks = [ ]
|
|
+ self.masquerade = False
|
|
+ self.forward_ports = [ ]
|
|
+ self.source_ports = [ ]
|
|
+ self.fw_config = None # to be able to check services and a icmp_blocks
|
|
+ self.rules = [ ]
|
|
+ self.combined = False
|
|
+ self.applied = False
|
|
+ self.priority = self.priority_default
|
|
+ self.derived_from_zone = None
|
|
+ self.ingress_zones = []
|
|
+ self.egress_zones = []
|
|
+
|
|
+ def cleanup(self):
|
|
+ self.version = ""
|
|
+ self.short = ""
|
|
+ self.description = ""
|
|
+ self.target = DEFAULT_POLICY_TARGET
|
|
+ del self.services[:]
|
|
+ del self.ports[:]
|
|
+ del self.protocols[:]
|
|
+ del self.icmp_blocks[:]
|
|
+ self.masquerade = False
|
|
+ del self.forward_ports[:]
|
|
+ del self.source_ports[:]
|
|
+ self.fw_config = None # to be able to check services and a icmp_blocks
|
|
+ del self.rules[:]
|
|
+ self.combined = False
|
|
+ self.applied = False
|
|
+ self.priority = self.priority_default
|
|
+ del self.ingress_zones[:]
|
|
+ del self.egress_zones[:]
|
|
+
|
|
+ def __getattr__(self, name):
|
|
+ if name == "rules_str":
|
|
+ rules_str = [str(rule) for rule in self.rules]
|
|
+ return rules_str
|
|
+ else:
|
|
+ return getattr(super(Policy, self), name)
|
|
+
|
|
+ def __setattr__(self, name, value):
|
|
+ if name == "rules_str":
|
|
+ self.rules = [rich.Rich_Rule(rule_str=s) for s in value]
|
|
+ else:
|
|
+ super(Policy, self).__setattr__(name, value)
|
|
+
|
|
+ def _check_config(self, config, item):
|
|
+ if item == "services" and self.fw_config:
|
|
+ existing_services = self.fw_config.get_services()
|
|
+ for service in config:
|
|
+ if service not in existing_services:
|
|
+ raise FirewallError(errors.INVALID_SERVICE,
|
|
+ "'%s' not among existing services" % \
|
|
+ service)
|
|
+ elif item == "ports":
|
|
+ for port in config:
|
|
+ check_port(port[0])
|
|
+ check_tcpudp(port[1])
|
|
+ elif item == "protocols":
|
|
+ for proto in config:
|
|
+ check_protocol(proto)
|
|
+ elif item == "icmp_blocks" and self.fw_config:
|
|
+ existing_icmptypes = self.fw_config.get_icmptypes()
|
|
+ for icmptype in config:
|
|
+ if icmptype not in existing_icmptypes:
|
|
+ raise FirewallError(errors.INVALID_ICMPTYPE,
|
|
+ "'%s' not among existing icmp types" % \
|
|
+ icmptype)
|
|
+ elif item == "forward_ports":
|
|
+ for fwd_port in config:
|
|
+ check_port(fwd_port[0])
|
|
+ check_tcpudp(fwd_port[1])
|
|
+ if not fwd_port[2] and not fwd_port[3]:
|
|
+ raise FirewallError(
|
|
+ errors.INVALID_FORWARD,
|
|
+ "'%s' is missing to-port AND to-addr " % fwd_port)
|
|
+ if fwd_port[2]:
|
|
+ check_port(fwd_port[2])
|
|
+ if fwd_port[3]:
|
|
+ if not checkIP(fwd_port[3]) and not checkIP6(fwd_port[3]):
|
|
+ raise FirewallError(
|
|
+ errors.INVALID_ADDR,
|
|
+ "to-addr '%s' is not a valid address" % fwd_port[3])
|
|
+ elif item == "source_ports":
|
|
+ for port in config:
|
|
+ check_port(port[0])
|
|
+ check_tcpudp(port[1])
|
|
+ elif item == "target":
|
|
+ if config not in POLICY_TARGETS:
|
|
+ raise FirewallError(errors.INVALID_TARGET, config)
|
|
+ elif item == "rules_str":
|
|
+ for rule in config:
|
|
+ rich.Rich_Rule(rule_str=rule)
|
|
+ elif item in ["ingress-zone", "egress-zone"] and self.fw_config:
|
|
+ existing_zones = self.fw_config.get_zones()
|
|
+ for zone in config:
|
|
+ if zone not in existing_zones:
|
|
+ raise FirewallError(errors.INVALID_SERVICE,
|
|
+ "'%s' not among existing zones" % zone)
|
|
+
|
|
+ def check_name(self, name):
|
|
+ super(Policy, self).check_name(name)
|
|
+ if name.startswith('/'):
|
|
+ raise FirewallError(errors.INVALID_NAME,
|
|
+ "'%s' can't start with '/'" % name)
|
|
+ elif name.endswith('/'):
|
|
+ raise FirewallError(errors.INVALID_NAME,
|
|
+ "'%s' can't end with '/'" % name)
|
|
+ elif name.count('/') > 1:
|
|
+ raise FirewallError(errors.INVALID_NAME,
|
|
+ "more than one '/' in '%s'" % name)
|
|
+ else:
|
|
+ if "/" in name:
|
|
+ checked_name = name[:name.find('/')]
|
|
+ else:
|
|
+ checked_name = name
|
|
+ if len(checked_name) > max_policy_name_len():
|
|
+ raise FirewallError(errors.INVALID_NAME,
|
|
+ "Policy of '%s' has %d chars, max is %d %s" % (
|
|
+ name, len(checked_name),
|
|
+ max_policy_name_len(),
|
|
+ self.combined))
|
|
+
|
|
+ def import_config(self, conf):
|
|
+ self.check_config(conf)
|
|
+
|
|
+ # FIXME:
|
|
+ for key in conf:
|
|
+ if not hasattr(self, key):
|
|
+ raise FirewallError(errors.UNKNOWN_ERROR, "Internal error. '{}' is not a valid attribute".format(key))
|
|
+ if isinstance(conf[key], list):
|
|
+ # maintain list order while removing duplicates
|
|
+ setattr(self, key, list(OrderedDict.fromkeys(copy.deepcopy(conf[key]))))
|
|
+ else:
|
|
+ setattr(self, key, copy.deepcopy(conf[key]))
|
|
+
|
|
+ def export_config(self):
|
|
+ conf = {}
|
|
+ # FIXME
|
|
+ type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE])
|
|
+ for key in type_formats:
|
|
+ if getattr(self, key):
|
|
+ conf[key] = copy.deepcopy(getattr(self, key))
|
|
+ return conf
|
|
+
|
|
+ def check_config(self, conf):
|
|
+ # FIXME
|
|
+ type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE])
|
|
+ for key in conf:
|
|
+ if key not in [x for (x,y) in self.IMPORT_EXPORT_STRUCTURE]:
|
|
+ raise FirewallError(errors.INVALID_OPTION, "policy option '{}' is not valid".format(key))
|
|
+ self._check_config_structure(conf[key], type_formats[key])
|
|
+ self._check_config(conf[key], key)
|
|
+
|
|
+# PARSER
|
|
+
|
|
+class policy_ContentHandler(IO_Object_ContentHandler):
|
|
+ def __init__(self, item):
|
|
+ IO_Object_ContentHandler.__init__(self, item)
|
|
+ self._rule = None
|
|
+ self._rule_error = False
|
|
+ self._limit_ok = None
|
|
+
|
|
+ def startElement(self, name, attrs):
|
|
+ IO_Object_ContentHandler.startElement(self, name, attrs)
|
|
+ if self._rule_error:
|
|
+ return
|
|
+
|
|
+ self.item.parser_check_element_attrs(name, attrs)
|
|
+
|
|
+ if name == "policy":
|
|
+ if "version" in attrs:
|
|
+ self.item.version = attrs["version"]
|
|
+ if "priority" in attrs:
|
|
+ self.item.priority = int(attrs["priority"])
|
|
+ target = attrs["target"]
|
|
+ if target not in POLICY_TARGETS:
|
|
+ raise FirewallError(errors.INVALID_TARGET, target)
|
|
+
|
|
+ elif name == "short":
|
|
+ pass
|
|
+ elif name == "description":
|
|
+ pass
|
|
+ elif name == "service":
|
|
+ if self._rule:
|
|
+ if self._rule.element:
|
|
+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_Service(attrs["name"])
|
|
+ return
|
|
+ if attrs["name"] not in self.item.services:
|
|
+ self.item.services.append(attrs["name"])
|
|
+ else:
|
|
+ log.warning("Service '%s' already set, ignoring.",
|
|
+ attrs["name"])
|
|
+ elif name == "ingress-zone":
|
|
+ if attrs["name"] not in self.item.ingress_zones:
|
|
+ self.item.ingress_zones.append(attrs["name"])
|
|
+ else:
|
|
+ log.warning("Ingress zone '%s' already set, ignoring.", attrs["name"])
|
|
+ elif name == "egress-zone":
|
|
+ if attrs["name"] not in self.item.egress_zones:
|
|
+ self.item.egress_zones.append(attrs["name"])
|
|
+ else:
|
|
+ log.warning("Egress zone '%s' already set, ignoring.", attrs["name"])
|
|
+
|
|
+ elif name == "port":
|
|
+ if self._rule:
|
|
+ if self._rule.element:
|
|
+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_Port(attrs["port"],
|
|
+ attrs["protocol"])
|
|
+ return
|
|
+ check_port(attrs["port"])
|
|
+ check_tcpudp(attrs["protocol"])
|
|
+ entry = (portStr(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"])
|
|
+
|
|
+ elif name == "protocol":
|
|
+ if self._rule:
|
|
+ if self._rule.element:
|
|
+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_Protocol(attrs["value"])
|
|
+ else:
|
|
+ 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 == "icmp-block":
|
|
+ if self._rule:
|
|
+ if self._rule.element:
|
|
+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_IcmpBlock(attrs["name"])
|
|
+ return
|
|
+ if attrs["name"] not in self.item.icmp_blocks:
|
|
+ self.item.icmp_blocks.append(attrs["name"])
|
|
+ else:
|
|
+ log.warning("icmp-block '%s' already set, ignoring.",
|
|
+ attrs["name"])
|
|
+
|
|
+ elif name == "icmp-type":
|
|
+ if self._rule:
|
|
+ if self._rule.element:
|
|
+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_IcmpType(attrs["name"])
|
|
+ return
|
|
+ else:
|
|
+ log.warning("Invalid rule: icmp-block '%s' outside of rule",
|
|
+ attrs["name"])
|
|
+
|
|
+ elif name == "masquerade":
|
|
+ if self._rule:
|
|
+ if self._rule.element:
|
|
+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_Masquerade()
|
|
+ else:
|
|
+ if self.item.masquerade:
|
|
+ log.warning("Masquerade already set, ignoring.")
|
|
+ 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.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_ForwardPort(attrs["port"],
|
|
+ attrs["protocol"],
|
|
+ to_port, to_addr)
|
|
+ return
|
|
+
|
|
+ check_port(attrs["port"])
|
|
+ check_tcpudp(attrs["protocol"])
|
|
+ if to_port:
|
|
+ check_port(to_port)
|
|
+ if to_addr:
|
|
+ if not checkIP(to_addr) and not checkIP6(to_addr):
|
|
+ raise FirewallError(errors.INVALID_ADDR,
|
|
+ "to-addr '%s' is not a valid address" \
|
|
+ % to_addr)
|
|
+ entry = (portStr(attrs["port"], "-"), attrs["protocol"],
|
|
+ portStr(to_port, "-"), str(to_addr))
|
|
+ if entry not in self.item.forward_ports:
|
|
+ self.item.forward_ports.append(entry)
|
|
+ else:
|
|
+ log.warning("Forward port %s/%s%s%s already set, ignoring.",
|
|
+ attrs["port"], attrs["protocol"],
|
|
+ " >%s" % to_port if to_port else "",
|
|
+ " @%s" % to_addr if to_addr else "")
|
|
+
|
|
+ elif name == "source-port":
|
|
+ if self._rule:
|
|
+ if self._rule.element:
|
|
+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.element = rich.Rich_SourcePort(attrs["port"],
|
|
+ attrs["protocol"])
|
|
+ return
|
|
+ check_port(attrs["port"])
|
|
+ check_tcpudp(attrs["protocol"])
|
|
+ entry = (portStr(attrs["port"], "-"), attrs["protocol"])
|
|
+ if entry not in self.item.source_ports:
|
|
+ self.item.source_ports.append(entry)
|
|
+ else:
|
|
+ log.warning("Source port '%s/%s' already set, ignoring.",
|
|
+ attrs["port"], attrs["protocol"])
|
|
+
|
|
+ elif name == "destination":
|
|
+ if not self._rule:
|
|
+ log.warning('Invalid rule: Destination outside of rule')
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ if self._rule.destination:
|
|
+ log.warning("Invalid rule: More than one destination in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ return
|
|
+ invert = False
|
|
+ if "invert" in attrs and \
|
|
+ attrs["invert"].lower() in [ "yes", "true" ]:
|
|
+ invert = True
|
|
+ self._rule.destination = rich.Rich_Destination(attrs["address"],
|
|
+ invert)
|
|
+
|
|
+ elif name in [ "accept", "reject", "drop", "mark" ]:
|
|
+ if not self._rule:
|
|
+ log.warning('Invalid rule: Action outside of rule')
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ if self._rule.action:
|
|
+ log.warning('Invalid rule: More than one action')
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ if name == "accept":
|
|
+ self._rule.action = rich.Rich_Accept()
|
|
+ elif name == "reject":
|
|
+ _type = None
|
|
+ if "type" in attrs:
|
|
+ _type = attrs["type"]
|
|
+ self._rule.action = rich.Rich_Reject(_type)
|
|
+ elif name == "drop":
|
|
+ self._rule.action = rich.Rich_Drop()
|
|
+ elif name == "mark":
|
|
+ _set = attrs["set"]
|
|
+ self._rule.action = rich.Rich_Mark(_set)
|
|
+ self._limit_ok = self._rule.action
|
|
+
|
|
+ elif name == "log":
|
|
+ if not self._rule:
|
|
+ log.warning('Invalid rule: Log outside of rule')
|
|
+ return
|
|
+ if self._rule.log:
|
|
+ log.warning('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.warning('Invalid rule: Invalid log level')
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ prefix = attrs["prefix"] if "prefix" in attrs else None
|
|
+ self._rule.log = rich.Rich_Log(prefix, level)
|
|
+ self._limit_ok = self._rule.log
|
|
+
|
|
+ elif name == "audit":
|
|
+ if not self._rule:
|
|
+ log.warning('Invalid rule: Audit outside of rule')
|
|
+ return
|
|
+ if self._rule.audit:
|
|
+ log.warning("Invalid rule: More than one audit in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ self._rule.audit = rich.Rich_Audit()
|
|
+ self._limit_ok = self._rule.audit
|
|
+
|
|
+ elif name == "rule":
|
|
+ family = None
|
|
+ priority = 0
|
|
+ if "family" in attrs:
|
|
+ family = attrs["family"]
|
|
+ if family not in [ "ipv4", "ipv6" ]:
|
|
+ log.warning('Invalid rule: Rule family "%s" invalid',
|
|
+ attrs["family"])
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ if "priority" in attrs:
|
|
+ priority = int(attrs["priority"])
|
|
+ self._rule = rich.Rich_Rule(family=family, priority=priority)
|
|
+
|
|
+ elif name == "limit":
|
|
+ if not self._limit_ok:
|
|
+ log.warning('Invalid rule: Limit outside of action, log and audit')
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ if self._limit_ok.limit:
|
|
+ log.warning("Invalid rule: More than one limit in rule '%s', ignoring.",
|
|
+ str(self._rule))
|
|
+ self._rule_error = True
|
|
+ return
|
|
+ value = attrs["value"]
|
|
+ self._limit_ok.limit = rich.Rich_Limit(value)
|
|
+
|
|
+ else:
|
|
+ log.warning("Unknown XML element '%s'", name)
|
|
+ return
|
|
+
|
|
+ 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 policy_reader(filename, path, no_check_name=False):
|
|
+ policy = Policy()
|
|
+ if not filename.endswith(".xml"):
|
|
+ raise FirewallError(errors.INVALID_NAME,
|
|
+ "'%s' is missing .xml suffix" % filename)
|
|
+ policy.name = filename[:-4]
|
|
+ if not no_check_name:
|
|
+ policy.check_name(policy.name)
|
|
+ policy.filename = filename
|
|
+ policy.path = path
|
|
+ policy.builtin = False if path.startswith(config.ETC_FIREWALLD) else True
|
|
+ policy.default = policy.builtin
|
|
+ handler = policy_ContentHandler(policy)
|
|
+ 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_ZONE,
|
|
+ "not a valid policy file: %s" % \
|
|
+ msg.getException())
|
|
+ del handler
|
|
+ del parser
|
|
+ return policy
|
|
+
|
|
+def policy_writer(policy, path=None):
|
|
+ _path = path if path else policy.path
|
|
+
|
|
+ if policy.filename:
|
|
+ name = "%s/%s" % (_path, policy.filename)
|
|
+ else:
|
|
+ name = "%s/%s.xml" % (_path, policy.name)
|
|
+
|
|
+ if os.path.exists(name):
|
|
+ try:
|
|
+ shutil.copy2(name, "%s.old" % name)
|
|
+ except Exception as msg:
|
|
+ log.error("Backup of file '%s' failed: %s", name, msg)
|
|
+
|
|
+ dirpath = os.path.dirname(name)
|
|
+ if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath):
|
|
+ if not os.path.exists(config.ETC_FIREWALLD):
|
|
+ os.mkdir(config.ETC_FIREWALLD, 0o750)
|
|
+ os.mkdir(dirpath, 0o750)
|
|
+
|
|
+ f = io.open(name, mode='wt', encoding='UTF-8')
|
|
+ handler = IO_Object_XMLGenerator(f)
|
|
+ handler.startDocument()
|
|
+
|
|
+ # start policy element
|
|
+ attrs = {}
|
|
+ if policy.version and policy.version != "":
|
|
+ attrs["version"] = policy.version
|
|
+ if policy.priority != policy.priority_default:
|
|
+ attrs["priority"] = str(policy.priority)
|
|
+ attrs["target"] = policy.target
|
|
+ handler.startElement("policy", attrs)
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # short
|
|
+ if policy.short and policy.short != "":
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.startElement("short", { })
|
|
+ handler.characters(policy.short)
|
|
+ handler.endElement("short")
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # description
|
|
+ if policy.description and policy.description != "":
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.startElement("description", { })
|
|
+ handler.characters(policy.description)
|
|
+ handler.endElement("description")
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # services
|
|
+ for service in uniqify(policy.services):
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("service", { "name": service })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # ingress-zones
|
|
+ for zone in uniqify(policy.ingress_zones):
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("ingress-zone", { "name": zone })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # egress-zones
|
|
+ for zone in uniqify(policy.ingress_zones):
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("egress-zone", { "name": zone })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # ports
|
|
+ for port in uniqify(policy.ports):
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("port", { "port": port[0], "protocol": port[1] })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # protocols
|
|
+ for protocol in uniqify(policy.protocols):
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("protocol", { "value": protocol })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # icmp-blocks
|
|
+ for icmp in uniqify(policy.icmp_blocks):
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("icmp-block", { "name": icmp })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # masquerade
|
|
+ if policy.masquerade:
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("masquerade", { })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # forward-ports
|
|
+ for forward in uniqify(policy.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")
|
|
+
|
|
+ # source-ports
|
|
+ for port in uniqify(policy.source_ports):
|
|
+ handler.ignorableWhitespace(" ")
|
|
+ handler.simpleElement("source-port", { "port": port[0],
|
|
+ "protocol": port[1] })
|
|
+ handler.ignorableWhitespace("\n")
|
|
+
|
|
+ # rules
|
|
+ for rule in policy.rules:
|
|
+ attrs = { }
|
|
+ if rule.family:
|
|
+ attrs["family"] = rule.family
|
|
+ if rule.priority != 0:
|
|
+ attrs["priority"] = str(rule.priority)
|
|
+ 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.Rich_Service:
|
|
+ element = "service"
|
|
+ attrs["name"] = rule.element.name
|
|
+ elif type(rule.element) == rich.Rich_Port:
|
|
+ element = "port"
|
|
+ attrs["port"] = rule.element.port
|
|
+ attrs["protocol"] = rule.element.protocol
|
|
+ elif type(rule.element) == rich.Rich_Protocol:
|
|
+ element = "protocol"
|
|
+ attrs["value"] = rule.element.value
|
|
+ elif type(rule.element) == rich.Rich_Masquerade:
|
|
+ element = "masquerade"
|
|
+ elif type(rule.element) == rich.Rich_IcmpBlock:
|
|
+ element = "icmp-block"
|
|
+ attrs["name"] = rule.element.name
|
|
+ elif type(rule.element) == rich.Rich_IcmpType:
|
|
+ element = "icmp-type"
|
|
+ attrs["name"] = rule.element.name
|
|
+ elif type(rule.element) == rich.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
|
|
+ elif type(rule.element) == rich.Rich_SourcePort:
|
|
+ element = "source-port"
|
|
+ attrs["port"] = rule.element.port
|
|
+ attrs["protocol"] = rule.element.protocol
|
|
+ else:
|
|
+ raise FirewallError(
|
|
+ errors.INVALID_OBJECT,
|
|
+ "Unknown element '%s' in policy_writer" % 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.Rich_Accept:
|
|
+ action = "accept"
|
|
+ elif type(rule.action) == rich.Rich_Reject:
|
|
+ action = "reject"
|
|
+ if rule.action.type:
|
|
+ attrs["type"] = rule.action.type
|
|
+ elif type(rule.action) == rich.Rich_Drop:
|
|
+ action = "drop"
|
|
+ elif type(rule.action) == rich.Rich_Mark:
|
|
+ action = "mark"
|
|
+ attrs["set"] = rule.action.set
|
|
+ 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 policy element
|
|
+ handler.endElement("policy")
|
|
+ handler.ignorableWhitespace("\n")
|
|
+ handler.endDocument()
|
|
+ f.close()
|
|
+ del handler
|
|
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
|
|
index b1d6c20..3d05d7b 100644
|
|
--- a/src/firewall/core/ipXtables.py
|
|
+++ b/src/firewall/core/ipXtables.py
|
|
@@ -22,7 +22,6 @@
|
|
import os.path
|
|
import copy
|
|
|
|
-from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET
|
|
from firewall.core.prog import runProg
|
|
from firewall.core.logger import log
|
|
from firewall.functions import tempFile, readfile, splitArgs, check_mac, portStr, \
|
|
@@ -33,6 +32,8 @@ from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark, \
|
|
Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock
|
|
import string
|
|
|
|
+POLICY_CHAIN_PREFIX = "pol_"
|
|
+
|
|
BUILT_IN_CHAINS = {
|
|
"security": [ "INPUT", "OUTPUT", "FORWARD" ],
|
|
"raw": [ "PREROUTING", "OUTPUT" ],
|
|
@@ -167,7 +168,7 @@ def common_check_passthrough(args):
|
|
class ip4tables(object):
|
|
ipv = "ipv4"
|
|
name = "ip4tables"
|
|
- zones_supported = True
|
|
+ policies_supported = True
|
|
|
|
def __init__(self, fw):
|
|
self._fw = fw
|
|
@@ -769,10 +770,9 @@ class ip4tables(object):
|
|
|
|
return {}
|
|
|
|
- def build_zone_source_interface_rules(self, enable, zone, interface,
|
|
+ def build_zone_source_interface_rules(self, enable, zone, policy, interface,
|
|
table, chain, append=False):
|
|
- # handle all zones in the same way here, now
|
|
- # trust and block zone targets are handled now in __chain
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
opt = {
|
|
"PREROUTING": "-i",
|
|
"POSTROUTING": "-o",
|
|
@@ -782,7 +782,6 @@ class ip4tables(object):
|
|
"OUTPUT": "-o",
|
|
}[chain]
|
|
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
|
|
action = "-g"
|
|
|
|
if enable and not append:
|
|
@@ -793,13 +792,14 @@ class ip4tables(object):
|
|
rule = [ "-D", "%s_ZONES" % chain ]
|
|
if not append:
|
|
rule += ["%%ZONE_INTERFACE%%"]
|
|
- rule += [ "-t", table, opt, interface, action, target ]
|
|
+ rule += [ "-t", table, opt, interface, action, _policy ]
|
|
return [rule]
|
|
|
|
- def build_zone_source_address_rules(self, enable, zone,
|
|
+ def build_zone_source_address_rules(self, enable, zone, policy,
|
|
address, table, chain):
|
|
add_del = { True: "-I", False: "-D" }[enable]
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
opt = {
|
|
"PREROUTING": "-s",
|
|
"POSTROUTING": "-d",
|
|
@@ -814,7 +814,6 @@ class ip4tables(object):
|
|
else:
|
|
zone_dispatch_chain = "%s_ZONES" % (chain)
|
|
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
|
|
action = "-g"
|
|
|
|
if address.startswith("ipset:"):
|
|
@@ -828,7 +827,7 @@ class ip4tables(object):
|
|
"%%ZONE_SOURCE%%", zone,
|
|
"-t", table,
|
|
"-m", "set", "--match-set", name,
|
|
- flags, action, target ]
|
|
+ flags, action, _policy ]
|
|
else:
|
|
if check_mac(address):
|
|
# outgoing can not be set
|
|
@@ -838,7 +837,7 @@ class ip4tables(object):
|
|
"%%ZONE_SOURCE%%", zone,
|
|
"-t", table,
|
|
"-m", "mac", "--mac-source", address.upper(),
|
|
- action, target ]
|
|
+ action, _policy ]
|
|
else:
|
|
if check_single_address("ipv6", address):
|
|
address = normalizeIP6(address)
|
|
@@ -848,56 +847,48 @@ class ip4tables(object):
|
|
rule = [ add_del, zone_dispatch_chain,
|
|
"%%ZONE_SOURCE%%", zone,
|
|
"-t", table,
|
|
- opt, address, action, target ]
|
|
+ opt, address, action, _policy ]
|
|
return [rule]
|
|
|
|
- def build_zone_chain_rules(self, zone, table, chain):
|
|
- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
|
|
+ def build_policy_chain_rules(self, policy, table):
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
- self.our_chains[table].update(set([_zone,
|
|
- "%s_log" % _zone,
|
|
- "%s_deny" % _zone,
|
|
- "%s_pre" % _zone,
|
|
- "%s_post" % _zone,
|
|
- "%s_allow" % _zone]))
|
|
+ self.our_chains[table].update(set([_policy,
|
|
+ "%s_log" % _policy,
|
|
+ "%s_deny" % _policy,
|
|
+ "%s_pre" % _policy,
|
|
+ "%s_post" % _policy,
|
|
+ "%s_allow" % _policy]))
|
|
|
|
rules = []
|
|
- rules.append([ "-N", _zone, "-t", table ])
|
|
- rules.append([ "-N", "%s_pre" % _zone, "-t", table ])
|
|
- rules.append([ "-N", "%s_log" % _zone, "-t", table ])
|
|
- rules.append([ "-N", "%s_deny" % _zone, "-t", table ])
|
|
- rules.append([ "-N", "%s_allow" % _zone, "-t", table ])
|
|
- rules.append([ "-N", "%s_post" % _zone, "-t", table ])
|
|
- rules.append([ "-A", _zone, "-t", table, "-j", "%s_pre" % _zone ])
|
|
- rules.append([ "-A", _zone, "-t", table, "-j", "%s_log" % _zone ])
|
|
- rules.append([ "-A", _zone, "-t", table, "-j", "%s_deny" % _zone ])
|
|
- rules.append([ "-A", _zone, "-t", table, "-j", "%s_allow" % _zone ])
|
|
- rules.append([ "-A", _zone, "-t", table, "-j", "%s_post" % _zone ])
|
|
-
|
|
- target = self._fw.zone._zones[zone].target
|
|
+ rules.append([ "-N", _policy, "-t", table ])
|
|
+ rules.append([ "-N", "%s_pre" % _policy, "-t", table ])
|
|
+ rules.append([ "-N", "%s_log" % _policy, "-t", table ])
|
|
+ rules.append([ "-N", "%s_deny" % _policy, "-t", table ])
|
|
+ rules.append([ "-N", "%s_allow" % _policy, "-t", table ])
|
|
+ rules.append([ "-N", "%s_post" % _policy, "-t", table ])
|
|
+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_pre" % _policy ])
|
|
+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_log" % _policy ])
|
|
+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_deny" % _policy ])
|
|
+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_allow" % _policy ])
|
|
+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_post" % _policy ])
|
|
+
|
|
+ target = self._fw.policy._policies[policy].target
|
|
|
|
if self._fw.get_log_denied() != "off":
|
|
- if table == "filter" and \
|
|
- chain in [ "INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT" ]:
|
|
+ if table == "filter":
|
|
if target in [ "REJECT", "%%REJECT%%" ]:
|
|
- rules.append([ "-A", _zone, "-t", table, "%%LOGTYPE%%",
|
|
+ rules.append([ "-A", _policy, "-t", table, "%%LOGTYPE%%",
|
|
"-j", "LOG", "--log-prefix",
|
|
- "\"%s_REJECT: \"" % _zone ])
|
|
+ "\"%s_REJECT: \"" % _policy ])
|
|
if target == "DROP":
|
|
- rules.append([ "-A", _zone, "-t", table, "%%LOGTYPE%%",
|
|
+ rules.append([ "-A", _policy, "-t", table, "%%LOGTYPE%%",
|
|
"-j", "LOG", "--log-prefix",
|
|
- "\"%s_DROP: \"" % _zone ])
|
|
-
|
|
- # Handle trust, block and drop zones:
|
|
- # Add an additional rule with the zone target (accept, reject
|
|
- # or drop) to the base zone only in the filter table.
|
|
- # Otherwise it is not be possible to have a zone with drop
|
|
- # target, that is allowing traffic that is locally initiated
|
|
- # or that adds additional rules. (RHBZ#1055190)
|
|
+ "\"%s_DROP: \"" % _policy ])
|
|
+
|
|
if table == "filter" and \
|
|
- target in [ "ACCEPT", "REJECT", "%%REJECT%%", "DROP" ] and \
|
|
- chain in [ "INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT" ]:
|
|
- rules.append([ "-A", _zone, "-t", table, "-j", target ])
|
|
+ target in [ "ACCEPT", "REJECT", "%%REJECT%%", "DROP" ]:
|
|
+ rules.append([ "-A", _policy, "-t", table, "-j", target ])
|
|
|
|
return rules
|
|
|
|
@@ -944,14 +935,16 @@ class ip4tables(object):
|
|
return []
|
|
return ["%%RICH_RULE_PRIORITY%%", rich_rule.priority]
|
|
|
|
- def _rich_rule_log(self, rich_rule, enable, table, target, rule_fragment):
|
|
+ def _rich_rule_log(self, policy, rich_rule, enable, table, rule_fragment):
|
|
if not rich_rule.log:
|
|
return []
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
|
|
chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule)
|
|
- rule = ["-t", table, add_del, "%s_%s" % (target, chain_suffix)]
|
|
+ rule = ["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)]
|
|
rule += self._rich_rule_priority_fragment(rich_rule)
|
|
rule += rule_fragment + [ "-j", "LOG" ]
|
|
if rich_rule.log.prefix:
|
|
@@ -962,14 +955,16 @@ class ip4tables(object):
|
|
|
|
return rule
|
|
|
|
- def _rich_rule_audit(self, rich_rule, enable, table, target, rule_fragment):
|
|
+ def _rich_rule_audit(self, policy, rich_rule, enable, table, rule_fragment):
|
|
if not rich_rule.audit:
|
|
return []
|
|
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+
|
|
chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule)
|
|
- rule = ["-t", table, add_del, "%s_%s" % (target, chain_suffix)]
|
|
+ rule = ["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)]
|
|
rule += self._rich_rule_priority_fragment(rich_rule)
|
|
rule += rule_fragment
|
|
if type(rich_rule.action) == Rich_Accept:
|
|
@@ -985,14 +980,16 @@ class ip4tables(object):
|
|
|
|
return rule
|
|
|
|
- def _rich_rule_action(self, zone, rich_rule, enable, table, target, rule_fragment):
|
|
+ def _rich_rule_action(self, policy, rich_rule, enable, table, rule_fragment):
|
|
if not rich_rule.action:
|
|
return []
|
|
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+
|
|
chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
- chain = "%s_%s" % (target, chain_suffix)
|
|
+ chain = "%s_%s" % (_policy, chain_suffix)
|
|
if type(rich_rule.action) == Rich_Accept:
|
|
rule_action = [ "-j", "ACCEPT" ]
|
|
elif type(rich_rule.action) == Rich_Reject:
|
|
@@ -1002,10 +999,9 @@ class ip4tables(object):
|
|
elif type(rich_rule.action) == Rich_Drop:
|
|
rule_action = [ "-j", "DROP" ]
|
|
elif type(rich_rule.action) == Rich_Mark:
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
|
|
- zone=zone)
|
|
table = "mangle"
|
|
- chain = "%s_%s" % (target, chain_suffix)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+ chain = "%s_%s" % (_policy, chain_suffix)
|
|
rule_action = [ "-j", "MARK", "--set-xmark", rich_rule.action.set ]
|
|
else:
|
|
raise FirewallError(INVALID_RULE,
|
|
@@ -1064,11 +1060,10 @@ class ip4tables(object):
|
|
|
|
return rule_fragment
|
|
|
|
- def build_zone_ports_rules(self, enable, zone, proto, port, destination=None, rich_rule=None):
|
|
+ def build_policy_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None):
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"],
|
|
- zone=zone)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
rule_fragment = [ "-p", proto ]
|
|
if port:
|
|
@@ -1083,19 +1078,19 @@ class ip4tables(object):
|
|
|
|
rules = []
|
|
if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment))
|
|
else:
|
|
- rules.append([add_del, "%s_allow" % (target), "-t", table] +
|
|
+ rules.append([add_del, "%s_allow" % (_policy), "-t", table] +
|
|
rule_fragment + [ "-j", "ACCEPT" ])
|
|
|
|
return rules
|
|
|
|
- def build_zone_protocol_rules(self, enable, zone, protocol, destination=None, rich_rule=None):
|
|
+ def build_policy_protocol_rules(self, enable, policy, protocol, destination=None, rich_rule=None):
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
rule_fragment = [ "-p", protocol ]
|
|
if destination:
|
|
@@ -1108,20 +1103,20 @@ class ip4tables(object):
|
|
|
|
rules = []
|
|
if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment))
|
|
else:
|
|
- rules.append([add_del, "%s_allow" % (target), "-t", table] +
|
|
+ rules.append([add_del, "%s_allow" % (_policy), "-t", table] +
|
|
rule_fragment + [ "-j", "ACCEPT" ])
|
|
|
|
return rules
|
|
|
|
- def build_zone_source_ports_rules(self, enable, zone, proto, port,
|
|
+ def build_policy_source_ports_rules(self, enable, policy, proto, port,
|
|
destination=None, rich_rule=None):
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
rule_fragment = [ "-p", proto ]
|
|
if port:
|
|
@@ -1136,21 +1131,22 @@ class ip4tables(object):
|
|
|
|
rules = []
|
|
if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment))
|
|
else:
|
|
- rules.append([add_del, "%s_allow" % (target), "-t", table] +
|
|
+ rules.append([add_del, "%s_allow" % (_policy), "-t", table] +
|
|
rule_fragment + [ "-j", "ACCEPT" ])
|
|
|
|
return rules
|
|
|
|
- def build_zone_helper_ports_rules(self, enable, zone, proto, port,
|
|
+ def build_policy_helper_ports_rules(self, enable, policy, proto, port,
|
|
destination, helper_name, module_short_name):
|
|
+ table = "raw"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
|
|
- zone=zone)
|
|
- rule = [ add_del, "%s_allow" % (target), "-t", "raw", "-p", proto ]
|
|
+
|
|
+ rule = [ add_del, "%s_allow" % (_policy), "-t", "raw", "-p", proto ]
|
|
if port:
|
|
rule += [ "--dport", "%s" % portStr(port) ]
|
|
if destination:
|
|
@@ -1159,10 +1155,11 @@ class ip4tables(object):
|
|
|
|
return [rule]
|
|
|
|
- def build_zone_masquerade_rules(self, enable, zone, rich_rule=None):
|
|
+ def build_policy_masquerade_rules(self, enable, policy, rich_rule=None):
|
|
+ table = "nat"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["POSTROUTING"],
|
|
- zone=zone)
|
|
+
|
|
rule_fragment = []
|
|
if rich_rule:
|
|
chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
@@ -1173,12 +1170,10 @@ class ip4tables(object):
|
|
chain_suffix = "allow"
|
|
|
|
rules = []
|
|
- rules.append(["-t", "nat", add_del, "%s_%s" % (target, chain_suffix)]
|
|
+ rules.append(["-t", "nat", add_del, "%s_%s" % (_policy, chain_suffix)]
|
|
+ rule_fragment +
|
|
[ "!", "-o", "lo", "-j", "MASQUERADE" ])
|
|
- # FORWARD_OUT
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["FORWARD_OUT"],
|
|
- zone=zone)
|
|
+
|
|
rule_fragment = []
|
|
if rich_rule:
|
|
chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
@@ -1188,14 +1183,18 @@ class ip4tables(object):
|
|
else:
|
|
chain_suffix = "allow"
|
|
|
|
- rules.append(["-t", "filter", add_del, "%s_%s" % (target, chain_suffix)]
|
|
+ table = "filter"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+ rules.append(["-t", "filter", add_del, "%s_%s" % (_policy, chain_suffix)]
|
|
+ rule_fragment +
|
|
["-m", "conntrack", "--ctstate", "NEW,UNTRACKED", "-j", "ACCEPT" ])
|
|
|
|
return rules
|
|
|
|
- def build_zone_forward_port_rules(self, enable, zone, port,
|
|
+ def build_policy_forward_port_rules(self, enable, policy, port,
|
|
protocol, toport, toaddr, rich_rule=None):
|
|
+ table = "nat"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
|
|
to = ""
|
|
@@ -1207,9 +1206,6 @@ class ip4tables(object):
|
|
if toport and toport != "":
|
|
to += ":%s" % portStr(toport, "-")
|
|
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
|
|
- zone=zone)
|
|
-
|
|
rule_fragment = []
|
|
if rich_rule:
|
|
chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
@@ -1221,16 +1217,17 @@ class ip4tables(object):
|
|
|
|
rules = []
|
|
if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, "nat", target, rule_fragment))
|
|
- rules.append(["-t", "nat", add_del, "%s_%s" % (target, chain_suffix)]
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, "nat", rule_fragment))
|
|
+ rules.append(["-t", "nat", add_del, "%s_%s" % (_policy, chain_suffix)]
|
|
+ rule_fragment +
|
|
["-p", protocol, "--dport", portStr(port),
|
|
"-j", "DNAT", "--to-destination", to])
|
|
|
|
return rules
|
|
|
|
- def build_zone_icmp_block_rules(self, enable, zone, ict, rich_rule=None):
|
|
+ def build_policy_icmp_block_rules(self, enable, policy, ict, rich_rule=None):
|
|
table = "filter"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "-A", False: "-D" }[enable]
|
|
|
|
if self.ipv == "ipv4":
|
|
@@ -1241,93 +1238,87 @@ class ip4tables(object):
|
|
match = [ "-m", "icmp6", "--icmpv6-type", ict.name ]
|
|
|
|
rules = []
|
|
- for chain in ["INPUT", "FORWARD_IN"]:
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain],
|
|
- zone=zone)
|
|
- if self._fw.zone.query_icmp_block_inversion(zone):
|
|
- final_chain = "%s_allow" % target
|
|
- final_target = "ACCEPT"
|
|
- else:
|
|
- final_chain = "%s_deny" % target
|
|
- final_target = "%%REJECT%%"
|
|
-
|
|
- rule_fragment = []
|
|
- if rich_rule:
|
|
- rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
|
|
- rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
|
|
- rule_fragment += proto + match
|
|
-
|
|
- if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
|
|
- if rich_rule.action:
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
|
|
- else:
|
|
- chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
- rules.append(["-t", table, add_del, "%s_%s" % (target, chain_suffix)]
|
|
- + self._rich_rule_priority_fragment(rich_rule)
|
|
- + rule_fragment +
|
|
- [ "-j", "%%REJECT%%" ])
|
|
+ if self._fw.policy.query_icmp_block_inversion(policy):
|
|
+ final_chain = "%s_allow" % (_policy)
|
|
+ final_target = "ACCEPT"
|
|
+ else:
|
|
+ final_chain = "%s_deny" % (_policy)
|
|
+ final_target = "%%REJECT%%"
|
|
+
|
|
+ rule_fragment = []
|
|
+ if rich_rule:
|
|
+ rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
|
|
+ rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
|
|
+ rule_fragment += proto + match
|
|
+
|
|
+ if rich_rule:
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment))
|
|
+ if rich_rule.action:
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment))
|
|
else:
|
|
- if self._fw.get_log_denied() != "off" and final_target != "ACCEPT":
|
|
- rules.append([ add_del, final_chain, "-t", table ]
|
|
- + rule_fragment +
|
|
- [ "%%LOGTYPE%%", "-j", "LOG",
|
|
- "--log-prefix", "\"%s_ICMP_BLOCK: \"" % zone ])
|
|
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
+ rules.append(["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)]
|
|
+ + self._rich_rule_priority_fragment(rich_rule)
|
|
+ + rule_fragment +
|
|
+ [ "-j", "%%REJECT%%" ])
|
|
+ else:
|
|
+ if self._fw.get_log_denied() != "off" and final_target != "ACCEPT":
|
|
rules.append([ add_del, final_chain, "-t", table ]
|
|
+ rule_fragment +
|
|
- [ "-j", final_target ])
|
|
+ [ "%%LOGTYPE%%", "-j", "LOG",
|
|
+ "--log-prefix", "\"%s_ICMP_BLOCK: \"" % policy ])
|
|
+ rules.append([ add_del, final_chain, "-t", table ]
|
|
+ + rule_fragment +
|
|
+ [ "-j", final_target ])
|
|
|
|
return rules
|
|
|
|
- def build_zone_icmp_block_inversion_rules(self, enable, zone):
|
|
+ def build_policy_icmp_block_inversion_rules(self, enable, policy):
|
|
table = "filter"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+
|
|
rules = []
|
|
- for chain in [ "INPUT", "FORWARD_IN" ]:
|
|
- rule_idx = 6
|
|
- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain],
|
|
- zone=zone)
|
|
+ rule_idx = 6
|
|
|
|
- if self._fw.zone.query_icmp_block_inversion(zone):
|
|
- ibi_target = "%%REJECT%%"
|
|
+ if self._fw.policy.query_icmp_block_inversion(policy):
|
|
+ ibi_target = "%%REJECT%%"
|
|
|
|
- if self._fw.get_log_denied() != "off":
|
|
- if enable:
|
|
- rule = [ "-I", _zone, str(rule_idx) ]
|
|
- else:
|
|
- rule = [ "-D", _zone ]
|
|
-
|
|
- rule = rule + [ "-t", table, "-p", "%%ICMP%%",
|
|
- "%%LOGTYPE%%",
|
|
- "-j", "LOG", "--log-prefix",
|
|
- "\"%s_ICMP_BLOCK: \"" % _zone ]
|
|
- rules.append(rule)
|
|
- rule_idx += 1
|
|
- else:
|
|
- ibi_target = "ACCEPT"
|
|
+ if self._fw.get_log_denied() != "off":
|
|
+ if enable:
|
|
+ rule = [ "-I", _policy, str(rule_idx) ]
|
|
+ else:
|
|
+ rule = [ "-D", _policy ]
|
|
+
|
|
+ rule = rule + [ "-t", table, "-p", "%%ICMP%%",
|
|
+ "%%LOGTYPE%%",
|
|
+ "-j", "LOG", "--log-prefix",
|
|
+ "\"%s_ICMP_BLOCK: \"" % _policy ]
|
|
+ rules.append(rule)
|
|
+ rule_idx += 1
|
|
+ else:
|
|
+ ibi_target = "ACCEPT"
|
|
|
|
- if enable:
|
|
- rule = [ "-I", _zone, str(rule_idx) ]
|
|
- else:
|
|
- rule = [ "-D", _zone ]
|
|
- rule = rule + [ "-t", table, "-p", "%%ICMP%%", "-j", ibi_target ]
|
|
- rules.append(rule)
|
|
+ if enable:
|
|
+ rule = [ "-I", _policy, str(rule_idx) ]
|
|
+ else:
|
|
+ rule = [ "-D", _policy ]
|
|
+ rule = rule + [ "-t", table, "-p", "%%ICMP%%", "-j", ibi_target ]
|
|
+ rules.append(rule)
|
|
|
|
return rules
|
|
|
|
- def build_zone_rich_source_destination_rules(self, enable, zone, rich_rule):
|
|
+ def build_policy_rich_source_destination_rules(self, enable, policy, rich_rule):
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"],
|
|
- zone=zone)
|
|
|
|
rule_fragment = []
|
|
rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
|
|
rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
|
|
|
|
rules = []
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment))
|
|
|
|
return rules
|
|
|
|
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
|
index a6ad5f7..f422556 100644
|
|
--- a/src/firewall/core/nftables.py
|
|
+++ b/src/firewall/core/nftables.py
|
|
@@ -23,7 +23,6 @@ from __future__ import absolute_import
|
|
import copy
|
|
import json
|
|
|
|
-from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET
|
|
from firewall.core.logger import log
|
|
from firewall.functions import check_mac, getPortRange, normalizeIP6, \
|
|
check_single_address, check_address
|
|
@@ -36,6 +35,7 @@ from nftables.nftables import Nftables
|
|
|
|
TABLE_NAME = "firewalld"
|
|
TABLE_NAME_POLICY = TABLE_NAME + "_" + "policy_drop"
|
|
+POLICY_CHAIN_PREFIX = "policy_"
|
|
|
|
# Map iptables (table, chain) to hooks and priorities.
|
|
# These are well defined by NF_IP_PRI_* defines in netfilter.
|
|
@@ -158,7 +158,7 @@ ICMP_TYPES_FRAGMENTS = {
|
|
|
|
class nftables(object):
|
|
name = "nftables"
|
|
- zones_supported = True
|
|
+ policies_supported = True
|
|
|
|
def __init__(self, fw):
|
|
self._fw = fw
|
|
@@ -174,7 +174,6 @@ class nftables(object):
|
|
self.nftables.set_echo_output(True)
|
|
self.nftables.set_handle_output(True)
|
|
|
|
-
|
|
def _run_replace_zone_source(self, rule, zone_source_index_cache):
|
|
for verb in ["add", "insert", "delete"]:
|
|
if verb in rule:
|
|
@@ -698,18 +697,19 @@ class nftables(object):
|
|
|
|
return []
|
|
|
|
- def build_zone_source_interface_rules(self, enable, zone, interface,
|
|
+ def build_zone_source_interface_rules(self, enable, zone, policy, interface,
|
|
table, chain, append=False,
|
|
family="inet"):
|
|
# nat tables needs to use ip/ip6 family
|
|
if table == "nat" and family == "inet":
|
|
rules = []
|
|
- rules.extend(self.build_zone_source_interface_rules(enable, zone,
|
|
+ rules.extend(self.build_zone_source_interface_rules(enable, zone, policy,
|
|
interface, table, chain, append, "ip"))
|
|
- rules.extend(self.build_zone_source_interface_rules(enable, zone,
|
|
+ rules.extend(self.build_zone_source_interface_rules(enable, zone, policy,
|
|
interface, table, chain, append, "ip6"))
|
|
return rules
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
opt = {
|
|
"PREROUTING": "iifname",
|
|
"POSTROUTING": "oifname",
|
|
@@ -722,16 +722,15 @@ class nftables(object):
|
|
if interface[len(interface)-1] == "+":
|
|
interface = interface[:len(interface)-1] + "*"
|
|
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
|
|
action = "goto"
|
|
|
|
if interface == "*":
|
|
- expr_fragments = [{action: {"target": "%s_%s" % (table, target)}}]
|
|
+ expr_fragments = [{action: {"target": "%s_%s" % (table, _policy)}}]
|
|
else:
|
|
expr_fragments = [{"match": {"left": {"meta": {"key": opt}},
|
|
"op": "==",
|
|
"right": interface}},
|
|
- {action: {"target": "%s_%s" % (table, target)}}]
|
|
+ {action: {"target": "%s_%s" % (table, _policy)}}]
|
|
|
|
if enable and not append:
|
|
verb = "insert"
|
|
@@ -757,7 +756,7 @@ class nftables(object):
|
|
|
|
return [{verb: {"rule": rule}}]
|
|
|
|
- def build_zone_source_address_rules(self, enable, zone,
|
|
+ def build_zone_source_address_rules(self, enable, zone, policy,
|
|
address, table, chain, family="inet"):
|
|
# nat tables needs to use ip/ip6 family
|
|
if table == "nat" and family == "inet":
|
|
@@ -768,13 +767,14 @@ class nftables(object):
|
|
ipset_family = None
|
|
|
|
if check_address("ipv4", address) or check_mac(address) or ipset_family == "ip":
|
|
- rules.extend(self.build_zone_source_address_rules(enable, zone,
|
|
+ rules.extend(self.build_zone_source_address_rules(enable, zone, policy,
|
|
address, table, chain, "ip"))
|
|
if check_address("ipv6", address) or check_mac(address) or ipset_family == "ip6":
|
|
- rules.extend(self.build_zone_source_address_rules(enable, zone,
|
|
+ rules.extend(self.build_zone_source_address_rules(enable, zone, policy,
|
|
address, table, chain, "ip6"))
|
|
return rules
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "insert", False: "delete" }[enable]
|
|
|
|
opt = {
|
|
@@ -791,73 +791,64 @@ class nftables(object):
|
|
else:
|
|
zone_dispatch_chain = "%s_%s_ZONES" % (table, chain)
|
|
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
|
|
action = "goto"
|
|
|
|
rule = {"family": family,
|
|
"table": TABLE_NAME,
|
|
"chain": zone_dispatch_chain,
|
|
"expr": [self._rule_addr_fragment(opt, address),
|
|
- {action: {"target": "%s_%s" % (table, target)}}]}
|
|
+ {action: {"target": "%s_%s" % (table, _policy)}}]}
|
|
rule.update(self._zone_source_fragment(zone, address))
|
|
return [{add_del: {"rule": rule}}]
|
|
|
|
- def build_zone_chain_rules(self, zone, table, chain, family="inet"):
|
|
+ def build_policy_chain_rules(self, policy, table, family="inet"):
|
|
# nat tables needs to use ip/ip6 family
|
|
if table == "nat" and family == "inet":
|
|
rules = []
|
|
- rules.extend(self.build_zone_chain_rules(zone, table, chain, "ip"))
|
|
- rules.extend(self.build_zone_chain_rules(zone, table, chain, "ip6"))
|
|
+ rules.extend(self.build_policy_chain_rules(policy, table, "ip"))
|
|
+ rules.extend(self.build_policy_chain_rules(policy, table, "ip6"))
|
|
return rules
|
|
|
|
- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
rules = []
|
|
rules.append({"add": {"chain": {"family": family,
|
|
"table": TABLE_NAME,
|
|
- "name": "%s_%s" % (table, _zone)}}})
|
|
+ "name": "%s_%s" % (table, _policy)}}})
|
|
for chain_suffix in ["pre", "log", "deny", "allow", "post"]:
|
|
rules.append({"add": {"chain": {"family": family,
|
|
"table": TABLE_NAME,
|
|
- "name": "%s_%s_%s" % (table, _zone, chain_suffix)}}})
|
|
+ "name": "%s_%s_%s" % (table, _policy, chain_suffix)}}})
|
|
|
|
for chain_suffix in ["pre", "log", "deny", "allow", "post"]:
|
|
rules.append({"add": {"rule": {"family": family,
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s" % (table, _zone),
|
|
- "expr": [{"jump": {"target": "%s_%s_%s" % (table, _zone, chain_suffix)}}]}}})
|
|
+ "chain": "%s_%s" % (table, _policy),
|
|
+ "expr": [{"jump": {"target": "%s_%s_%s" % (table, _policy, chain_suffix)}}]}}})
|
|
|
|
- target = self._fw.zone._zones[zone].target
|
|
+ target = self._fw.policy._policies[policy].target
|
|
|
|
if self._fw.get_log_denied() != "off":
|
|
- if table == "filter" and \
|
|
- chain in ["INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT"]:
|
|
+ if table == "filter":
|
|
if target in ["REJECT", "%%REJECT%%", "DROP"]:
|
|
log_suffix = target
|
|
if target == "%%REJECT%%":
|
|
log_suffix = "REJECT"
|
|
rules.append({"add": {"rule": {"family": family,
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s" % (table, _zone),
|
|
+ "chain": "%s_%s" % (table, _policy),
|
|
"expr": [self._pkttype_match_fragment(self._fw.get_log_denied()),
|
|
- {"log": {"prefix": "\"filter_%s_%s: \"" % (_zone, log_suffix)}}]}}})
|
|
-
|
|
- # Handle trust, block and drop zones:
|
|
- # Add an additional rule with the zone target (accept, reject
|
|
- # or drop) to the base zone only in the filter table.
|
|
- # Otherwise it is not be possible to have a zone with drop
|
|
- # target, that is allowing traffic that is locally initiated
|
|
- # or that adds additional rules. (RHBZ#1055190)
|
|
+ {"log": {"prefix": "\"filter_%s_%s: \"" % (_policy, log_suffix)}}]}}})
|
|
+
|
|
if table == "filter" and \
|
|
- target in ["ACCEPT", "REJECT", "%%REJECT%%", "DROP"] and \
|
|
- chain in ["INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT"]:
|
|
+ target in ["ACCEPT", "REJECT", "%%REJECT%%", "DROP"]:
|
|
if target == "%%REJECT%%":
|
|
target_fragment = self._reject_fragment()
|
|
else:
|
|
target_fragment = {target.lower(): None}
|
|
rules.append({"add": {"rule": {"family": family,
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s" % (table, _zone),
|
|
+ "chain": "%s_%s" % (table, _policy),
|
|
"expr": [target_fragment]}}})
|
|
|
|
return rules
|
|
@@ -981,10 +972,12 @@ class nftables(object):
|
|
return {}
|
|
return {"%%RICH_RULE_PRIORITY%%": rich_rule.priority}
|
|
|
|
- def _rich_rule_log(self, rich_rule, enable, table, target, expr_fragments):
|
|
+ def _rich_rule_log(self, policy, rich_rule, enable, table, expr_fragments):
|
|
if not rich_rule.log:
|
|
return {}
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
|
|
chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule)
|
|
@@ -997,37 +990,41 @@ class nftables(object):
|
|
|
|
rule = {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s_%s" % (table, target, chain_suffix),
|
|
+ "chain": "%s_%s_%s" % (table, _policy, chain_suffix),
|
|
"expr": expr_fragments +
|
|
[{"log": log_options},
|
|
self._rich_rule_limit_fragment(rich_rule.log.limit)]}
|
|
rule.update(self._rich_rule_priority_fragment(rich_rule))
|
|
return {add_del: {"rule": rule}}
|
|
|
|
- def _rich_rule_audit(self, rich_rule, enable, table, target, expr_fragments):
|
|
+ def _rich_rule_audit(self, policy, rich_rule, enable, table, expr_fragments):
|
|
if not rich_rule.audit:
|
|
return {}
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
|
|
chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule)
|
|
rule = {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s_%s" % (table, target, chain_suffix),
|
|
+ "chain": "%s_%s_%s" % (table, _policy, chain_suffix),
|
|
"expr": expr_fragments +
|
|
[{"log": {"level": "audit"}},
|
|
self._rich_rule_limit_fragment(rich_rule.audit.limit)]}
|
|
rule.update(self._rich_rule_priority_fragment(rich_rule))
|
|
return {add_del: {"rule": rule}}
|
|
|
|
- def _rich_rule_action(self, zone, rich_rule, enable, table, target, expr_fragments):
|
|
+ def _rich_rule_action(self, policy, rich_rule, enable, table, expr_fragments):
|
|
if not rich_rule.action:
|
|
return {}
|
|
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
|
|
chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
- chain = "%s_%s_%s" % (table, target, chain_suffix)
|
|
+ chain = "%s_%s_%s" % (table, _policy, chain_suffix)
|
|
if type(rich_rule.action) == Rich_Accept:
|
|
rule_action = {"accept": None}
|
|
elif type(rich_rule.action) == Rich_Reject:
|
|
@@ -1038,10 +1035,9 @@ class nftables(object):
|
|
elif type(rich_rule.action) == Rich_Drop:
|
|
rule_action = {"drop": None}
|
|
elif type(rich_rule.action) == Rich_Mark:
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
|
|
- zone=zone)
|
|
table = "mangle"
|
|
- chain = "%s_%s_%s" % (table, target, chain_suffix)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
+ chain = "%s_%s_%s" % (table, _policy, chain_suffix)
|
|
rule_action = {"mangle": {"key": {"meta": {"key": "mark"}},
|
|
"value": rich_rule.action.set}}
|
|
else:
|
|
@@ -1121,10 +1117,10 @@ class nftables(object):
|
|
else:
|
|
return {"range": [range[0], range[1]]}
|
|
|
|
- def build_zone_ports_rules(self, enable, zone, proto, port, destination=None, rich_rule=None):
|
|
+ def build_policy_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None):
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
expr_fragments = []
|
|
if rich_rule:
|
|
@@ -1146,21 +1142,21 @@ class nftables(object):
|
|
|
|
rules = []
|
|
if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments))
|
|
else:
|
|
rules.append({add_del: {"rule": {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s_allow" % (table, target),
|
|
+ "chain": "%s_%s_allow" % (table, _policy),
|
|
"expr": expr_fragments + [{"accept": None}]}}})
|
|
|
|
return rules
|
|
|
|
- def build_zone_protocol_rules(self, enable, zone, protocol, destination=None, rich_rule=None):
|
|
+ def build_policy_protocol_rules(self, enable, policy, protocol, destination=None, rich_rule=None):
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
expr_fragments = []
|
|
if rich_rule:
|
|
@@ -1181,22 +1177,22 @@ class nftables(object):
|
|
|
|
rules = []
|
|
if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments))
|
|
else:
|
|
rules.append({add_del: {"rule": {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s_allow" % (table, target),
|
|
+ "chain": "%s_%s_allow" % (table, _policy),
|
|
"expr": expr_fragments + [{"accept": None}]}}})
|
|
|
|
return rules
|
|
|
|
- def build_zone_source_ports_rules(self, enable, zone, proto, port,
|
|
+ def build_policy_source_ports_rules(self, enable, policy, proto, port,
|
|
destination=None, rich_rule=None):
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone)
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
|
|
expr_fragments = []
|
|
if rich_rule:
|
|
@@ -1218,19 +1214,21 @@ class nftables(object):
|
|
|
|
rules = []
|
|
if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments))
|
|
else:
|
|
rules.append({add_del: {"rule": {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s_allow" % (table, target),
|
|
+ "chain": "%s_%s_allow" % (table, _policy),
|
|
"expr": expr_fragments + [{"accept": None}]}}})
|
|
|
|
return rules
|
|
|
|
- def build_zone_helper_ports_rules(self, enable, zone, proto, port,
|
|
- destination, helper_name, module_short_name):
|
|
+ def build_policy_helper_ports_rules(self, enable, policy, proto, port,
|
|
+ destination, helper_name, module_short_name):
|
|
+ table = "filter"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
rules = []
|
|
|
|
@@ -1241,8 +1239,6 @@ class nftables(object):
|
|
"type": module_short_name,
|
|
"protocol": proto}}})
|
|
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"],
|
|
- zone=zone)
|
|
expr_fragments = []
|
|
if destination:
|
|
expr_fragments.append(self._rule_addr_fragment("daddr", destination))
|
|
@@ -1253,15 +1249,15 @@ class nftables(object):
|
|
expr_fragments.append({"ct helper": "helper-%s-%s" % (helper_name, proto)})
|
|
rules.append({add_del: {"rule": {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "filter_%s_allow" % (target),
|
|
+ "chain": "filter_%s_allow" % (_policy),
|
|
"expr": expr_fragments}}})
|
|
|
|
return rules
|
|
|
|
- def _build_zone_masquerade_nat_rules(self, enable, zone, family, rich_rule=None):
|
|
+ def _build_policy_masquerade_nat_rules(self, enable, policy, family, rich_rule=None):
|
|
+ table = "nat"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["POSTROUTING"],
|
|
- zone=zone)
|
|
|
|
expr_fragments = []
|
|
if rich_rule:
|
|
@@ -1273,7 +1269,7 @@ class nftables(object):
|
|
|
|
rule = {"family": family,
|
|
"table": TABLE_NAME,
|
|
- "chain": "nat_%s_%s" % (target, chain_suffix),
|
|
+ "chain": "nat_%s_%s" % (_policy, chain_suffix),
|
|
"expr": expr_fragments +
|
|
[{"match": {"left": {"meta": {"key": "oifname"}},
|
|
"op": "!=",
|
|
@@ -1282,21 +1278,21 @@ class nftables(object):
|
|
rule.update(self._rich_rule_priority_fragment(rich_rule))
|
|
return [{add_del: {"rule": rule}}]
|
|
|
|
- def build_zone_masquerade_rules(self, enable, zone, rich_rule=None):
|
|
+ def build_policy_masquerade_rules(self, enable, policy, rich_rule=None):
|
|
# nat tables needs to use ip/ip6 family
|
|
rules = []
|
|
if rich_rule and (rich_rule.family and rich_rule.family == "ipv6"
|
|
or rich_rule.source and check_address("ipv6", rich_rule.source.addr)):
|
|
- rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip6", rich_rule))
|
|
+ rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip6", rich_rule))
|
|
elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4"
|
|
or rich_rule.source and check_address("ipv4", rich_rule.source.addr)):
|
|
- rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip", rich_rule))
|
|
+ rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip", rich_rule))
|
|
else:
|
|
- rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip", rich_rule))
|
|
+ rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip", rich_rule))
|
|
|
|
+ table = "filter"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["FORWARD_OUT"],
|
|
- zone=zone)
|
|
|
|
expr_fragments = []
|
|
if rich_rule:
|
|
@@ -1308,7 +1304,7 @@ class nftables(object):
|
|
|
|
rule = {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "filter_%s_%s" % (target, chain_suffix),
|
|
+ "chain": "filter_%s_%s" % (_policy, chain_suffix),
|
|
"expr": expr_fragments +
|
|
[{"match": {"left": {"ct": {"key": "state"}},
|
|
"op": "in",
|
|
@@ -1319,12 +1315,12 @@ class nftables(object):
|
|
|
|
return rules
|
|
|
|
- def _build_zone_forward_port_nat_rules(self, enable, zone, port, protocol,
|
|
+ def _build_policy_forward_port_nat_rules(self, enable, policy, port, protocol,
|
|
toaddr, toport, family,
|
|
rich_rule=None):
|
|
+ table = "nat"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
|
|
- zone=zone)
|
|
|
|
expr_fragments = []
|
|
if rich_rule:
|
|
@@ -1351,28 +1347,28 @@ class nftables(object):
|
|
|
|
rule = {"family": family,
|
|
"table": TABLE_NAME,
|
|
- "chain": "nat_%s_%s" % (target, chain_suffix),
|
|
+ "chain": "nat_%s_%s" % (_policy, chain_suffix),
|
|
"expr": expr_fragments}
|
|
rule.update(self._rich_rule_priority_fragment(rich_rule))
|
|
return [{add_del: {"rule": rule}}]
|
|
|
|
- def build_zone_forward_port_rules(self, enable, zone, port,
|
|
+ def build_policy_forward_port_rules(self, enable, policy, port,
|
|
protocol, toport, toaddr, rich_rule=None):
|
|
rules = []
|
|
if rich_rule and (rich_rule.family and rich_rule.family == "ipv6"
|
|
or toaddr and check_single_address("ipv6", toaddr)):
|
|
- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy,
|
|
port, protocol, toaddr, toport, "ip6", rich_rule))
|
|
elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4"
|
|
or toaddr and check_single_address("ipv4", toaddr)):
|
|
- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy,
|
|
port, protocol, toaddr, toport, "ip", rich_rule))
|
|
else:
|
|
if toaddr and check_single_address("ipv6", toaddr):
|
|
- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy,
|
|
port, protocol, toaddr, toport, "ip6", rich_rule))
|
|
else:
|
|
- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy,
|
|
port, protocol, toaddr, toport, "ip", rich_rule))
|
|
|
|
return rules
|
|
@@ -1384,8 +1380,9 @@ class nftables(object):
|
|
raise FirewallError(INVALID_ICMPTYPE,
|
|
"ICMP type '%s' not supported by %s" % (icmp_type, self.name))
|
|
|
|
- def build_zone_icmp_block_rules(self, enable, zone, ict, rich_rule=None):
|
|
+ def build_policy_icmp_block_rules(self, enable, policy, ict, rich_rule=None):
|
|
table = "filter"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
|
|
if rich_rule and rich_rule.ipvs:
|
|
@@ -1401,83 +1398,77 @@ class nftables(object):
|
|
|
|
rules = []
|
|
for ipv in ipvs:
|
|
- for chain in ["INPUT", "FORWARD_IN"]:
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain],
|
|
- zone=zone)
|
|
- if self._fw.zone.query_icmp_block_inversion(zone):
|
|
- final_chain = "%s_%s_allow" % (table, target)
|
|
- target_fragment = {"accept": None}
|
|
- else:
|
|
- final_chain = "%s_%s_deny" % (table, target)
|
|
- target_fragment = self._reject_fragment()
|
|
-
|
|
- expr_fragments = []
|
|
- if rich_rule:
|
|
- expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family))
|
|
- expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination))
|
|
- expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source))
|
|
- expr_fragments.extend(self._icmp_types_to_nft_fragments(ipv, ict.name))
|
|
-
|
|
- if rich_rule:
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments))
|
|
- if rich_rule.action:
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments))
|
|
- else:
|
|
- chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
- rule = {"family": "inet",
|
|
- "table": TABLE_NAME,
|
|
- "chain": "%s_%s_%s" % (table, target, chain_suffix),
|
|
- "expr": expr_fragments + [self._reject_fragment()]}
|
|
- rule.update(self._rich_rule_priority_fragment(rich_rule))
|
|
- rules.append({add_del: {"rule": rule}})
|
|
+ if self._fw.policy.query_icmp_block_inversion(policy):
|
|
+ final_chain = "%s_%s_allow" % (table, _policy)
|
|
+ target_fragment = {"accept": None}
|
|
+ else:
|
|
+ final_chain = "%s_%s_deny" % (table, _policy)
|
|
+ target_fragment = self._reject_fragment()
|
|
+
|
|
+ expr_fragments = []
|
|
+ if rich_rule:
|
|
+ expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family))
|
|
+ expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination))
|
|
+ expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source))
|
|
+ expr_fragments.extend(self._icmp_types_to_nft_fragments(ipv, ict.name))
|
|
+
|
|
+ if rich_rule:
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments))
|
|
+ if rich_rule.action:
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments))
|
|
else:
|
|
- if self._fw.get_log_denied() != "off" and self._fw.zone.query_icmp_block_inversion(zone):
|
|
- rules.append({add_del: {"rule": {"family": "inet",
|
|
- "table": TABLE_NAME,
|
|
- "chain": final_chain,
|
|
- "expr": (expr_fragments +
|
|
- [self._pkttype_match_fragment(self._fw.get_log_denied()),
|
|
- {"log": {"prefix": "\"%s_%s_ICMP_BLOCK: \"" % (table, zone)}}])}}})
|
|
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
+ rule = {"family": "inet",
|
|
+ "table": TABLE_NAME,
|
|
+ "chain": "%s_%s_%s" % (table, _policy, chain_suffix),
|
|
+ "expr": expr_fragments + [self._reject_fragment()]}
|
|
+ rule.update(self._rich_rule_priority_fragment(rich_rule))
|
|
+ rules.append({add_del: {"rule": rule}})
|
|
+ else:
|
|
+ if self._fw.get_log_denied() != "off" and self._fw.policy.query_icmp_block_inversion(policy):
|
|
rules.append({add_del: {"rule": {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
"chain": final_chain,
|
|
- "expr": expr_fragments + [target_fragment]}}})
|
|
+ "expr": (expr_fragments +
|
|
+ [self._pkttype_match_fragment(self._fw.get_log_denied()),
|
|
+ {"log": {"prefix": "\"%s_%s_ICMP_BLOCK: \"" % (table, policy)}}])}}})
|
|
+ rules.append({add_del: {"rule": {"family": "inet",
|
|
+ "table": TABLE_NAME,
|
|
+ "chain": final_chain,
|
|
+ "expr": expr_fragments + [target_fragment]}}})
|
|
|
|
return rules
|
|
|
|
- def build_zone_icmp_block_inversion_rules(self, enable, zone):
|
|
+ def build_policy_icmp_block_inversion_rules(self, enable, policy):
|
|
table = "filter"
|
|
+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX)
|
|
rules = []
|
|
add_del = { True: "add", False: "delete" }[enable]
|
|
|
|
- for chain in ["INPUT", "FORWARD_IN"]:
|
|
- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain],
|
|
- zone=zone)
|
|
+ if self._fw.policy.query_icmp_block_inversion(policy):
|
|
+ target_fragment = self._reject_fragment()
|
|
+ else:
|
|
+ target_fragment = {"accept": None}
|
|
|
|
- if self._fw.zone.query_icmp_block_inversion(zone):
|
|
- target_fragment = self._reject_fragment()
|
|
- else:
|
|
- target_fragment = {"accept": None}
|
|
+ # WARN: The "index" used here must be kept in sync with
|
|
+ # build_policy_chain_rules()
|
|
+ #
|
|
+ rules.append({add_del: {"rule": {"family": "inet",
|
|
+ "table": TABLE_NAME,
|
|
+ "chain": "%s_%s" % (table, _policy),
|
|
+ "index": 4,
|
|
+ "expr": [self._icmp_match_fragment(),
|
|
+ target_fragment]}}})
|
|
|
|
- # WARN: The "index" used here must be kept in sync with
|
|
- # build_zone_chain_rules()
|
|
- #
|
|
+ if self._fw.get_log_denied() != "off" and self._fw.policy.query_icmp_block_inversion(policy):
|
|
rules.append({add_del: {"rule": {"family": "inet",
|
|
"table": TABLE_NAME,
|
|
- "chain": "%s_%s" % (table, _zone),
|
|
+ "chain": "%s_%s" % (table, _policy),
|
|
"index": 4,
|
|
"expr": [self._icmp_match_fragment(),
|
|
- target_fragment]}}})
|
|
-
|
|
- if self._fw.get_log_denied() != "off" and self._fw.zone.query_icmp_block_inversion(zone):
|
|
- rules.append({add_del: {"rule": {"family": "inet",
|
|
- "table": TABLE_NAME,
|
|
- "chain": "%s_%s" % (table, _zone),
|
|
- "index": 4,
|
|
- "expr": [self._icmp_match_fragment(),
|
|
- self._pkttype_match_fragment(self._fw.get_log_denied()),
|
|
- {"log": {"prefix": "%s_%s_ICMP_BLOCK: " % (table, _zone)}}]}}})
|
|
+ self._pkttype_match_fragment(self._fw.get_log_denied()),
|
|
+ {"log": {"prefix": "%s_%s_ICMP_BLOCK: " % (table, policy)}}]}}})
|
|
return rules
|
|
|
|
def build_rpfilter_rules(self, log_denied=False):
|
|
@@ -1543,10 +1534,8 @@ class nftables(object):
|
|
"expr": expr_fragments}}})
|
|
return rules
|
|
|
|
- def build_zone_rich_source_destination_rules(self, enable, zone, rich_rule):
|
|
+ def build_policy_rich_source_destination_rules(self, enable, policy, rich_rule):
|
|
table = "filter"
|
|
- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"],
|
|
- zone=zone)
|
|
|
|
expr_fragments = []
|
|
expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family))
|
|
@@ -1554,9 +1543,9 @@ class nftables(object):
|
|
expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source))
|
|
|
|
rules = []
|
|
- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments))
|
|
- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments))
|
|
+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments))
|
|
+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments))
|
|
|
|
return rules
|
|
|
|
diff --git a/src/firewall/errors.py b/src/firewall/errors.py
|
|
index 4589f60..2ad46c2 100644
|
|
--- a/src/firewall/errors.py
|
|
+++ b/src/firewall/errors.py
|
|
@@ -88,6 +88,7 @@ INVALID_ENTRY = 136
|
|
INVALID_OPTION = 137
|
|
INVALID_HELPER = 138
|
|
INVALID_PRIORITY = 139
|
|
+INVALID_POLICY = 140
|
|
|
|
MISSING_TABLE = 200
|
|
MISSING_CHAIN = 201
|
|
diff --git a/src/firewall/functions.py b/src/firewall/functions.py
|
|
index 6bc52d9..d4c5e90 100644
|
|
--- a/src/firewall/functions.py
|
|
+++ b/src/firewall/functions.py
|
|
@@ -27,7 +27,7 @@ __all__ = [ "PY2", "getPortID", "getPortRange", "portStr", "getServiceName",
|
|
"check_single_address", "check_mac", "uniqify", "ppid_of_pid",
|
|
"max_zone_name_len", "checkUser", "checkUid", "checkCommand",
|
|
"checkContext", "joinArgs", "splitArgs",
|
|
- "b2u", "u2b", "u2b_if_py2" ]
|
|
+ "b2u", "u2b", "u2b_if_py2", "max_policy_name_len"]
|
|
|
|
import socket
|
|
import os
|
|
@@ -505,6 +505,15 @@ def ppid_of_pid(pid):
|
|
return None
|
|
return pid
|
|
|
|
+def max_policy_name_len():
|
|
+ """
|
|
+ iptables limits length of chain to (currently) 28 chars.
|
|
+ The longest chain we create is pol_<policy>_allow,
|
|
+ which leaves 28 - 10 = 18 chars for <policy>.
|
|
+ """
|
|
+ from firewall.core.ipXtables import POLICY_CHAIN_PREFIX
|
|
+ return 28 - (len(POLICY_CHAIN_PREFIX) + len("_allow"))
|
|
+
|
|
def max_zone_name_len():
|
|
"""
|
|
Netfilter limits length of chain to (currently) 28 chars.
|
|
diff --git a/src/tests/features/service_include.at b/src/tests/features/service_include.at
|
|
index 7f02701..1c86900 100644
|
|
--- a/src/tests/features/service_include.at
|
|
+++ b/src/tests/features/service_include.at
|
|
@@ -116,9 +116,7 @@ FWD_CHECK([-q --permanent --zone=drop --add-interface=foobar0])
|
|
FWD_CHECK([-q --permanent --zone=drop --add-service=my-service-with-include])
|
|
FWD_CHECK([-q --permanent --service=my-service-with-include --add-include=does-not-exist])
|
|
FWD_RELOAD(101, [ignore], [ignore], 251)
|
|
-FWD_CHECK([--zone=drop --list-services], 0, [dnl
|
|
|
|
-])
|
|
FWD_CHECK([--zone=public --list-services], 0, [dnl
|
|
dhcpv6-client ssh
|
|
])
|
|
--
|
|
1.8.3.1
|
|
|