c8bceb
From 256b70dfaaa027a961205b08cd671f1fccf9b663 Mon Sep 17 00:00:00 2001
c8bceb
From: Eric Garver <eric@garver.life>
c8bceb
Date: Thu, 10 Jan 2019 13:02:34 -0500
c8bceb
Subject: [PATCH 18/23] ipXtables: Avoid inserting rules with index
c8bceb
c8bceb
iptables-restore (nftables) has a bug in which inserting by index
c8bceb
doesn't always work as expected. Rules may be inserted at the wrong
c8bceb
index. We can mostly avoid this by appending rules. This actually
c8bceb
simplifies things because we don't have to count indexes. Ref rhbz
c8bceb
1647925.
c8bceb
c8bceb
(cherry picked from commit 9f0eb929c240211d3da978732a9f6930da71486c)
c8bceb
---
c8bceb
 src/firewall/core/fw_direct.py |   5 +-
c8bceb
 src/firewall/core/ipXtables.py | 256 ++++++++++++++++-----------------
c8bceb
 2 files changed, 132 insertions(+), 129 deletions(-)
c8bceb
c8bceb
diff --git a/src/firewall/core/fw_direct.py b/src/firewall/core/fw_direct.py
c8bceb
index 94196e8a41fa..e53a72e3326a 100644
c8bceb
--- a/src/firewall/core/fw_direct.py
c8bceb
+++ b/src/firewall/core/fw_direct.py
c8bceb
@@ -181,7 +181,10 @@ class FirewallDirect(object):
c8bceb
     def _check_builtin_chain(self, ipv, table, chain):
c8bceb
         if ipv in ['ipv4', 'ipv6']:
c8bceb
             built_in_chains = ipXtables.BUILT_IN_CHAINS[table]
c8bceb
-            our_chains = ipXtables.OUR_CHAINS[table]
c8bceb
+            if self._fw.nftables_enabled:
c8bceb
+                our_chains = {}
c8bceb
+            else:
c8bceb
+                our_chains = self._fw.get_direct_backend_by_ipv(ipv).our_chains[table]
c8bceb
         else:
c8bceb
             built_in_chains = ebtables.BUILT_IN_CHAINS[table]
c8bceb
             our_chains = ebtables.OUR_CHAINS[table]
c8bceb
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
c8bceb
index 2bd8cc20dc7b..4f04ac41f6a0 100644
c8bceb
--- a/src/firewall/core/ipXtables.py
c8bceb
+++ b/src/firewall/core/ipXtables.py
c8bceb
@@ -49,106 +49,6 @@ ICMP = {
c8bceb
     "ipv6": "ipv6-icmp",
c8bceb
 }
c8bceb
 
