feat(IPv6_rpfilter): support strict-forward rpfilter
feat(IPv6_rpfilter): support loose rpfilter feat(IPv6_rpfilter): support loose-forward rpfilter Resolves: RHEL-33330
This commit is contained in:
parent
9c0f94b6e4
commit
c6bfeff9a2
@ -0,0 +1,27 @@
|
||||
From 55e40954a8c596fabe03371e9a508d3518273ac1 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 14 May 2024 16:27:54 -0400
|
||||
Subject: [PATCH 11/22] v2.2.0: test(functions): add macro CHECK_NFTABLES_FIB
|
||||
|
||||
(cherry picked from commit 0aeebef07bc57b1f56b107632cdfdd809384398c)
|
||||
---
|
||||
src/tests/functions.at | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index f454ca980046..65a4ce078e05 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -748,3 +748,9 @@ m4_define([CHECK_NM_CAPABILITY_OVS], [
|
||||
m4_define([IF_BACKEND_IS_DEFAULT], [
|
||||
m4_if(nftables, FIREWALL_BACKEND, [$1], [])
|
||||
])
|
||||
+
|
||||
+m4_define([CHECK_NFTABLES_FIB], [
|
||||
+ m4_if(nftables, FIREWALL_BACKEND, [
|
||||
+ IF_HOST_SUPPORTS_NFT_FIB([], [AT_SKIP_IF([:])])
|
||||
+ ])
|
||||
+])
|
||||
--
|
||||
2.43.5
|
||||
|
@ -0,0 +1,31 @@
|
||||
From d368d579c78652a68273897d5f8b5099d251a9b5 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 14 May 2024 16:21:06 -0400
|
||||
Subject: [PATCH 12/22] v2.2.0: test(functions): add macro
|
||||
CHECK_NFTABLES_FIB_IN_FORWARD
|
||||
|
||||
(cherry picked from commit b9cf7b75c7d94efa98545a3b7ad5020b1896b22a)
|
||||
---
|
||||
src/tests/functions.at | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index 65a4ce078e05..b2372dd4075b 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -754,3 +754,12 @@ m4_define([CHECK_NFTABLES_FIB], [
|
||||
IF_HOST_SUPPORTS_NFT_FIB([], [AT_SKIP_IF([:])])
|
||||
])
|
||||
])
|
||||
+
|
||||
+m4_define([CHECK_NFTABLES_FIB_IN_FORWARD], [
|
||||
+ m4_if(nftables, FIREWALL_BACKEND, [
|
||||
+ NS_CHECK([nft add table inet firewalld_check])
|
||||
+ NS_CHECK([nft add chain inet firewalld_check foobar { type filter hook forward priority 0 \; }])
|
||||
+ AT_SKIP_IF([! NS_CMD([nft add rule inet firewalld_check foobar meta nfproto ipv6 fib saddr . mark . iif oif missing drop >/dev/null 2>&1])])
|
||||
+ NS_CHECK([nft delete table inet firewalld_check])
|
||||
+ ])
|
||||
+])
|
||||
--
|
||||
2.43.5
|
||||
|
51
0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
Normal file
51
0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
Normal file
@ -0,0 +1,51 @@
|
||||
From c1620d5ad4c151382373a138ab0c36dd7561a4bb Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 14 May 2024 16:29:50 -0400
|
||||
Subject: [PATCH 13/22] v2.2.0: test(rpfilter): use CHECK macros
|
||||
|
||||
(cherry picked from commit 352f3fc7fc00b675178de1eff8f0197607741de7)
|
||||
---
|
||||
src/tests/features/rpfilter.at | 27 +++++++++++----------------
|
||||
1 file changed, 11 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
|
||||
index 01fb81ea75ef..ccc8a6cf5e80 100644
|
||||
--- a/src/tests/features/rpfilter.at
|
||||
+++ b/src/tests/features/rpfilter.at
|
||||
@@ -1,22 +1,17 @@
|
||||
-FWD_START_TEST([rpfilter])
|
||||
+FWD_START_TEST([rpfilter - strict])
|
||||
AT_KEYWORDS(rpfilter)
|
||||
+CHECK_NFTABLES_FIB()
|
||||
|
||||
-IF_HOST_SUPPORTS_NFT_FIB([
|
||||
- NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
|
||||
- table inet firewalld {
|
||||
- chain filter_PREROUTING {
|
||||
- icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
|
||||
- meta nfproto ipv6 fib saddr . mark . iif oif missing drop
|
||||
- }
|
||||
- }
|
||||
- ])
|
||||
-], [
|
||||
- NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
|
||||
- table inet firewalld {
|
||||
- chain filter_PREROUTING {
|
||||
- }
|
||||
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=yes/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_PREROUTING {
|
||||
+ icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
|
||||
+ meta nfproto ipv6 fib saddr . mark . iif oif missing drop
|
||||
}
|
||||
- ])
|
||||
+ }
|
||||
])
|
||||
|
||||
IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
|
||||
--
|
||||
2.43.5
|
||||
|
41
0014-v2.2.0-test-IPv6_rpfilter-verify-valid-values.patch
Normal file
41
0014-v2.2.0-test-IPv6_rpfilter-verify-valid-values.patch
Normal file
@ -0,0 +1,41 @@
|
||||
From 0ba1eed533e4cd1dd77771ba7c16dc0edcea841e Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 13 May 2024 13:53:55 -0400
|
||||
Subject: [PATCH 14/22] v2.2.0: test(IPv6_rpfilter): verify valid values
|
||||
|
||||
Including the deprecated "yes" value.
|
||||
|
||||
(cherry picked from commit 1e91792157d36355669b4f02a82c1ee603a9467d)
|
||||
---
|
||||
src/tests/features/rpfilter.at | 18 +++++++++++++++++-
|
||||
1 file changed, 17 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
|
||||
index ccc8a6cf5e80..755d9dfd33cc 100644
|
||||
--- a/src/tests/features/rpfilter.at
|
||||
+++ b/src/tests/features/rpfilter.at
|
||||
@@ -22,4 +22,20 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
|
||||
PREROUTING_ZONES 0 -- ::/0 ::/0
|
||||
])
|
||||
|
||||
-FWD_END_TEST
|
||||
+FWD_END_TEST()
|
||||
+
|
||||
+FWD_START_TEST([rpfilter - config values])
|
||||
+AT_KEYWORDS(rpfilter)
|
||||
+CHECK_NFTABLES_FIB()
|
||||
+
|
||||
+dnl Verify other/deprecated configuration values are accepted.
|
||||
+dnl
|
||||
+m4_foreach([VALUE], [[no], [yes], [false], [true]], [
|
||||
+ AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=VALUE/' ./firewalld.conf])
|
||||
+ FWD_RELOAD()
|
||||
+])
|
||||
+dnl And a bogus one.
|
||||
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=bogus/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+FWD_END_TEST([-e "/^WARNING: IPv6_rpfilter 'bogus' is not valid/d"])
|
||||
--
|
||||
2.43.5
|
||||
|
336
0015-v2.2.0-chore-IPv6_rpfilter-prepare-for-new-config-va.patch
Normal file
336
0015-v2.2.0-chore-IPv6_rpfilter-prepare-for-new-config-va.patch
Normal file
@ -0,0 +1,336 @@
|
||||
From 7973ddf8d9f972f0292c8c865da9e0ebaefd77cb Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Thu, 16 May 2024 09:26:43 -0400
|
||||
Subject: [PATCH 15/22] v2.2.0: chore(IPv6_rpfilter): prepare for new config
|
||||
values
|
||||
|
||||
This is just prep work for supporting other configuration values. This
|
||||
commits supports using "strict" as a value which is synonymous with
|
||||
"yes" and is the current default behavior.
|
||||
|
||||
(cherry picked from commit cd959f21a5ceb41057b76f817b0456c281408ae0)
|
||||
---
|
||||
config/firewalld.conf | 15 ++++++++++-----
|
||||
doc/xml/firewalld.conf.xml | 12 +++++++++---
|
||||
doc/xml/firewalld.dbus.xml | 8 ++++++--
|
||||
src/firewall/config/__init__.py.in | 3 ++-
|
||||
src/firewall/core/fw.py | 19 ++++++++-----------
|
||||
src/firewall/core/io/firewalld_conf.py | 14 ++++++++------
|
||||
src/firewall/server/config.py | 22 ++++++++++++++++++----
|
||||
src/firewall/server/firewalld.py | 2 +-
|
||||
src/tests/dbus/firewalld.conf.at | 4 ++++
|
||||
src/tests/features/rpfilter.at | 2 +-
|
||||
10 files changed, 67 insertions(+), 34 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index 7a0be1ff1b76..48e2a5a6527a 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -26,14 +26,19 @@ CleanupModulesOnExit=no
|
||||
Lockdown=no
|
||||
|
||||
# IPv6_rpfilter
|
||||
-# Performs a reverse path filter test on a packet for IPv6. If a reply to the
|
||||
-# packet would be sent via the same interface that the packet arrived on, the
|
||||
-# packet will match and be accepted, otherwise dropped.
|
||||
+# Performs reverse path filtering (RPF) on IPv6 packets as per RFC 3704.
|
||||
+# Possible values:
|
||||
+# - strict: Performs "strict" filtering as per RFC 3704. This check
|
||||
+# verifies that the in ingress interface is the same interface
|
||||
+# that would be used to send a packet reply to the source. That
|
||||
+# is, ingress == egress.
|
||||
+# - no: RPF is completely disabled.
|
||||
+#
|
||||
# The rp_filter for IPv4 is controlled using sysctl.
|
||||
# Note: This feature has a performance impact. See man page FIREWALLD.CONF(5)
|
||||
# for details.
|
||||
-# Default: yes
|
||||
-IPv6_rpfilter=yes
|
||||
+# Default: strict
|
||||
+IPv6_rpfilter=strict
|
||||
|
||||
# IndividualCalls
|
||||
# Do not use combined -restore calls, but individual calls. This increases the
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index 022569ccf502..be6972aa0d8a 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -121,9 +121,15 @@
|
||||
<term><option>IPv6_rpfilter</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
- If this option is enabled (it is by default), reverse path filter test on a packet for IPv6 is performed.
|
||||
- If a reply to the packet would be sent via the same interface that the packet arrived on, the packet will match and be accepted, otherwise dropped.
|
||||
- For IPv4 the rp_filter is controlled using sysctl.
|
||||
+ Performs reverse path filtering (RPF) on IPv6 packets as per RFC 3704.
|
||||
+ Possible values:
|
||||
+ - strict: Performs "strict" filtering as per RFC 3704. This check
|
||||
+ verifies that the in ingress interface is the same interface
|
||||
+ that would be used to send a packet reply to the source. That
|
||||
+ is, ingress == egress.
|
||||
+ - no: RPF is completely disabled.
|
||||
+
|
||||
+ The rp_filter for IPv4 is controlled using sysctl.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Note</emphasis>: This feature has a performance
|
||||
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
|
||||
index a3196ea4af38..f04cf5ae757b 100644
|
||||
--- a/doc/xml/firewalld.dbus.xml
|
||||
+++ b/doc/xml/firewalld.dbus.xml
|
||||
@@ -2858,8 +2858,12 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="FirewallD1.config.Properties.IPv6_rpfilter">
|
||||
- <term><parameter>IPv6_rpfilter</parameter> - s - (rw)</term>
|
||||
- <listitem><para>Indicates whether the reverse path filter test on a packet for IPv6 is enabled. If a reply to the packet would be sent via the same interface that the packet arrived on, the packet will match and be accepted, otherwise dropped.</para></listitem>
|
||||
+ <term><parameter>IPv6_rpfilter</parameter> - b - (rw)</term>
|
||||
+ <listitem><para>Deprecated. See <link linkend="FirewallD1.config.Properties.IPv6_rpfilter2">org.fedoraproject.FirewallD1.config.Properties.IPv6_rpfilter2</link>.</para></listitem>
|
||||
+ </varlistentry>
|
||||
+ <varlistentry id="FirewallD1.config.Properties.IPv6_rpfilter2">
|
||||
+ <term><parameter>IPv6_rpfilter2</parameter> - s - (rw)</term>
|
||||
+ <listitem><para>Indicates whether the reverse path filter (RFE 3704) test on a packet for IPv6 is enabled.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="FirewallD1.config.Properties.IndividualCalls">
|
||||
<term><parameter>IndividualCalls</parameter> - s - (ro)</term>
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index da1e31e10e58..68e9bddce5a8 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -120,6 +120,7 @@ COMMANDS = {
|
||||
LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
|
||||
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
|
||||
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
|
||||
+IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict"]
|
||||
|
||||
# fallbacks: will be overloaded by firewalld.conf
|
||||
FALLBACK_ZONE = "public"
|
||||
@@ -127,7 +128,7 @@ FALLBACK_MINIMAL_MARK = 100
|
||||
FALLBACK_CLEANUP_ON_EXIT = True
|
||||
FALLBACK_CLEANUP_MODULES_ON_EXIT = False
|
||||
FALLBACK_LOCKDOWN = False
|
||||
-FALLBACK_IPV6_RPFILTER = True
|
||||
+FALLBACK_IPV6_RPFILTER = "strict"
|
||||
FALLBACK_INDIVIDUAL_CALLS = False
|
||||
FALLBACK_LOG_DENIED = "off"
|
||||
FALLBACK_AUTOMATIC_HELPERS = "no"
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index ac13be122b66..b2e150077958 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -98,7 +98,7 @@ class Firewall(object):
|
||||
self.ebtables_enabled, self._state, self._panic,
|
||||
self._default_zone, self._module_refcount, self._marks,
|
||||
self.cleanup_on_exit, self.cleanup_modules_on_exit,
|
||||
- self.ipv6_rpfilter_enabled, self.ipset_enabled,
|
||||
+ self._ipv6_rpfilter, self.ipset_enabled,
|
||||
self._individual_calls, self._log_denied)
|
||||
|
||||
def __init_vars(self):
|
||||
@@ -112,7 +112,7 @@ class Firewall(object):
|
||||
# fallback settings will be overloaded by firewalld.conf
|
||||
self.cleanup_on_exit = config.FALLBACK_CLEANUP_ON_EXIT
|
||||
self.cleanup_modules_on_exit = config.FALLBACK_CLEANUP_MODULES_ON_EXIT
|
||||
- self.ipv6_rpfilter_enabled = config.FALLBACK_IPV6_RPFILTER
|
||||
+ self._ipv6_rpfilter = config.FALLBACK_IPV6_RPFILTER
|
||||
self._individual_calls = config.FALLBACK_INDIVIDUAL_CALLS
|
||||
self._log_denied = config.FALLBACK_LOG_DENIED
|
||||
self._firewall_backend = config.FALLBACK_FIREWALL_BACKEND
|
||||
@@ -329,14 +329,11 @@ class Firewall(object):
|
||||
if self._firewalld_conf.get("IPv6_rpfilter"):
|
||||
value = self._firewalld_conf.get("IPv6_rpfilter")
|
||||
if value is not None:
|
||||
- if value.lower() in [ "no", "false" ]:
|
||||
- self.ipv6_rpfilter_enabled = False
|
||||
- if value.lower() in [ "yes", "true" ]:
|
||||
- self.ipv6_rpfilter_enabled = True
|
||||
- if self.ipv6_rpfilter_enabled:
|
||||
- log.debug1("IPv6 rpfilter is enabled")
|
||||
- else:
|
||||
- log.debug1("IPV6 rpfilter is disabled")
|
||||
+ if value.lower() in ["no", "false"]:
|
||||
+ self._ipv6_rpfilter = "no"
|
||||
+ elif value.lower() in ["yes", "true", "strict"]:
|
||||
+ self._ipv6_rpfilter = "strict"
|
||||
+ log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
|
||||
|
||||
if self._firewalld_conf.get("IndividualCalls"):
|
||||
value = self._firewalld_conf.get("IndividualCalls")
|
||||
@@ -933,7 +930,7 @@ class Firewall(object):
|
||||
if self.is_ipv_enabled("ipv6"):
|
||||
ipv6_backend = self.get_backend_by_ipv("ipv6")
|
||||
if "raw" in ipv6_backend.get_available_tables():
|
||||
- if self.ipv6_rpfilter_enabled:
|
||||
+ if self._ipv6_rpfilter != "no":
|
||||
rules = ipv6_backend.build_rpfilter_rules(self._log_denied)
|
||||
transaction.add_rules(ipv6_backend, rules)
|
||||
|
||||
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
|
||||
index d2879b319d1f..9ad64883b656 100644
|
||||
--- a/src/firewall/core/io/firewalld_conf.py
|
||||
+++ b/src/firewall/core/io/firewalld_conf.py
|
||||
@@ -71,7 +71,7 @@ class firewalld_conf(object):
|
||||
self.set("CleanupOnExit", "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no")
|
||||
self.set("CleanupModulesOnExit", "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no")
|
||||
self.set("Lockdown", "yes" if config.FALLBACK_LOCKDOWN else "no")
|
||||
- self.set("IPv6_rpfilter","yes" if config.FALLBACK_IPV6_RPFILTER else "no")
|
||||
+ self.set("IPv6_rpfilter", config.FALLBACK_IPV6_RPFILTER)
|
||||
self.set("IndividualCalls", "yes" if config.FALLBACK_INDIVIDUAL_CALLS else "no")
|
||||
self.set("LogDenied", config.FALLBACK_LOG_DENIED)
|
||||
self.set("AutomaticHelpers", config.FALLBACK_AUTOMATIC_HELPERS)
|
||||
@@ -160,12 +160,14 @@ class firewalld_conf(object):
|
||||
|
||||
# check ipv6_rpfilter
|
||||
value = self.get("IPv6_rpfilter")
|
||||
- if not value or value.lower() not in [ "yes", "true", "no", "false" ]:
|
||||
+ if not value or value.lower() not in config.IPV6_RPFILTER_VALUES:
|
||||
if value is not None:
|
||||
- log.warning("IPv6_rpfilter '%s' is not valid, using default "
|
||||
- "value %s", value if value else '',
|
||||
- config.FALLBACK_IPV6_RPFILTER)
|
||||
- self.set("IPv6_rpfilter","yes" if config.FALLBACK_IPV6_RPFILTER else "no")
|
||||
+ log.warning(
|
||||
+ "IPv6_rpfilter '%s' is not valid, using default " "value %s",
|
||||
+ value if value else "",
|
||||
+ config.FALLBACK_IPV6_RPFILTER,
|
||||
+ )
|
||||
+ self.set("IPv6_rpfilter", config.FALLBACK_IPV6_RPFILTER)
|
||||
|
||||
# check individual calls
|
||||
value = self.get("IndividualCalls")
|
||||
diff --git a/src/firewall/server/config.py b/src/firewall/server/config.py
|
||||
index dfbbb889b520..b805e497bb05 100644
|
||||
--- a/src/firewall/server/config.py
|
||||
+++ b/src/firewall/server/config.py
|
||||
@@ -100,6 +100,7 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"CleanupOnExit": "readwrite",
|
||||
"CleanupModulesOnExit": "readwrite",
|
||||
"IPv6_rpfilter": "readwrite",
|
||||
+ "IPv6_rpfilter2": "readwrite",
|
||||
"Lockdown": "readwrite",
|
||||
"MinimalMark": "readwrite",
|
||||
"IndividualCalls": "readwrite",
|
||||
@@ -564,7 +565,7 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
- "AllowZoneDrifting" ]:
|
||||
+ "AllowZoneDrifting", "IPv6_rpfilter2" ]:
|
||||
raise dbus.exceptions.DBusException(
|
||||
"org.freedesktop.DBus.Error.InvalidArgs: "
|
||||
"Property '%s' does not exist" % prop)
|
||||
@@ -594,8 +595,13 @@ class FirewallDConfig(DbusServiceObject):
|
||||
value = "yes" if config.FALLBACK_LOCKDOWN else "no"
|
||||
return dbus.String(value)
|
||||
elif prop == "IPv6_rpfilter":
|
||||
+ if value is None or value != "no":
|
||||
+ return dbus.String("yes")
|
||||
+ else:
|
||||
+ return dbus.String("no")
|
||||
+ elif prop == "IPv6_rpfilter2":
|
||||
if value is None:
|
||||
- value = "yes" if config.FALLBACK_IPV6_RPFILTER else "no"
|
||||
+ value = config.FALLBACK_IPV6_RPFILTER
|
||||
return dbus.String(value)
|
||||
elif prop == "IndividualCalls":
|
||||
if value is None:
|
||||
@@ -640,6 +646,8 @@ class FirewallDConfig(DbusServiceObject):
|
||||
return dbus.String(self._get_property(prop))
|
||||
elif prop == "IPv6_rpfilter":
|
||||
return dbus.String(self._get_property(prop))
|
||||
+ elif prop == "IPv6_rpfilter2":
|
||||
+ return dbus.String(self._get_property(prop))
|
||||
elif prop == "IndividualCalls":
|
||||
return dbus.String(self._get_property(prop))
|
||||
elif prop == "LogDenied":
|
||||
@@ -693,7 +701,7 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
- "AllowZoneDrifting" ]:
|
||||
+ "AllowZoneDrifting", "IPv6_rpfilter2" ]:
|
||||
ret[x] = self._get_property(x)
|
||||
elif interface_name in [ config.dbus.DBUS_INTERFACE_CONFIG_DIRECT,
|
||||
config.dbus.DBUS_INTERFACE_CONFIG_POLICIES ]:
|
||||
@@ -722,7 +730,7 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"IPv6_rpfilter", "IndividualCalls",
|
||||
"LogDenied",
|
||||
"FirewallBackend", "FlushAllOnReload",
|
||||
- "RFC3964_IPv4"]:
|
||||
+ "RFC3964_IPv4", "IPv6_rpfilter2"]:
|
||||
if property_name in [ "CleanupOnExit", "CleanupModulesOnExit",
|
||||
"Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "FlushAllOnReload",
|
||||
@@ -742,6 +750,12 @@ class FirewallDConfig(DbusServiceObject):
|
||||
raise FirewallError(errors.INVALID_VALUE,
|
||||
"'%s' for %s" % \
|
||||
(new_value, property_name))
|
||||
+ elif property_name == "IPv6_rpfilter2":
|
||||
+ if new_value not in config.IPV6_RPFILTER_VALUES:
|
||||
+ raise FirewallError(
|
||||
+ errors.INVALID_VALUE,
|
||||
+ "'%s' for %s" % (new_value, property_name),
|
||||
+ )
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
"org.freedesktop.DBus.Error.InvalidArgs: "
|
||||
diff --git a/src/firewall/server/firewalld.py b/src/firewall/server/firewalld.py
|
||||
index e43ddb959f41..8b9593a22fd8 100644
|
||||
--- a/src/firewall/server/firewalld.py
|
||||
+++ b/src/firewall/server/firewalld.py
|
||||
@@ -165,7 +165,7 @@ class FirewallD(DbusServiceObject):
|
||||
return dbus.Boolean(self.fw.is_ipv_enabled("ipv6"))
|
||||
|
||||
elif prop == "IPv6_rpfilter":
|
||||
- return dbus.Boolean(self.fw.ipv6_rpfilter_enabled)
|
||||
+ return dbus.Boolean(False if self.fw._ipv6_rpfilter == "no" else True)
|
||||
|
||||
elif prop == "IPv6ICMPTypes":
|
||||
return dbus.Array(self.fw.ipv6_supported_icmp_types, "s")
|
||||
diff --git a/src/tests/dbus/firewalld.conf.at b/src/tests/dbus/firewalld.conf.at
|
||||
index 10b16cd9e06f..61c220f3e59c 100644
|
||||
--- a/src/tests/dbus/firewalld.conf.at
|
||||
+++ b/src/tests/dbus/firewalld.conf.at
|
||||
@@ -3,8 +3,10 @@ AT_KEYWORDS(dbus)
|
||||
|
||||
IF_HOST_SUPPORTS_NFT_FIB([
|
||||
EXPECTED_IPV6_RPFILTER_VALUE=yes
|
||||
+ EXPECTED_IPV6_RPFILTER2_VALUE=strict
|
||||
], [
|
||||
EXPECTED_IPV6_RPFILTER_VALUE=no
|
||||
+ EXPECTED_IPV6_RPFILTER2_VALUE=no
|
||||
])
|
||||
|
||||
IF_HOST_SUPPORTS_NFT_RULE_INDEX([
|
||||
@@ -23,6 +25,7 @@ string "DefaultZone" : variant string "public"
|
||||
string "FirewallBackend" : variant string "nftables"
|
||||
string "FlushAllOnReload" : variant string "yes"
|
||||
string "IPv6_rpfilter" : variant string m4_escape(["${EXPECTED_IPV6_RPFILTER_VALUE}"])
|
||||
+string "IPv6_rpfilter2" : variant string m4_escape(["${EXPECTED_IPV6_RPFILTER2_VALUE}"])
|
||||
string "IndividualCalls" : variant string m4_escape(["${EXPECTED_INDIVIDUAL_CALLS_VALUE}"])
|
||||
string "Lockdown" : variant string "no"
|
||||
string "LogDenied" : variant string "off"
|
||||
@@ -43,6 +46,7 @@ _helper([AutomaticHelpers], [string:"yes"], [variant string "no"])
|
||||
_helper([Lockdown], [string:"yes"], [variant string "yes"])
|
||||
_helper([LogDenied], [string:"all"], [variant string "all"])
|
||||
_helper([IPv6_rpfilter], [string:"yes"], [variant string "yes"])
|
||||
+_helper([IPv6_rpfilter2], [string:"no"], [variant string "no"])
|
||||
_helper([IndividualCalls], [string:"yes"], [variant string "yes"])
|
||||
_helper([FirewallBackend], [string:"iptables"], [variant string "iptables"])
|
||||
_helper([FlushAllOnReload], [string:"no"], [variant string "no"])
|
||||
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
|
||||
index 755d9dfd33cc..58a4b4500330 100644
|
||||
--- a/src/tests/features/rpfilter.at
|
||||
+++ b/src/tests/features/rpfilter.at
|
||||
@@ -2,7 +2,7 @@ FWD_START_TEST([rpfilter - strict])
|
||||
AT_KEYWORDS(rpfilter)
|
||||
CHECK_NFTABLES_FIB()
|
||||
|
||||
-AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=yes/' ./firewalld.conf])
|
||||
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=strict/' ./firewalld.conf])
|
||||
FWD_RELOAD()
|
||||
|
||||
NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
|
||||
--
|
||||
2.43.5
|
||||
|
166
0016-v2.2.0-feat-IPv6_rpfilter-support-loose-rpfilter.patch
Normal file
166
0016-v2.2.0-feat-IPv6_rpfilter-support-loose-rpfilter.patch
Normal file
@ -0,0 +1,166 @@
|
||||
From 41828e0723b1ad195a33c535918a2fb6fabaf88f Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 6 May 2024 10:22:09 -0400
|
||||
Subject: [PATCH 16/22] v2.2.0: feat(IPv6_rpfilter): support loose rpfilter
|
||||
|
||||
Support "loose" mode as per RFC 3704. This guarantees only that there is
|
||||
a patch back to the source, but does NOT guarantee that ingress ==
|
||||
egress.
|
||||
|
||||
(cherry picked from commit 669524a10658761f614e1f199970844db7259960)
|
||||
---
|
||||
config/firewalld.conf | 4 ++++
|
||||
doc/xml/firewalld.conf.xml | 4 ++++
|
||||
src/firewall/config/__init__.py.in | 2 +-
|
||||
src/firewall/core/fw.py | 2 ++
|
||||
src/firewall/core/ipXtables.py | 16 ++++++++++------
|
||||
src/firewall/core/nftables.py | 8 +++++++-
|
||||
src/tests/features/rpfilter.at | 26 ++++++++++++++++++++++++++
|
||||
7 files changed, 54 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index 48e2a5a6527a..c35caf9f152b 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -32,6 +32,10 @@ Lockdown=no
|
||||
# verifies that the in ingress interface is the same interface
|
||||
# that would be used to send a packet reply to the source. That
|
||||
# is, ingress == egress.
|
||||
+# - loose: Performs "loose" filtering as per RFC 3704. This check only
|
||||
+# verifies that there is a route back to the source through any
|
||||
+# interface; even if it's not the same one on which the packet
|
||||
+# arrived.
|
||||
# - no: RPF is completely disabled.
|
||||
#
|
||||
# The rp_filter for IPv4 is controlled using sysctl.
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index be6972aa0d8a..279a149ab2a1 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -127,6 +127,10 @@
|
||||
verifies that the in ingress interface is the same interface
|
||||
that would be used to send a packet reply to the source. That
|
||||
is, ingress == egress.
|
||||
+ - loose: Performs "loose" filtering as per RFC 3704. This check only
|
||||
+ verifies that there is a route back to the source through any
|
||||
+ interface; even if it's not the same one on which the packet
|
||||
+ arrived.
|
||||
- no: RPF is completely disabled.
|
||||
|
||||
The rp_filter for IPv4 is controlled using sysctl.
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index 68e9bddce5a8..6bfe96be35a6 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -120,7 +120,7 @@ COMMANDS = {
|
||||
LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
|
||||
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
|
||||
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
|
||||
-IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict"]
|
||||
+IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose"]
|
||||
|
||||
# fallbacks: will be overloaded by firewalld.conf
|
||||
FALLBACK_ZONE = "public"
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index b2e150077958..d9724e9c8534 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -333,6 +333,8 @@ class Firewall(object):
|
||||
self._ipv6_rpfilter = "no"
|
||||
elif value.lower() in ["yes", "true", "strict"]:
|
||||
self._ipv6_rpfilter = "strict"
|
||||
+ elif value.lower() in ["loose"]:
|
||||
+ self._ipv6_rpfilter = "loose"
|
||||
log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
|
||||
|
||||
if self._firewalld_conf.get("IndividualCalls"):
|
||||
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
|
||||
index 1a0cea7a3b4e..339840c37ba4 100644
|
||||
--- a/src/firewall/core/ipXtables.py
|
||||
+++ b/src/firewall/core/ipXtables.py
|
||||
@@ -1456,13 +1456,17 @@ class ip6tables(ip4tables):
|
||||
|
||||
def build_rpfilter_rules(self, log_denied=False):
|
||||
rules = []
|
||||
- rules.append([ "-I", "PREROUTING", "-t", "mangle",
|
||||
- "-m", "rpfilter", "--invert", "--validmark",
|
||||
- "-j", "DROP" ])
|
||||
+ rpfilter_fragment = ["-m", "rpfilter", "--invert", "--validmark"]
|
||||
+ if self._fw._ipv6_rpfilter == "loose":
|
||||
+ rpfilter_fragment += ["--loose"]
|
||||
+
|
||||
+ rules.append([ "-I", "PREROUTING", "-t", "mangle" ]
|
||||
+ + rpfilter_fragment +
|
||||
+ [ "-j", "DROP" ])
|
||||
if log_denied != "off":
|
||||
- rules.append([ "-I", "PREROUTING", "-t", "mangle",
|
||||
- "-m", "rpfilter", "--invert", "--validmark",
|
||||
- "-j", "LOG",
|
||||
+ rules.append([ "-I", "PREROUTING", "-t", "mangle" ]
|
||||
+ + rpfilter_fragment +
|
||||
+ [ "-j", "LOG",
|
||||
"--log-prefix", "rpfilter_DROP: " ])
|
||||
rules.append([ "-I", "PREROUTING", "-t", "mangle",
|
||||
"-p", "ipv6-icmp",
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index e9816147ef8e..9827e84042ef 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1614,10 +1614,16 @@ class nftables(object):
|
||||
|
||||
def build_rpfilter_rules(self, log_denied=False):
|
||||
rules = []
|
||||
+
|
||||
+ if self._fw._ipv6_rpfilter == "loose":
|
||||
+ fib_flags = ["saddr", "mark"]
|
||||
+ else:
|
||||
+ fib_flags = ["saddr", "mark", "iif"]
|
||||
+
|
||||
expr_fragments = [{"match": {"left": {"meta": {"key": "nfproto"}},
|
||||
"op": "==",
|
||||
"right": "ipv6"}},
|
||||
- {"match": {"left": {"fib": {"flags": ["saddr", "iif", "mark"],
|
||||
+ {"match": {"left": {"fib": {"flags": fib_flags,
|
||||
"result": "oif"}},
|
||||
"op": "==",
|
||||
"right": False}}]
|
||||
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
|
||||
index 58a4b4500330..23cd9e0e8d7f 100644
|
||||
--- a/src/tests/features/rpfilter.at
|
||||
+++ b/src/tests/features/rpfilter.at
|
||||
@@ -24,6 +24,32 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
|
||||
|
||||
FWD_END_TEST()
|
||||
|
||||
+FWD_START_TEST([rpfilter - loose])
|
||||
+AT_KEYWORDS(rpfilter)
|
||||
+CHECK_NFTABLES_FIB()
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=loose/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_PREROUTING {
|
||||
+ icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
|
||||
+ meta nfproto ipv6 fib saddr . mark oif missing drop
|
||||
+ }
|
||||
+ }
|
||||
+])
|
||||
+
|
||||
+IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
|
||||
+ ACCEPT 58 -- ::/0 ::/0 ipv6-icmptype 134
|
||||
+ ACCEPT 58 -- ::/0 ::/0 ipv6-icmptype 135
|
||||
+ DROP 0 -- ::/0 ::/0 rpfilter loose validmark invert
|
||||
+ PREROUTING_direct 0 -- ::/0 ::/0
|
||||
+ PREROUTING_ZONES 0 -- ::/0 ::/0
|
||||
+])
|
||||
+
|
||||
+FWD_END_TEST()
|
||||
+
|
||||
FWD_START_TEST([rpfilter - config values])
|
||||
AT_KEYWORDS(rpfilter)
|
||||
CHECK_NFTABLES_FIB()
|
||||
--
|
||||
2.43.5
|
||||
|
229
0017-v2.2.0-feat-IPv6_rpfilter-support-loose-forward-rpfi.patch
Normal file
229
0017-v2.2.0-feat-IPv6_rpfilter-support-loose-forward-rpfi.patch
Normal file
@ -0,0 +1,229 @@
|
||||
From a965defecfad03530f3374271fb4889ff895ab1c Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 14 May 2024 12:16:44 -0400
|
||||
Subject: [PATCH 17/22] v2.2.0: feat(IPv6_rpfilter): support loose-forward
|
||||
rpfilter
|
||||
|
||||
This adds a new config value for IPv6_rpfilter, loose-forward. In this
|
||||
mode the rpfilter occurs only for forwarded packets using the "loose"
|
||||
algorithm from RFC 3704. This is a significant performance improvement
|
||||
for end-stations that have a single default route because the RPF check
|
||||
is completely absent on INPUT.
|
||||
|
||||
This new value is NOT compatible with the iptables backend. This is
|
||||
enforced by configuration checks.
|
||||
|
||||
(cherry picked from commit fb692d2e560253c97c7fdd1e9fdbfcc8c61d0eba)
|
||||
---
|
||||
config/firewalld.conf | 2 ++
|
||||
doc/xml/firewalld.conf.xml | 2 ++
|
||||
src/firewall/config/__init__.py.in | 3 ++-
|
||||
src/firewall/core/fw.py | 2 ++
|
||||
src/firewall/core/io/firewalld_conf.py | 15 +++++++++++
|
||||
src/firewall/core/nftables.py | 30 ++++++++++++++-------
|
||||
src/tests/features/rpfilter.at | 36 ++++++++++++++++++++++++++
|
||||
7 files changed, 79 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index c35caf9f152b..5d0777d54b15 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -36,6 +36,8 @@ Lockdown=no
|
||||
# verifies that there is a route back to the source through any
|
||||
# interface; even if it's not the same one on which the packet
|
||||
# arrived.
|
||||
+# - loose-forward: This is almost identical to "loose", but does not perform
|
||||
+# RPF for packets targeted to the host (INPUT).
|
||||
# - no: RPF is completely disabled.
|
||||
#
|
||||
# The rp_filter for IPv4 is controlled using sysctl.
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index 279a149ab2a1..1303758347e2 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -131,6 +131,8 @@
|
||||
verifies that there is a route back to the source through any
|
||||
interface; even if it's not the same one on which the packet
|
||||
arrived.
|
||||
+ - loose-forward: This is almost identical to "loose", but does not perform
|
||||
+ RPF for packets targeted to the host (INPUT).
|
||||
- no: RPF is completely disabled.
|
||||
|
||||
The rp_filter for IPv4 is controlled using sysctl.
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index 6bfe96be35a6..f2c4c9f5afa1 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -120,7 +120,8 @@ COMMANDS = {
|
||||
LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
|
||||
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
|
||||
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
|
||||
-IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose"]
|
||||
+IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose",
|
||||
+ "loose-forward"]
|
||||
|
||||
# fallbacks: will be overloaded by firewalld.conf
|
||||
FALLBACK_ZONE = "public"
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index d9724e9c8534..8c20a4a606e2 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -335,6 +335,8 @@ class Firewall(object):
|
||||
self._ipv6_rpfilter = "strict"
|
||||
elif value.lower() in ["loose"]:
|
||||
self._ipv6_rpfilter = "loose"
|
||||
+ elif value.lower() in ["loose-forward"]:
|
||||
+ self._ipv6_rpfilter = "loose-forward"
|
||||
log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
|
||||
|
||||
if self._firewalld_conf.get("IndividualCalls"):
|
||||
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
|
||||
index 9ad64883b656..159715df7ede 100644
|
||||
--- a/src/firewall/core/io/firewalld_conf.py
|
||||
+++ b/src/firewall/core/io/firewalld_conf.py
|
||||
@@ -26,6 +26,7 @@ import shutil
|
||||
|
||||
from firewall import config
|
||||
from firewall.core.logger import log
|
||||
+from firewall import errors
|
||||
|
||||
valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit",
|
||||
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
@@ -81,6 +82,18 @@ class firewalld_conf(object):
|
||||
self.set("RFC3964_IPv4", "yes" if config.FALLBACK_RFC3964_IPV4 else "no")
|
||||
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
|
||||
|
||||
+ def sanity_check(self):
|
||||
+ if (
|
||||
+ self.get("FirewallBackend") == "iptables"
|
||||
+ and self.get("IPv6_rpfilter") == "loose-forward"
|
||||
+ ):
|
||||
+ raise errors.FirewallError(
|
||||
+ errors.INVALID_VALUE,
|
||||
+ "IPv6_rpfilter=loose-forward is incompatible "
|
||||
+ "with FirewallBackend=iptables. This is a limitation "
|
||||
+ "of the iptables backend.",
|
||||
+ )
|
||||
+
|
||||
# load self.filename
|
||||
def read(self):
|
||||
self.clear()
|
||||
@@ -238,6 +251,8 @@ class firewalld_conf(object):
|
||||
config.FALLBACK_ALLOW_ZONE_DRIFTING)
|
||||
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
|
||||
|
||||
+ self.sanity_check()
|
||||
+
|
||||
# save to self.filename if there are key/value changes
|
||||
def write(self):
|
||||
if len(self._config) < 1:
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 9827e84042ef..da2d8eb8ec29 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1614,9 +1614,13 @@ class nftables(object):
|
||||
|
||||
def build_rpfilter_rules(self, log_denied=False):
|
||||
rules = []
|
||||
+ rpfilter_chain = "filter_PREROUTING"
|
||||
|
||||
if self._fw._ipv6_rpfilter == "loose":
|
||||
fib_flags = ["saddr", "mark"]
|
||||
+ elif self._fw._ipv6_rpfilter == "loose-forward":
|
||||
+ fib_flags = ["saddr", "mark"]
|
||||
+ rpfilter_chain = "filter_FORWARD"
|
||||
else:
|
||||
fib_flags = ["saddr", "mark", "iif"]
|
||||
|
||||
@@ -1633,17 +1637,19 @@ class nftables(object):
|
||||
|
||||
rules.append({"insert": {"rule": {"family": "inet",
|
||||
"table": TABLE_NAME,
|
||||
- "chain": "filter_PREROUTING",
|
||||
+ "chain": rpfilter_chain,
|
||||
"expr": expr_fragments}}})
|
||||
# RHBZ#1058505, RHBZ#1575431 (bug in kernel 4.16-4.17)
|
||||
- rules.append({"insert": {"rule": {"family": "inet",
|
||||
- "table": TABLE_NAME,
|
||||
- "chain": "filter_PREROUTING",
|
||||
- "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6",
|
||||
- "field": "type"}},
|
||||
- "op": "==",
|
||||
- "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}},
|
||||
- {"accept": None}]}}})
|
||||
+ if self._fw._ipv6_rpfilter != "loose-forward":
|
||||
+ # this rule doesn't make sense for forwarded packets
|
||||
+ rules.append({"insert": {"rule": {"family": "inet",
|
||||
+ "table": TABLE_NAME,
|
||||
+ "chain": rpfilter_chain,
|
||||
+ "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6",
|
||||
+ "field": "type"}},
|
||||
+ "op": "==",
|
||||
+ "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}},
|
||||
+ {"accept": None}]}}})
|
||||
return rules
|
||||
|
||||
def build_rfc3964_ipv4_rules(self):
|
||||
@@ -1674,7 +1680,11 @@ class nftables(object):
|
||||
"chain": "filter_OUTPUT",
|
||||
"index": 1,
|
||||
"expr": expr_fragments}}})
|
||||
- forward_index = 4 if self._fw.get_log_denied() != "off" else 3
|
||||
+ forward_index = 3
|
||||
+ if self._fw.get_log_denied() != "off":
|
||||
+ forward_index += 1
|
||||
+ if self._fw._ipv6_rpfilter == "loose-forward":
|
||||
+ forward_index += 1
|
||||
rules.append({"add": {"rule": {"family": "inet",
|
||||
"table": TABLE_NAME,
|
||||
"chain": "filter_FORWARD",
|
||||
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
|
||||
index 23cd9e0e8d7f..5c25ed7e16f0 100644
|
||||
--- a/src/tests/features/rpfilter.at
|
||||
+++ b/src/tests/features/rpfilter.at
|
||||
@@ -50,6 +50,42 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
|
||||
|
||||
FWD_END_TEST()
|
||||
|
||||
+FWD_START_TEST([rpfilter - loose-forward])
|
||||
+AT_KEYWORDS(rpfilter)
|
||||
+CHECK_NFTABLES_FIB()
|
||||
+CHECK_NFTABLES_FIB_IN_FORWARD()
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=loose-forward/' ./firewalld.conf])
|
||||
+m4_if(iptables, FIREWALL_BACKEND, [
|
||||
+FWD_RELOAD(114, [ignore], [ignore])
|
||||
+], [
|
||||
+FWD_RELOAD()
|
||||
+])
|
||||
+
|
||||
+NFT_LIST_RULES([inet], [filter_FORWARD], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_FORWARD {
|
||||
+ meta nfproto ipv6 fib saddr . mark oif missing drop
|
||||
+ ct state established,related accept
|
||||
+ ct status dnat accept
|
||||
+ iifname "lo" accept
|
||||
+ ct state invalid drop
|
||||
+ ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable
|
||||
+ jump filter_FORWARD_ZONES
|
||||
+ reject with icmpx admin-prohibited
|
||||
+ }
|
||||
+ }
|
||||
+])
|
||||
+
|
||||
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_PREROUTING {
|
||||
+ }
|
||||
+ }
|
||||
+])
|
||||
+
|
||||
+FWD_END_TEST([-e "/^ERROR: INVALID_VALUE:/d"])
|
||||
+
|
||||
FWD_START_TEST([rpfilter - config values])
|
||||
AT_KEYWORDS(rpfilter)
|
||||
CHECK_NFTABLES_FIB()
|
||||
--
|
||||
2.43.5
|
||||
|
183
0018-v2.2.0-feat-IPv6_rpfilter-support-strict-forward-rpf.patch
Normal file
183
0018-v2.2.0-feat-IPv6_rpfilter-support-strict-forward-rpf.patch
Normal file
@ -0,0 +1,183 @@
|
||||
From 7629e1c24b73d8c1e56d275c07076bc3bf99caad Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Fri, 17 May 2024 10:05:04 -0400
|
||||
Subject: [PATCH 18/22] v2.2.0: feat(IPv6_rpfilter): support strict-forward
|
||||
rpfilter
|
||||
|
||||
(cherry picked from commit 98e5cbb862885e00981611a79f7a3186da5d1050)
|
||||
---
|
||||
config/firewalld.conf | 2 ++
|
||||
doc/xml/firewalld.conf.xml | 5 +++-
|
||||
src/firewall/config/__init__.py.in | 2 +-
|
||||
src/firewall/core/fw.py | 2 ++
|
||||
src/firewall/core/io/firewalld_conf.py | 8 +++---
|
||||
src/firewall/core/nftables.py | 7 +++--
|
||||
src/tests/features/rpfilter.at | 36 ++++++++++++++++++++++++++
|
||||
7 files changed, 54 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index 5d0777d54b15..cf95af3eea8e 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -36,6 +36,8 @@ Lockdown=no
|
||||
# verifies that there is a route back to the source through any
|
||||
# interface; even if it's not the same one on which the packet
|
||||
# arrived.
|
||||
+# - strict-forward: This is almost identical to "strict", but does not perform
|
||||
+# RPF for packets targeted to the host (INPUT).
|
||||
# - loose-forward: This is almost identical to "loose", but does not perform
|
||||
# RPF for packets targeted to the host (INPUT).
|
||||
# - no: RPF is completely disabled.
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index 1303758347e2..4b9bfdad15be 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -131,6 +131,8 @@
|
||||
verifies that there is a route back to the source through any
|
||||
interface; even if it's not the same one on which the packet
|
||||
arrived.
|
||||
+ - strict-forward: This is almost identical to "loose", but does not perform
|
||||
+ RPF for packets targeted to the host (INPUT).
|
||||
- loose-forward: This is almost identical to "loose", but does not perform
|
||||
RPF for packets targeted to the host (INPUT).
|
||||
- no: RPF is completely disabled.
|
||||
@@ -144,7 +146,8 @@
|
||||
the established connections fast path. As such it can have a
|
||||
significant performance impact if there is a lot of traffic. It's
|
||||
enabled by default for security, but can be disabled if performance is
|
||||
- a concern.
|
||||
+ a concern. Alternatively one of the variants that only does RPF on
|
||||
+ forwarded packets may be used.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index f2c4c9f5afa1..2eda337e8180 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -121,7 +121,7 @@ LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
|
||||
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
|
||||
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
|
||||
IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose",
|
||||
- "loose-forward"]
|
||||
+ "loose-forward", "strict-forward"]
|
||||
|
||||
# fallbacks: will be overloaded by firewalld.conf
|
||||
FALLBACK_ZONE = "public"
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index 8c20a4a606e2..f91ff53fc37a 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -337,6 +337,8 @@ class Firewall(object):
|
||||
self._ipv6_rpfilter = "loose"
|
||||
elif value.lower() in ["loose-forward"]:
|
||||
self._ipv6_rpfilter = "loose-forward"
|
||||
+ elif value.lower() in ["strict-forward"]:
|
||||
+ self._ipv6_rpfilter = "strict-forward"
|
||||
log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
|
||||
|
||||
if self._firewalld_conf.get("IndividualCalls"):
|
||||
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
|
||||
index 159715df7ede..20072e26cfcd 100644
|
||||
--- a/src/firewall/core/io/firewalld_conf.py
|
||||
+++ b/src/firewall/core/io/firewalld_conf.py
|
||||
@@ -83,13 +83,13 @@ class firewalld_conf(object):
|
||||
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
|
||||
|
||||
def sanity_check(self):
|
||||
- if (
|
||||
- self.get("FirewallBackend") == "iptables"
|
||||
- and self.get("IPv6_rpfilter") == "loose-forward"
|
||||
+ if self.get("FirewallBackend") == "iptables" and self.get("IPv6_rpfilter") in (
|
||||
+ "loose-forward",
|
||||
+ "strict-forward",
|
||||
):
|
||||
raise errors.FirewallError(
|
||||
errors.INVALID_VALUE,
|
||||
- "IPv6_rpfilter=loose-forward is incompatible "
|
||||
+ f"IPv6_rpfilter={self.get('IPv6_rpfilter')} is incompatible "
|
||||
"with FirewallBackend=iptables. This is a limitation "
|
||||
"of the iptables backend.",
|
||||
)
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index da2d8eb8ec29..5a49b34e3a4f 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1621,6 +1621,9 @@ class nftables(object):
|
||||
elif self._fw._ipv6_rpfilter == "loose-forward":
|
||||
fib_flags = ["saddr", "mark"]
|
||||
rpfilter_chain = "filter_FORWARD"
|
||||
+ elif self._fw._ipv6_rpfilter == "strict-forward":
|
||||
+ fib_flags = ["saddr", "mark", "iif"]
|
||||
+ rpfilter_chain = "filter_FORWARD"
|
||||
else:
|
||||
fib_flags = ["saddr", "mark", "iif"]
|
||||
|
||||
@@ -1640,7 +1643,7 @@ class nftables(object):
|
||||
"chain": rpfilter_chain,
|
||||
"expr": expr_fragments}}})
|
||||
# RHBZ#1058505, RHBZ#1575431 (bug in kernel 4.16-4.17)
|
||||
- if self._fw._ipv6_rpfilter != "loose-forward":
|
||||
+ if self._fw._ipv6_rpfilter not in ("loose-forward", "strict-forward"):
|
||||
# this rule doesn't make sense for forwarded packets
|
||||
rules.append({"insert": {"rule": {"family": "inet",
|
||||
"table": TABLE_NAME,
|
||||
@@ -1683,7 +1686,7 @@ class nftables(object):
|
||||
forward_index = 3
|
||||
if self._fw.get_log_denied() != "off":
|
||||
forward_index += 1
|
||||
- if self._fw._ipv6_rpfilter == "loose-forward":
|
||||
+ if self._fw._ipv6_rpfilter in ("loose-forward", "strict-forward"):
|
||||
forward_index += 1
|
||||
rules.append({"add": {"rule": {"family": "inet",
|
||||
"table": TABLE_NAME,
|
||||
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
|
||||
index 5c25ed7e16f0..9ad50c993ba0 100644
|
||||
--- a/src/tests/features/rpfilter.at
|
||||
+++ b/src/tests/features/rpfilter.at
|
||||
@@ -50,6 +50,42 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
|
||||
|
||||
FWD_END_TEST()
|
||||
|
||||
+FWD_START_TEST([rpfilter - strict-forward])
|
||||
+AT_KEYWORDS(rpfilter)
|
||||
+CHECK_NFTABLES_FIB()
|
||||
+CHECK_NFTABLES_FIB_IN_FORWARD()
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=strict-forward/' ./firewalld.conf])
|
||||
+m4_if(iptables, FIREWALL_BACKEND, [
|
||||
+FWD_RELOAD(114, [ignore], [ignore])
|
||||
+], [
|
||||
+FWD_RELOAD()
|
||||
+])
|
||||
+
|
||||
+NFT_LIST_RULES([inet], [filter_FORWARD], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_FORWARD {
|
||||
+ meta nfproto ipv6 fib saddr . mark . iif oif missing drop
|
||||
+ ct state established,related accept
|
||||
+ ct status dnat accept
|
||||
+ iifname "lo" accept
|
||||
+ ct state invalid drop
|
||||
+ ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable
|
||||
+ jump filter_FORWARD_ZONES
|
||||
+ reject with icmpx admin-prohibited
|
||||
+ }
|
||||
+ }
|
||||
+])
|
||||
+
|
||||
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_PREROUTING {
|
||||
+ }
|
||||
+ }
|
||||
+])
|
||||
+
|
||||
+FWD_END_TEST([-e "/^ERROR: INVALID_VALUE:/d"])
|
||||
+
|
||||
FWD_START_TEST([rpfilter - loose-forward])
|
||||
AT_KEYWORDS(rpfilter)
|
||||
CHECK_NFTABLES_FIB()
|
||||
--
|
||||
2.43.5
|
||||
|
@ -1,7 +1,7 @@
|
||||
Summary: A firewall daemon with D-Bus interface providing a dynamic firewall
|
||||
Name: firewalld
|
||||
Version: 1.3.4
|
||||
Release: 5%{?dist}
|
||||
Release: 6%{?dist}
|
||||
URL: http://www.firewalld.org
|
||||
License: GPLv2+
|
||||
Source0: https://github.com/firewalld/firewalld/releases/download/v%{version}/firewalld-%{version}.tar.bz2
|
||||
@ -15,6 +15,14 @@ Patch7: 0007-v2.1.0-fix-rich-validate-service-name-of-rich-rule.patch
|
||||
Patch8: 0008-v2.1.0-improvement-nftables-do-not-track-rule-handle.patch
|
||||
Patch9: 0009-v2.1.0-improvement-fw-make-set_policy-DROP-more-flex.patch
|
||||
Patch10: 0010-v2.1.0-feat-fw-add-ReloadPolicy-option-in-firewalld..patch
|
||||
Patch11: 0011-v2.2.0-test-functions-add-macro-CHECK_NFTABLES_FIB.patch
|
||||
Patch12: 0012-v2.2.0-test-functions-add-macro-CHECK_NFTABLES_FIB_I.patch
|
||||
Patch13: 0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
|
||||
Patch14: 0014-v2.2.0-test-IPv6_rpfilter-verify-valid-values.patch
|
||||
Patch15: 0015-v2.2.0-chore-IPv6_rpfilter-prepare-for-new-config-va.patch
|
||||
Patch16: 0016-v2.2.0-feat-IPv6_rpfilter-support-loose-rpfilter.patch
|
||||
Patch17: 0017-v2.2.0-feat-IPv6_rpfilter-support-loose-forward-rpfi.patch
|
||||
Patch18: 0018-v2.2.0-feat-IPv6_rpfilter-support-strict-forward-rpf.patch
|
||||
BuildArch: noarch
|
||||
BuildRequires: autoconf
|
||||
BuildRequires: automake
|
||||
@ -238,6 +246,11 @@ rm -rf %{buildroot}%{_datadir}/firewalld/testsuite
|
||||
%{_mandir}/man1/firewall-config*.1*
|
||||
|
||||
%changelog
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-6
|
||||
- feat(IPv6_rpfilter): support loose rpfilter
|
||||
- feat(IPv6_rpfilter): support loose-forward rpfilter
|
||||
- feat(IPv6_rpfilter): support strict-forward rpfilter
|
||||
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-5
|
||||
- feat(fw): add ReloadPolicy option in firewalld.conf
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user