167 lines
6.9 KiB
Diff
167 lines
6.9 KiB
Diff
|
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
|
||
|
|