c8bceb
-DEFAULT_RULES = { }
c8bceb
-LOG_RULES = { }
c8bceb
-OUR_CHAINS = {} # chains created by firewalld
c8bceb
-
c8bceb
-DEFAULT_RULES["security"] = [ ]
c8bceb
-OUR_CHAINS["security"] = set()
c8bceb
-for chain in BUILT_IN_CHAINS["security"]:
c8bceb
-    DEFAULT_RULES["security"].append("-N %s_direct" % chain)
c8bceb
-    DEFAULT_RULES["security"].append("-I %s 1 -j %s_direct" % (chain, chain))
c8bceb
-    OUR_CHAINS["security"].add("%s_direct" % chain)
c8bceb
-
c8bceb
-DEFAULT_RULES["raw"] = [ ]
c8bceb
-OUR_CHAINS["raw"] = set()
c8bceb
-for chain in BUILT_IN_CHAINS["raw"]:
c8bceb
-    DEFAULT_RULES["raw"].append("-N %s_direct" % chain)
c8bceb
-    DEFAULT_RULES["raw"].append("-I %s 1 -j %s_direct" % (chain, chain))
c8bceb
-    OUR_CHAINS["raw"].add("%s_direct" % chain)
c8bceb
-
c8bceb
-    if chain == "PREROUTING":
c8bceb
-        DEFAULT_RULES["raw"].append("-N %s_ZONES_SOURCE" % chain)
c8bceb
-        DEFAULT_RULES["raw"].append("-N %s_ZONES" % chain)
c8bceb
-        DEFAULT_RULES["raw"].append("-I %s 2 -j %s_ZONES_SOURCE" % (chain, chain))
c8bceb
-        DEFAULT_RULES["raw"].append("-I %s 3 -j %s_ZONES" % (chain, chain))
c8bceb
-        OUR_CHAINS["raw"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain]))
c8bceb
-
c8bceb
-DEFAULT_RULES["mangle"] = [ ]
c8bceb
-OUR_CHAINS["mangle"] = set()
c8bceb
-for chain in BUILT_IN_CHAINS["mangle"]:
c8bceb
-    DEFAULT_RULES["mangle"].append("-N %s_direct" % chain)
c8bceb
-    DEFAULT_RULES["mangle"].append("-I %s 1 -j %s_direct" % (chain, chain))
c8bceb
-    OUR_CHAINS["mangle"].add("%s_direct" % chain)
c8bceb
-
c8bceb
-    if chain == "PREROUTING":
c8bceb
-        DEFAULT_RULES["mangle"].append("-N %s_ZONES_SOURCE" % chain)
c8bceb
-        DEFAULT_RULES["mangle"].append("-N %s_ZONES" % chain)
c8bceb
-        DEFAULT_RULES["mangle"].append("-I %s 2 -j %s_ZONES_SOURCE" % (chain, chain))
c8bceb
-        DEFAULT_RULES["mangle"].append("-I %s 3 -j %s_ZONES" % (chain, chain))
c8bceb
-        OUR_CHAINS["mangle"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain]))
c8bceb
-
c8bceb
-DEFAULT_RULES["nat"] = [ ]
c8bceb
-OUR_CHAINS["nat"] = set()
c8bceb
-for chain in BUILT_IN_CHAINS["nat"]:
c8bceb
-    DEFAULT_RULES["nat"].append("-N %s_direct" % chain)
c8bceb
-    DEFAULT_RULES["nat"].append("-I %s 1 -j %s_direct" % (chain, chain))
c8bceb
-    OUR_CHAINS["nat"].add("%s_direct" % chain)
c8bceb
-
c8bceb
-    if chain in [ "PREROUTING", "POSTROUTING" ]:
c8bceb
-        DEFAULT_RULES["nat"].append("-N %s_ZONES_SOURCE" % chain)
c8bceb
-        DEFAULT_RULES["nat"].append("-N %s_ZONES" % chain)
c8bceb
-        DEFAULT_RULES["nat"].append("-I %s 2 -j %s_ZONES_SOURCE" % (chain, chain))
c8bceb
-        DEFAULT_RULES["nat"].append("-I %s 3 -j %s_ZONES" % (chain, chain))
c8bceb
-        OUR_CHAINS["nat"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain]))
c8bceb
-
c8bceb
-DEFAULT_RULES["filter"] = [
c8bceb
-    "-N INPUT_direct",
c8bceb
-    "-N INPUT_ZONES_SOURCE",
c8bceb
-    "-N INPUT_ZONES",
c8bceb
-
c8bceb
-    "-I INPUT 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT",
c8bceb
-    "-I INPUT 2 -i lo -j ACCEPT",
c8bceb
-    "-I INPUT 3 -j INPUT_direct",
c8bceb
-    "-I INPUT 4 -j INPUT_ZONES_SOURCE",
c8bceb
-    "-I INPUT 5 -j INPUT_ZONES",
c8bceb
-    "-I INPUT 6 -m conntrack --ctstate INVALID -j DROP",
c8bceb
-    "-I INPUT 7 -j %%REJECT%%",
c8bceb
-
c8bceb
-    "-N FORWARD_direct",
c8bceb
-    "-N FORWARD_IN_ZONES_SOURCE",
c8bceb
-    "-N FORWARD_IN_ZONES",
c8bceb
-    "-N FORWARD_OUT_ZONES_SOURCE",
c8bceb
-    "-N FORWARD_OUT_ZONES",
c8bceb
-
c8bceb
-    "-I FORWARD 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT",
c8bceb
-    "-I FORWARD 2 -i lo -j ACCEPT",
c8bceb
-    "-I FORWARD 3 -j FORWARD_direct",
c8bceb
-    "-I FORWARD 4 -j FORWARD_IN_ZONES_SOURCE",
c8bceb
-    "-I FORWARD 5 -j FORWARD_IN_ZONES",
c8bceb
-    "-I FORWARD 6 -j FORWARD_OUT_ZONES_SOURCE",
c8bceb
-    "-I FORWARD 7 -j FORWARD_OUT_ZONES",
c8bceb
-    "-I FORWARD 8 -m conntrack --ctstate INVALID -j DROP",
c8bceb
-    "-I FORWARD 9 -j %%REJECT%%",
c8bceb
-
c8bceb
-    "-N OUTPUT_direct",
c8bceb
-
c8bceb
-    "-I OUTPUT 1 -j OUTPUT_direct",
c8bceb
-]
c8bceb
-
c8bceb
-LOG_RULES["filter"] = [
c8bceb
-    "-I INPUT 6 -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '",
c8bceb
-    "-I INPUT 8 %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: '",
c8bceb
-
c8bceb
-    "-I FORWARD 8 -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '",
c8bceb
-    "-I FORWARD 10 %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: '",
c8bceb
-]
c8bceb
-
c8bceb
-OUR_CHAINS["filter"] = set(["INPUT_direct", "INPUT_ZONES_SOURCE", "INPUT_ZONES",
c8bceb
-                            "FORWARD_direct", "FORWARD_IN_ZONES_SOURCE",
c8bceb
-                            "FORWARD_IN_ZONES", "FORWARD_OUT_ZONES_SOURCE",
c8bceb
-                            "FORWARD_OUT_ZONES", "OUTPUT_direct"])
c8bceb
-
c8bceb
 # ipv ebtables also uses this
c8bceb
 #
c8bceb
 def common_reverse_rule(args):
c8bceb
@@ -275,6 +175,7 @@ class ip4tables(object):
c8bceb
         self.restore_wait_option = self._detect_restore_wait_option()
c8bceb
         self.fill_exists()
c8bceb
         self.available_tables = []
c8bceb
+        self.our_chains = {} # chains created by firewalld
c8bceb
 
c8bceb
     def fill_exists(self):
c8bceb
         self.command_exists = os.path.exists(self._command)
c8bceb
@@ -602,20 +503,117 @@ class ip4tables(object):
c8bceb
         return []
c8bceb
 
c8bceb
     def build_default_rules(self, log_denied="off"):
c8bceb
-        default_rules = []
c8bceb
-        for table in DEFAULT_RULES:
c8bceb
+        default_rules = {}
c8bceb
+
c8bceb
+        default_rules["security"] = [ ]
c8bceb
+        self.our_chains["security"] = set()
c8bceb
+        for chain in BUILT_IN_CHAINS["security"]:
c8bceb
+            default_rules["security"].append("-N %s_direct" % chain)
c8bceb
+            default_rules["security"].append("-A %s -j %s_direct" % (chain, chain))
c8bceb
+            self.our_chains["security"].add("%s_direct" % chain)
c8bceb
+
c8bceb
+        default_rules["raw"] = [ ]
c8bceb
+        self.our_chains["raw"] = set()
c8bceb
+        for chain in BUILT_IN_CHAINS["raw"]:
c8bceb
+            default_rules["raw"].append("-N %s_direct" % chain)
c8bceb
+            default_rules["raw"].append("-A %s -j %s_direct" % (chain, chain))
c8bceb
+            self.our_chains["raw"].add("%s_direct" % chain)
c8bceb
+
c8bceb
+            if chain == "PREROUTING":
c8bceb
+                default_rules["raw"].append("-N %s_ZONES_SOURCE" % chain)
c8bceb
+                default_rules["raw"].append("-N %s_ZONES" % chain)
c8bceb
+                default_rules["raw"].append("-A %s -j %s_ZONES_SOURCE" % (chain, chain))
c8bceb
+                default_rules["raw"].append("-A %s -j %s_ZONES" % (chain, chain))
c8bceb
+                self.our_chains["raw"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain]))
c8bceb
+
c8bceb
+        default_rules["mangle"] = [ ]
c8bceb
+        self.our_chains["mangle"] = set()
c8bceb
+        for chain in BUILT_IN_CHAINS["mangle"]:
c8bceb
+            default_rules["mangle"].append("-N %s_direct" % chain)
c8bceb
+            default_rules["mangle"].append("-A %s -j %s_direct" % (chain, chain))
c8bceb
+            self.our_chains["mangle"].add("%s_direct" % chain)
c8bceb
+
c8bceb
+            if chain == "PREROUTING":
c8bceb
+                default_rules["mangle"].append("-N %s_ZONES_SOURCE" % chain)
c8bceb
+                default_rules["mangle"].append("-N %s_ZONES" % chain)
c8bceb
+                default_rules["mangle"].append("-A %s -j %s_ZONES_SOURCE" % (chain, chain))
c8bceb
+                default_rules["mangle"].append("-A %s -j %s_ZONES" % (chain, chain))
c8bceb
+                self.our_chains["mangle"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain]))
c8bceb
+
c8bceb
+        default_rules["nat"] = [ ]
c8bceb
+        self.our_chains["nat"] = set()
c8bceb
+        for chain in BUILT_IN_CHAINS["nat"]:
c8bceb
+            default_rules["nat"].append("-N %s_direct" % chain)
c8bceb
+            default_rules["nat"].append("-A %s -j %s_direct" % (chain, chain))
c8bceb
+            self.our_chains["nat"].add("%s_direct" % chain)
c8bceb
+
c8bceb
+            if chain in [ "PREROUTING", "POSTROUTING" ]:
c8bceb
+                default_rules["nat"].append("-N %s_ZONES_SOURCE" % chain)
c8bceb
+                default_rules["nat"].append("-N %s_ZONES" % chain)
c8bceb
+                default_rules["nat"].append("-A %s -j %s_ZONES_SOURCE" % (chain, chain))
c8bceb
+                default_rules["nat"].append("-A %s -j %s_ZONES" % (chain, chain))
c8bceb
+                self.our_chains["nat"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain]))
c8bceb
+
c8bceb
+        default_rules["filter"] = [
c8bceb
+            "-N INPUT_direct",
c8bceb
+            "-N INPUT_ZONES_SOURCE",
c8bceb
+            "-N INPUT_ZONES",
c8bceb
+
c8bceb
+            "-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT",
c8bceb
+            "-A INPUT -i lo -j ACCEPT",
c8bceb
+            "-A INPUT -j INPUT_direct",
c8bceb
+            "-A INPUT -j INPUT_ZONES_SOURCE",
c8bceb
+            "-A INPUT -j INPUT_ZONES",
c8bceb
+        ]
c8bceb
+        if log_denied != "off":
c8bceb
+            default_rules["filter"].append("-A INPUT -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '")
c8bceb
+        default_rules["filter"].append("-A INPUT -m conntrack --ctstate INVALID -j DROP")
c8bceb
+        if log_denied != "off":
c8bceb
+            default_rules["filter"].append("-A INPUT %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: '")
c8bceb
+        default_rules["filter"].append("-A INPUT -j %%REJECT%%")
c8bceb
+
c8bceb
+        default_rules["filter"] += [
c8bceb
+            "-N FORWARD_direct",
c8bceb
+            "-N FORWARD_IN_ZONES_SOURCE",
c8bceb
+            "-N FORWARD_IN_ZONES",
c8bceb
+            "-N FORWARD_OUT_ZONES_SOURCE",
c8bceb
+            "-N FORWARD_OUT_ZONES",
c8bceb
+
c8bceb
+            "-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT",
c8bceb
+            "-A FORWARD -i lo -j ACCEPT",
c8bceb
+            "-A FORWARD -j FORWARD_direct",
c8bceb
+            "-A FORWARD -j FORWARD_IN_ZONES_SOURCE",
c8bceb
+            "-A FORWARD -j FORWARD_IN_ZONES",
c8bceb
+            "-A FORWARD -j FORWARD_OUT_ZONES_SOURCE",
c8bceb
+            "-A FORWARD -j FORWARD_OUT_ZONES",
c8bceb
+        ]
c8bceb
+        if log_denied != "off":
c8bceb
+            default_rules["filter"].append("-A FORWARD -m conntrack --ctstate INVALID %%LOGTYPE%% -j LOG --log-prefix 'STATE_INVALID_DROP: '")
c8bceb
+        default_rules["filter"].append("-A FORWARD -m conntrack --ctstate INVALID -j DROP")
c8bceb
+        if log_denied != "off":
c8bceb
+            default_rules["filter"].append("-A FORWARD %%LOGTYPE%% -j LOG --log-prefix 'FINAL_REJECT: '")
c8bceb
+        default_rules["filter"].append("-A FORWARD -j %%REJECT%%")
c8bceb
+
c8bceb
+        default_rules["filter"] += [
c8bceb
+            "-N OUTPUT_direct",
c8bceb
+
c8bceb
+            "-A OUTPUT -o lo -j ACCEPT",
c8bceb
+            "-A OUTPUT -j OUTPUT_direct",
c8bceb
+        ]
c8bceb
+
c8bceb
+        self.our_chains["filter"] = set(["INPUT_direct", "INPUT_ZONES_SOURCE", "INPUT_ZONES",
c8bceb
+                                    "FORWARD_direct", "FORWARD_IN_ZONES_SOURCE",
c8bceb
+                                    "FORWARD_IN_ZONES", "FORWARD_OUT_ZONES_SOURCE",
c8bceb
+                                    "FORWARD_OUT_ZONES", "OUTPUT_direct"])
c8bceb
+
c8bceb
+        final_default_rules = []
c8bceb
+        for table in default_rules:
c8bceb
             if table not in self.get_available_tables():
c8bceb
                 continue
c8bceb
-            _default_rules = DEFAULT_RULES[table][:]
c8bceb
-            if log_denied != "off" and table in LOG_RULES:
c8bceb
-                _default_rules.extend(LOG_RULES[table])
c8bceb
-            prefix = [ "-t", table ]
c8bceb
-            for rule in _default_rules:
c8bceb
-                if type(rule) == list:
c8bceb
-                    default_rules.append(prefix + rule)
c8bceb
-                else:
c8bceb
-                    default_rules.append(prefix + splitArgs(rule))
c8bceb
-        return default_rules
c8bceb
+            for rule in default_rules[table]:
c8bceb
+                final_default_rules.append(["-t", table] + splitArgs(rule))
c8bceb
+
c8bceb
+        return final_default_rules
c8bceb
 
c8bceb
     def get_zone_table_chains(self, table):
c8bceb
         if table == "filter":
c8bceb
@@ -709,7 +707,7 @@ class ip4tables(object):
c8bceb
     def build_zone_chain_rules(self, zone, table, chain):
c8bceb
         _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone)
c8bceb
 
c8bceb
-        OUR_CHAINS[table].update(set([_zone,
c8bceb
+        self.our_chains[table].update(set([_zone,
c8bceb
                                       "%s_log" % _zone,
c8bceb
                                       "%s_deny" % _zone,
c8bceb
                                       "%s_allow" % _zone]))
c8bceb
@@ -719,33 +717,35 @@ class ip4tables(object):
c8bceb
         rules.append([ "-N", "%s_log" % _zone, "-t", table ])
c8bceb
         rules.append([ "-N", "%s_deny" % _zone, "-t", table ])
c8bceb
         rules.append([ "-N", "%s_allow" % _zone, "-t", table ])
c8bceb
-        rules.append([ "-I", _zone, "1", "-t", table, "-j", "%s_log" % _zone ])
c8bceb
-        rules.append([ "-I", _zone, "2", "-t", table, "-j", "%s_deny" % _zone ])
c8bceb
-        rules.append([ "-I", _zone, "3", "-t", table, "-j", "%s_allow" % _zone ])
c8bceb
+        rules.append([ "-A", _zone, "-t", table, "-j", "%s_log" % _zone ])
c8bceb
+        rules.append([ "-A", _zone, "-t", table, "-j", "%s_deny" % _zone ])
c8bceb
+        rules.append([ "-A", _zone, "-t", table, "-j", "%s_allow" % _zone ])
c8bceb
 
c8bceb
-        # Handle trust, block and drop zones:
c8bceb
-        # Add an additional rule with the zone target (accept, reject
c8bceb
-        # or drop) to the base zone only in the filter table.
c8bceb
-        # Otherwise it is not be possible to have a zone with drop
c8bceb
-        # target, that is allowing traffic that is locally initiated
c8bceb
-        # or that adds additional rules. (RHBZ#1055190)
c8bceb
         target = self._fw.zone._zones[zone].target
c8bceb
-        if table == "filter" and \
c8bceb
-           target in [ "ACCEPT", "REJECT", "%%REJECT%%", "DROP" ] and \
c8bceb
-           chain in [ "INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT" ]:
c8bceb
-            rules.append([ "-I", _zone, "4", "-t", table, "-j", target ])
c8bceb
 
c8bceb
         if self._fw.get_log_denied() != "off":
c8bceb
             if table == "filter" and \
c8bceb
                chain in [ "INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT" ]:
c8bceb
                 if target in [ "REJECT", "%%REJECT%%" ]:
c8bceb
-                    rules.append([ "-I", _zone, "4", "-t", table, "%%LOGTYPE%%",
c8bceb
+                    rules.append([ "-A", _zone, "-t", table, "%%LOGTYPE%%",
c8bceb
                                    "-j", "LOG", "--log-prefix",
c8bceb
                                    "\"%s_REJECT: \"" % _zone ])
c8bceb
                 if target == "DROP":
c8bceb
-                    rules.append([ "-I", _zone, "4", "-t", table, "%%LOGTYPE%%",
c8bceb
+                    rules.append([ "-A", _zone, "-t", table, "%%LOGTYPE%%",
c8bceb
                                    "-j", "LOG", "--log-prefix",
c8bceb
                                    "\"%s_DROP: \"" % _zone ])
c8bceb
+
c8bceb
+        # Handle trust, block and drop zones:
c8bceb
+        # Add an additional rule with the zone target (accept, reject
c8bceb
+        # or drop) to the base zone only in the filter table.
c8bceb
+        # Otherwise it is not be possible to have a zone with drop
c8bceb
+        # target, that is allowing traffic that is locally initiated
c8bceb
+        # or that adds additional rules. (RHBZ#1055190)
c8bceb
+        if table == "filter" and \
c8bceb
+           target in [ "ACCEPT", "REJECT", "%%REJECT%%", "DROP" ] and \
c8bceb
+           chain in [ "INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT" ]:
c8bceb
+            rules.append([ "-A", _zone, "-t", table, "-j", target ])
c8bceb
+
c8bceb
         return rules
c8bceb
 
c8bceb
     def _rule_limit(self, limit):
c8bceb
-- 
c8bceb
2.20.1
c8bceb