Compare commits
No commits in common. "c8" and "c9-beta" have entirely different histories.
@ -1 +1 @@
|
||||
e5b8b96e901d81ea8e806f44306acbf73487f3ad SOURCES/firewalld-0.9.11.tar.gz
|
||||
c72d4a04e7726d3352c911f20ddc44cff5515c4c SOURCES/firewalld-1.3.4.tar.bz2
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
||||
SOURCES/firewalld-0.9.11.tar.gz
|
||||
SOURCES/firewalld-1.3.4.tar.bz2
|
||||
|
@ -1,7 +1,7 @@
|
||||
From feb06c3d50c737183c08fd05592d5c9209f4b966 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <e@erig.me>
|
||||
Date: Mon, 9 Jul 2018 11:29:33 -0400
|
||||
Subject: [PATCH 01/10] RHEL only: Add cockpit by default to some zones
|
||||
From f113f17734cfb964bd2b72f233c48e650e205cb9 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <egarver@redhat.com>
|
||||
Date: Tue, 25 May 2021 13:31:41 -0400
|
||||
Subject: [PATCH 1/4] RHEL only: Add cockpit by default to some zones
|
||||
|
||||
Fixes: #1581578
|
||||
---
|
||||
@ -9,57 +9,75 @@ Fixes: #1581578
|
||||
config/zones/internal.xml | 1 +
|
||||
config/zones/public.xml | 1 +
|
||||
config/zones/work.xml | 1 +
|
||||
src/tests/functions.at | 19 +++++++++++++++++++
|
||||
5 files changed, 23 insertions(+)
|
||||
src/tests/features/startup_failsafe.at | 1 +
|
||||
src/tests/functions.at | 20 ++++++++++++++++++++
|
||||
6 files changed, 25 insertions(+)
|
||||
|
||||
diff --git a/config/zones/home.xml b/config/zones/home.xml
|
||||
index 42b29b2f2d50..8aa8afa0e8aa 100644
|
||||
index d73c9bdb16b6..33064688367e 100644
|
||||
--- a/config/zones/home.xml
|
||||
+++ b/config/zones/home.xml
|
||||
@@ -6,4 +6,5 @@
|
||||
@@ -6,5 +6,6 @@
|
||||
<service name="mdns"/>
|
||||
<service name="samba-client"/>
|
||||
<service name="dhcpv6-client"/>
|
||||
+ <service name="cockpit"/>
|
||||
<forward/>
|
||||
</zone>
|
||||
diff --git a/config/zones/internal.xml b/config/zones/internal.xml
|
||||
index e646b48c94e8..40cb7e14424b 100644
|
||||
index 053c18ccda8b..852b16ad94dd 100644
|
||||
--- a/config/zones/internal.xml
|
||||
+++ b/config/zones/internal.xml
|
||||
@@ -6,4 +6,5 @@
|
||||
@@ -6,5 +6,6 @@
|
||||
<service name="mdns"/>
|
||||
<service name="samba-client"/>
|
||||
<service name="dhcpv6-client"/>
|
||||
+ <service name="cockpit"/>
|
||||
<forward/>
|
||||
</zone>
|
||||
diff --git a/config/zones/public.xml b/config/zones/public.xml
|
||||
index 49795d8c9068..617e131a4895 100644
|
||||
index 49fc4c20af52..62bc751de448 100644
|
||||
--- a/config/zones/public.xml
|
||||
+++ b/config/zones/public.xml
|
||||
@@ -4,4 +4,5 @@
|
||||
@@ -4,5 +4,6 @@
|
||||
<description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
|
||||
<service name="ssh"/>
|
||||
<service name="dhcpv6-client"/>
|
||||
+ <service name="cockpit"/>
|
||||
<forward/>
|
||||
</zone>
|
||||
diff --git a/config/zones/work.xml b/config/zones/work.xml
|
||||
index 6ea5550a40bd..9609ee6f65c2 100644
|
||||
index f1a14a9b4682..27b54a7783c4 100644
|
||||
--- a/config/zones/work.xml
|
||||
+++ b/config/zones/work.xml
|
||||
@@ -4,4 +4,5 @@
|
||||
@@ -4,5 +4,6 @@
|
||||
<description>For use in work areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
|
||||
<service name="ssh"/>
|
||||
<service name="dhcpv6-client"/>
|
||||
+ <service name="cockpit"/>
|
||||
<forward/>
|
||||
</zone>
|
||||
diff --git a/src/tests/features/startup_failsafe.at b/src/tests/features/startup_failsafe.at
|
||||
index 3cdf7c3c307a..b9401d460114 100644
|
||||
--- a/src/tests/features/startup_failsafe.at
|
||||
+++ b/src/tests/features/startup_failsafe.at
|
||||
@@ -20,6 +20,7 @@ NFT_LIST_RULES([inet], [filter_IN_public_allow], 0, [dnl
|
||||
chain filter_IN_public_allow {
|
||||
tcp dport 22 accept
|
||||
ip6 daddr fe80::/64 udp dport 546 accept
|
||||
+ tcp dport 9090 accept
|
||||
tcp dport 443 accept
|
||||
}
|
||||
}
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index 72db26d5ce0c..2f8183966760 100644
|
||||
index 244d24686c86..ad3462c6715f 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -112,6 +112,13 @@ m4_define([FWD_START_TEST], [
|
||||
@@ -128,6 +128,14 @@ m4_define([FWD_START_TEST], [
|
||||
fi
|
||||
|
||||
m4_ifdef([TESTING_FIREWALL_OFFLINE_CMD], [
|
||||
+ AT_KEYWORDS(offline)
|
||||
+ dnl cockpit is added by default downstream, but upstream tests don't expect
|
||||
+ dnl it. Simply remove it at the start of every test.
|
||||
+ dnl
|
||||
@ -68,9 +86,9 @@ index 72db26d5ce0c..2f8183966760 100644
|
||||
+ FWD_OFFLINE_CHECK([--zone public --remove-service-from-zone cockpit], 0, [ignore])
|
||||
+ FWD_OFFLINE_CHECK([--zone work --remove-service-from-zone cockpit], 0, [ignore])
|
||||
], [
|
||||
dnl don't unload modules or bother cleaning up, the namespace will be deleted
|
||||
AT_CHECK([sed -i 's/^CleanupOnExit.*/CleanupOnExit=no/' ./firewalld.conf])
|
||||
@@ -229,6 +236,18 @@ m4_define([FWD_START_TEST], [
|
||||
dnl set the appropriate backend
|
||||
AT_CHECK([sed -i 's/^FirewallBackend.*/FirewallBackend=FIREWALL_BACKEND/' ./firewalld.conf])
|
||||
@@ -259,6 +267,18 @@ m4_define([FWD_START_TEST], [
|
||||
])
|
||||
|
||||
FWD_START_FIREWALLD
|
||||
@ -90,5 +108,5 @@ index 72db26d5ce0c..2f8183966760 100644
|
||||
])
|
||||
|
||||
--
|
||||
2.39.1
|
||||
2.39.3
|
||||
|
||||
|
@ -1,80 +0,0 @@
|
||||
From 6b88f757186f0b6479c2a334c0c0362a2ba05570 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 4 Feb 2020 09:12:17 -0500
|
||||
Subject: [PATCH 02/10] RHEL only: default to AllowZoneDrifting=yes
|
||||
|
||||
---
|
||||
config/firewalld.conf | 4 ++--
|
||||
doc/xml/firewalld.conf.xml | 2 +-
|
||||
doc/xml/firewalld.dbus.xml | 2 +-
|
||||
src/firewall/config/__init__.py.in | 2 +-
|
||||
src/tests/functions.at | 5 +++++
|
||||
5 files changed, 10 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index 99d573dcf06f..a0556c0bbf5b 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -73,5 +73,5 @@ RFC3964_IPv4=yes
|
||||
# Note: If "yes" packets will only drift from source based zones to interface
|
||||
# based zones (including the default zone). Packets never drift from interface
|
||||
# based zones to other interfaces based zones (including the default zone).
|
||||
-# Possible values; "yes", "no". Defaults to "no".
|
||||
-AllowZoneDrifting=no
|
||||
+# Possible values; "yes", "no". Defaults to "yes".
|
||||
+AllowZoneDrifting=yes
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index 8155c547a216..0a6e8f2fdebf 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -206,7 +206,7 @@
|
||||
to interface based zones (including the default zone). Packets
|
||||
never drift from interface based zones to other interfaces
|
||||
based zones (including the default zone).
|
||||
- Valid values; "yes", "no". Defaults to "no".
|
||||
+ Valid values; "yes", "no". Defaults to "yes".
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
|
||||
index da442f3f41b9..1c33ad5ee918 100644
|
||||
--- a/doc/xml/firewalld.dbus.xml
|
||||
+++ b/doc/xml/firewalld.dbus.xml
|
||||
@@ -2787,7 +2787,7 @@
|
||||
to interface based zones (including the default zone). Packets
|
||||
never drift from interface based zones to other interfaces
|
||||
based zones (including the default zone).
|
||||
- Valid values; "yes", "no". Defaults to "no".
|
||||
+ Valid values; "yes", "no". Defaults to "yes".
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="FirewallD1.config.Properties.AutomaticHelpers">
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index e875e849dec1..0dec7913f694 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -133,4 +133,4 @@ FALLBACK_AUTOMATIC_HELPERS = "no"
|
||||
FALLBACK_FIREWALL_BACKEND = "nftables"
|
||||
FALLBACK_FLUSH_ALL_ON_RELOAD = True
|
||||
FALLBACK_RFC3964_IPV4 = True
|
||||
-FALLBACK_ALLOW_ZONE_DRIFTING = False
|
||||
+FALLBACK_ALLOW_ZONE_DRIFTING = True
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index 2f8183966760..a2989c6345da 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -126,6 +126,11 @@ m4_define([FWD_START_TEST], [
|
||||
dnl set the appropriate backend
|
||||
AT_CHECK([sed -i 's/^FirewallBackend.*/FirewallBackend=FIREWALL_BACKEND/' ./firewalld.conf])
|
||||
|
||||
+ dnl Expected test results assume this is set to "no", but downstream
|
||||
+ dnl RHEL overrides it to "yes". Override it back to "no" so we don't
|
||||
+ dnl have to fix up all the tests when bringing them from upstream.
|
||||
+ AT_CHECK([sed -i 's/^AllowZoneDrifting.*/AllowZoneDrifting=no/' ./firewalld.conf])
|
||||
+
|
||||
dnl fib matching is pretty new in nftables. Don't use rpfilter on older
|
||||
dnl kernels.
|
||||
m4_if(nftables, FIREWALL_BACKEND, [
|
||||
--
|
||||
2.39.1
|
||||
|
@ -1,13 +1,13 @@
|
||||
From 3a56ea30acb41358742a94f088f12bd4f1ba1f80 Mon Sep 17 00:00:00 2001
|
||||
From 0598abdbc3dd1816e9cc19186de9e95c6485519d Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 31 Jan 2023 09:24:56 -0500
|
||||
Subject: [PATCH 27/30] v2.0.0: test(atlocal): pass EBTABLES to testsuite
|
||||
Subject: [PATCH 2/4] v1.4.0: test(atlocal): pass EBTABLES to testsuite
|
||||
|
||||
(cherry picked from commit a5adb26a5eebdaa6e978c580d4fb73f7aa06802f)
|
||||
---
|
||||
src/tests/atlocal.in | 1 +
|
||||
src/tests/functions.at | 2 +-
|
||||
2 files changed, 2 insertions(+), 1 deletion(-)
|
||||
src/tests/functions.at | 3 ++-
|
||||
2 files changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/tests/atlocal.in b/src/tests/atlocal.in
|
||||
index 8c5493ac38df..595a96f0f5c9 100644
|
||||
@ -21,10 +21,18 @@ index 8c5493ac38df..595a96f0f5c9 100644
|
||||
export IPTABLES_RESTORE="@IPTABLES_RESTORE@"
|
||||
export IP6TABLES="@IP6TABLES@"
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index a2989c6345da..35e3271ce68d 100644
|
||||
index ad3462c6715f..f454ca980046 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -368,7 +368,7 @@ m4_define([EBTABLES_LIST_RULES_NORMALIZE], [dnl
|
||||
@@ -104,6 +104,7 @@ m4_define([FWD_START_TEST], [
|
||||
dnl in atlocal.
|
||||
dnl
|
||||
test -z "$PYTHON" && export PYTHON="python3"
|
||||
+ test -z "$EBTABLES" && export EBTABLES="ebtables"
|
||||
test -z "$IPTABLES" && export IPTABLES="iptables"
|
||||
test -z "$IPTABLES_RESTORE" && export IPTABLES_RESTORE="iptables-restore"
|
||||
test -z "$IP6TABLES" && export IP6TABLES="ip6tables"
|
||||
@@ -398,7 +399,7 @@ m4_define([EBTABLES_LIST_RULES_NORMALIZE], [dnl
|
||||
m4_define([EBTABLES_LIST_RULES], [
|
||||
dnl ebtables commit 5f508b76a0ce change list output for inversion.
|
||||
m4_ifdef([TESTING_FIREWALL_OFFLINE_CMD], [], [
|
||||
@ -34,5 +42,5 @@ index a2989c6345da..35e3271ce68d 100644
|
||||
])
|
||||
])
|
||||
--
|
||||
2.43.0
|
||||
2.39.3
|
||||
|
@ -1,56 +0,0 @@
|
||||
From 17a69c4dd7feff3c6101b5541497b8304447ed40 Mon Sep 17 00:00:00 2001
|
||||
From: Vrinda Punj <vpunj@redhat.com>
|
||||
Date: Tue, 1 Dec 2020 11:58:19 -0500
|
||||
Subject: [PATCH 03/10] v1.0.0: feat(service): add galera service Fixes:
|
||||
rhbz1696260
|
||||
|
||||
(cherry picked from commit 11632147677464cb7121d17526ead242e68be041)
|
||||
---
|
||||
config/Makefile.am | 1 +
|
||||
config/services/galera.xml | 9 +++++++++
|
||||
po/POTFILES.in | 1 +
|
||||
3 files changed, 11 insertions(+)
|
||||
create mode 100644 config/services/galera.xml
|
||||
|
||||
diff --git a/config/Makefile.am b/config/Makefile.am
|
||||
index fef3b55dc527..f844a5a00e2f 100644
|
||||
--- a/config/Makefile.am
|
||||
+++ b/config/Makefile.am
|
||||
@@ -159,6 +159,7 @@ CONFIG_FILES = \
|
||||
services/freeipa-replication.xml \
|
||||
services/freeipa-trust.xml \
|
||||
services/ftp.xml \
|
||||
+ services/galera.xml \
|
||||
services/ganglia-client.xml \
|
||||
services/ganglia-master.xml \
|
||||
services/git.xml \
|
||||
diff --git a/config/services/galera.xml b/config/services/galera.xml
|
||||
new file mode 100644
|
||||
index 000000000000..2305713fbcab
|
||||
--- /dev/null
|
||||
+++ b/config/services/galera.xml
|
||||
@@ -0,0 +1,9 @@
|
||||
+<?xml version="1.0" encoding="utf-8"?>
|
||||
+<service>
|
||||
+ <short>Galera</short>
|
||||
+ <description>MariaDB-Galera Database Server</description>
|
||||
+ <port protocol="tcp" port="3306"/>
|
||||
+ <port protocol="tcp" port="4567"/>
|
||||
+ <port protocol="tcp" port="4568"/>
|
||||
+ <port protocol="tcp" port="4444"/>
|
||||
+</service>
|
||||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||||
index 666eb677855b..249cff8d0d2f 100644
|
||||
--- a/po/POTFILES.in
|
||||
+++ b/po/POTFILES.in
|
||||
@@ -91,6 +91,7 @@ config/services/freeipa-ldap.xml
|
||||
config/services/freeipa-replication.xml
|
||||
config/services/freeipa-trust.xml
|
||||
config/services/ftp.xml
|
||||
+config/services/galera.xml
|
||||
config/services/ganglia-client.xml
|
||||
config/services/ganglia-master.xml
|
||||
config/services/git.xml
|
||||
--
|
||||
2.39.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 17c70eba7ddfd8a8687b16102cf5ee988e33993f Mon Sep 17 00:00:00 2001
|
||||
From 35a4e98cfee37b2883a58ac586f0bdb34810293b Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 30 Jan 2023 16:42:50 -0500
|
||||
Subject: [PATCH 29/30] v2.0.0: feat(direct): avoid iptables flush if using
|
||||
Subject: [PATCH 3/4] v1.4.0: feat(direct): avoid iptables flush if using
|
||||
nftables backend
|
||||
|
||||
If FirewallBackend=nftables and there are no direct rules; then we can
|
||||
@ -11,25 +11,25 @@ applications can control iptables while firewalld only touches nftables.
|
||||
Fixes: #863
|
||||
(cherry picked from commit b7faa74db15e2d1ebd9fdfcdc7579874d3a2fa87)
|
||||
---
|
||||
src/firewall/core/fw.py | 31 +++++++++++++++++++++++++++----
|
||||
src/firewall/core/fw.py | 30 ++++++++++++++++++++++++++----
|
||||
src/firewall/core/fw_direct.py | 9 +++++++++
|
||||
2 files changed, 36 insertions(+), 4 deletions(-)
|
||||
2 files changed, 35 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index 5cef18b5f889..a2ad39bd9f5f 100644
|
||||
index e9db1c6fede0..f1bc124b9443 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -425,7 +425,8 @@ class Firewall(object):
|
||||
@@ -473,7 +473,8 @@ class Firewall(object):
|
||||
def _start_apply_objects(self, reload=False, complete_reload=False):
|
||||
transaction = FirewallTransaction(self)
|
||||
|
||||
# flush rules
|
||||
- self.flush(use_transaction=transaction)
|
||||
+ if not reload:
|
||||
+ self.flush(use_transaction=transaction)
|
||||
|
||||
# If modules need to be unloaded in complete reload or if there are
|
||||
# ipsets to get applied, limit the transaction to flush.
|
||||
@@ -836,7 +837,26 @@ class Firewall(object):
|
||||
@@ -943,7 +944,26 @@ class Firewall(object):
|
||||
if use_transaction is None:
|
||||
transaction.execute(True)
|
||||
|
||||
@ -57,7 +57,7 @@ index 5cef18b5f889..a2ad39bd9f5f 100644
|
||||
|
||||
def flush(self, use_transaction=None):
|
||||
if use_transaction is None:
|
||||
@@ -846,7 +866,10 @@ class Firewall(object):
|
||||
@@ -953,7 +973,10 @@ class Firewall(object):
|
||||
|
||||
log.debug1("Flushing rule set")
|
||||
|
||||
@ -69,17 +69,16 @@ index 5cef18b5f889..a2ad39bd9f5f 100644
|
||||
rules = backend.build_flush_rules()
|
||||
transaction.add_rules(backend, rules)
|
||||
|
||||
@@ -1002,7 +1025,7 @@ class Firewall(object):
|
||||
@@ -1114,7 +1137,6 @@ class Firewall(object):
|
||||
if not _panic:
|
||||
self.set_policy("DROP")
|
||||
|
||||
- # stop
|
||||
+ self.flush()
|
||||
self.flush()
|
||||
self.cleanup()
|
||||
|
||||
start_exception = None
|
||||
diff --git a/src/firewall/core/fw_direct.py b/src/firewall/core/fw_direct.py
|
||||
index a35ebce1f276..5d4cc6a6918e 100644
|
||||
index 508cfa54f7fa..a4cd8a77e773 100644
|
||||
--- a/src/firewall/core/fw_direct.py
|
||||
+++ b/src/firewall/core/fw_direct.py
|
||||
@@ -219,6 +219,9 @@ class FirewallDirect(object):
|
||||
@ -89,29 +88,29 @@ index a35ebce1f276..5d4cc6a6918e 100644
|
||||
+ if self._fw.may_skip_flush_direct_backends():
|
||||
+ transaction.add_pre(self._fw.flush_direct_backends)
|
||||
+
|
||||
#TODO: policy="ACCEPT"
|
||||
self._chain(True, ipv, table, chain, transaction)
|
||||
if self._fw.ipset_enabled and self._fw.ipset.omit_native_ipset():
|
||||
transaction.add_pre(self._fw.ipset.apply_ipsets, [self._fw.ipset_backend])
|
||||
|
||||
@@ -265,6 +268,9 @@ class FirewallDirect(object):
|
||||
@@ -268,6 +271,9 @@ class FirewallDirect(object):
|
||||
else:
|
||||
transaction = use_transaction
|
||||
|
||||
+ if self._fw.may_skip_flush_direct_backends():
|
||||
+ transaction.add_pre(self._fw.flush_direct_backends)
|
||||
+
|
||||
self._rule(True, ipv, table, chain, priority, args, transaction)
|
||||
if self._fw.ipset_enabled and self._fw.ipset.omit_native_ipset():
|
||||
transaction.add_pre(self._fw.ipset.apply_ipsets, [self._fw.ipset_backend])
|
||||
|
||||
if use_transaction is None:
|
||||
@@ -347,6 +353,9 @@ class FirewallDirect(object):
|
||||
@@ -353,6 +359,9 @@ class FirewallDirect(object):
|
||||
else:
|
||||
transaction = use_transaction
|
||||
|
||||
+ if self._fw.may_skip_flush_direct_backends():
|
||||
+ transaction.add_pre(self._fw.flush_direct_backends)
|
||||
+
|
||||
self._passthrough(True, ipv, list(args), transaction)
|
||||
if self._fw.ipset_enabled and self._fw.ipset.omit_native_ipset():
|
||||
transaction.add_pre(self._fw.ipset.apply_ipsets, [self._fw.ipset_backend])
|
||||
|
||||
if use_transaction is None:
|
||||
--
|
||||
2.43.0
|
||||
2.39.3
|
||||
|
@ -1,242 +0,0 @@
|
||||
From 430dee713b69a32e5c5bf6b1f68a605564fe93ef Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Fri, 12 Feb 2021 14:23:21 -0500
|
||||
Subject: [PATCH 04/10] v1.0.0: fix(ipset): normalize entries in CIDR notation
|
||||
|
||||
This will convert things like 10.0.1.0/22 to 10.0.0.0/22. Fix up test
|
||||
cases in which the error code changed due to this.
|
||||
|
||||
(cherry picked from commit e4dc44fcfd214b27c718eb4d99d3b137495b9626)
|
||||
---
|
||||
src/firewall/client.py | 9 ++++++++-
|
||||
src/firewall/core/fw_ipset.py | 11 ++++++++++-
|
||||
src/firewall/core/ipset.py | 13 +++++++++++++
|
||||
src/firewall/server/config_ipset.py | 10 ++++++++--
|
||||
src/tests/regression/rhbz1601610.at | 19 +++++++++++++------
|
||||
5 files changed, 52 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/client.py b/src/firewall/client.py
|
||||
index 51bf09c8fad6..aa6bd7cd282b 100644
|
||||
--- a/src/firewall/client.py
|
||||
+++ b/src/firewall/client.py
|
||||
@@ -34,6 +34,7 @@ from firewall.core.base import DEFAULT_ZONE_TARGET, DEFAULT_POLICY_TARGET, DEFAU
|
||||
from firewall.dbus_utils import dbus_to_python
|
||||
from firewall.functions import b2u
|
||||
from firewall.core.rich import Rich_Rule
|
||||
+from firewall.core.ipset import normalize_ipset_entry
|
||||
from firewall import errors
|
||||
from firewall.errors import FirewallError
|
||||
|
||||
@@ -1616,12 +1617,16 @@ class FirewallClientIPSetSettings(object):
|
||||
if "timeout" in self.settings[4] and \
|
||||
self.settings[4]["timeout"] != "0":
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
- self.settings[5] = entries
|
||||
+ _entries = set()
|
||||
+ for _entry in dbus_to_python(entries, list):
|
||||
+ _entries.add(normalize_ipset_entry(_entry))
|
||||
+ self.settings[5] = list(_entries)
|
||||
@handle_exceptions
|
||||
def addEntry(self, entry):
|
||||
if "timeout" in self.settings[4] and \
|
||||
self.settings[4]["timeout"] != "0":
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
if entry not in self.settings[5]:
|
||||
self.settings[5].append(entry)
|
||||
else:
|
||||
@@ -1631,6 +1636,7 @@ class FirewallClientIPSetSettings(object):
|
||||
if "timeout" in self.settings[4] and \
|
||||
self.settings[4]["timeout"] != "0":
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
if entry in self.settings[5]:
|
||||
self.settings[5].remove(entry)
|
||||
else:
|
||||
@@ -1640,6 +1646,7 @@ class FirewallClientIPSetSettings(object):
|
||||
if "timeout" in self.settings[4] and \
|
||||
self.settings[4]["timeout"] != "0":
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
return entry in self.settings[5]
|
||||
|
||||
# ipset config
|
||||
diff --git a/src/firewall/core/fw_ipset.py b/src/firewall/core/fw_ipset.py
|
||||
index e90082407562..57e0e6cb51db 100644
|
||||
--- a/src/firewall/core/fw_ipset.py
|
||||
+++ b/src/firewall/core/fw_ipset.py
|
||||
@@ -24,7 +24,8 @@
|
||||
__all__ = [ "FirewallIPSet" ]
|
||||
|
||||
from firewall.core.logger import log
|
||||
-from firewall.core.ipset import remove_default_create_options as rm_def_cr_opts
|
||||
+from firewall.core.ipset import remove_default_create_options as rm_def_cr_opts, \
|
||||
+ normalize_ipset_entry
|
||||
from firewall.core.io.ipset import IPSet
|
||||
from firewall import errors
|
||||
from firewall.errors import FirewallError
|
||||
@@ -189,6 +190,7 @@ class FirewallIPSet(object):
|
||||
|
||||
def add_entry(self, name, entry):
|
||||
obj = self.get_ipset(name, applied=True)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
|
||||
IPSet.check_entry(entry, obj.options, obj.type)
|
||||
if entry in obj.entries:
|
||||
@@ -207,6 +209,7 @@ class FirewallIPSet(object):
|
||||
|
||||
def remove_entry(self, name, entry):
|
||||
obj = self.get_ipset(name, applied=True)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
|
||||
# no entry check for removal
|
||||
if entry not in obj.entries:
|
||||
@@ -224,6 +227,7 @@ class FirewallIPSet(object):
|
||||
|
||||
def query_entry(self, name, entry):
|
||||
obj = self.get_ipset(name, applied=True)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
if "timeout" in obj.options and obj.options["timeout"] != "0":
|
||||
# no entries visible for ipsets with timeout
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT, name)
|
||||
@@ -237,6 +241,11 @@ class FirewallIPSet(object):
|
||||
def set_entries(self, name, entries):
|
||||
obj = self.get_ipset(name, applied=True)
|
||||
|
||||
+ _entries = set()
|
||||
+ for _entry in entries:
|
||||
+ _entries.add(normalize_ipset_entry(_entry))
|
||||
+ entries = list(_entries)
|
||||
+
|
||||
for entry in entries:
|
||||
IPSet.check_entry(entry, obj.options, obj.type)
|
||||
if "timeout" not in obj.options or obj.options["timeout"] == "0":
|
||||
diff --git a/src/firewall/core/ipset.py b/src/firewall/core/ipset.py
|
||||
index 0d632143ce13..5bb21856f648 100644
|
||||
--- a/src/firewall/core/ipset.py
|
||||
+++ b/src/firewall/core/ipset.py
|
||||
@@ -24,6 +24,7 @@
|
||||
__all__ = [ "ipset", "check_ipset_name", "remove_default_create_options" ]
|
||||
|
||||
import os.path
|
||||
+import ipaddress
|
||||
|
||||
from firewall import errors
|
||||
from firewall.errors import FirewallError
|
||||
@@ -289,3 +290,15 @@ def remove_default_create_options(options):
|
||||
IPSET_DEFAULT_CREATE_OPTIONS[opt] == _options[opt]:
|
||||
del _options[opt]
|
||||
return _options
|
||||
+
|
||||
+def normalize_ipset_entry(entry):
|
||||
+ """ Normalize IP addresses in entry """
|
||||
+ _entry = []
|
||||
+ for _part in entry.split(","):
|
||||
+ try:
|
||||
+ _part.index("/")
|
||||
+ _entry.append(str(ipaddress.ip_network(_part, strict=False)))
|
||||
+ except ValueError:
|
||||
+ _entry.append(_part)
|
||||
+
|
||||
+ return ",".join(_entry)
|
||||
diff --git a/src/firewall/server/config_ipset.py b/src/firewall/server/config_ipset.py
|
||||
index 8c647bc29ab9..18ef5783de62 100644
|
||||
--- a/src/firewall/server/config_ipset.py
|
||||
+++ b/src/firewall/server/config_ipset.py
|
||||
@@ -33,7 +33,7 @@ from firewall.dbus_utils import dbus_to_python, \
|
||||
dbus_introspection_prepare_properties, \
|
||||
dbus_introspection_add_properties
|
||||
from firewall.core.io.ipset import IPSet
|
||||
-from firewall.core.ipset import IPSET_TYPES
|
||||
+from firewall.core.ipset import IPSET_TYPES, normalize_ipset_entry
|
||||
from firewall.core.logger import log
|
||||
from firewall.server.decorators import handle_exceptions, \
|
||||
dbus_handle_exceptions, dbus_service_method
|
||||
@@ -406,7 +406,10 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
|
||||
in_signature='as')
|
||||
@dbus_handle_exceptions
|
||||
def setEntries(self, entries, sender=None):
|
||||
- entries = dbus_to_python(entries, list)
|
||||
+ _entries = set()
|
||||
+ for _entry in dbus_to_python(entries, list):
|
||||
+ _entries.add(normalize_ipset_entry(_entry))
|
||||
+ entries = list(_entries)
|
||||
log.debug1("%s.setEntries('[%s]')", self._log_prefix,
|
||||
",".join(entries))
|
||||
self.parent.accessCheck(sender)
|
||||
@@ -421,6 +424,7 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
|
||||
@dbus_handle_exceptions
|
||||
def addEntry(self, entry, sender=None):
|
||||
entry = dbus_to_python(entry, str)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
log.debug1("%s.addEntry('%s')", self._log_prefix, entry)
|
||||
self.parent.accessCheck(sender)
|
||||
settings = list(self.getSettings())
|
||||
@@ -436,6 +440,7 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
|
||||
@dbus_handle_exceptions
|
||||
def removeEntry(self, entry, sender=None):
|
||||
entry = dbus_to_python(entry, str)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
log.debug1("%s.removeEntry('%s')", self._log_prefix, entry)
|
||||
self.parent.accessCheck(sender)
|
||||
settings = list(self.getSettings())
|
||||
@@ -451,6 +456,7 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
|
||||
@dbus_handle_exceptions
|
||||
def queryEntry(self, entry, sender=None): # pylint: disable=W0613
|
||||
entry = dbus_to_python(entry, str)
|
||||
+ entry = normalize_ipset_entry(entry)
|
||||
log.debug1("%s.queryEntry('%s')", self._log_prefix, entry)
|
||||
settings = list(self.getSettings())
|
||||
if "timeout" in settings[4] and settings[4]["timeout"] != "0":
|
||||
diff --git a/src/tests/regression/rhbz1601610.at b/src/tests/regression/rhbz1601610.at
|
||||
index ede2c45b88c1..a716539a8acf 100644
|
||||
--- a/src/tests/regression/rhbz1601610.at
|
||||
+++ b/src/tests/regression/rhbz1601610.at
|
||||
@@ -6,11 +6,14 @@ CHECK_IPSET
|
||||
FWD_CHECK([-q --new-ipset=foobar --permanent --type=hash:net])
|
||||
FWD_RELOAD
|
||||
|
||||
-FWD_CHECK([-q --ipset=foobar --add-entry=10.1.1.0/22])
|
||||
-FWD_CHECK([-q --ipset=foobar --add-entry=10.1.2.0/22], 13, ignore, ignore)
|
||||
-FWD_CHECK([-q --ipset=foobar --add-entry=10.2.0.0/22])
|
||||
+FWD_CHECK([--ipset=foobar --add-entry=10.1.1.0/22], 0, [ignore])
|
||||
+FWD_CHECK([--ipset=foobar --query-entry 10.1.2.0/22], 0, [ignore])
|
||||
+FWD_CHECK([--ipset=foobar --add-entry=10.1.2.0/22], 0, [ignore], [dnl
|
||||
+Warning: ALREADY_ENABLED: '10.1.0.0/22' already is in 'foobar'
|
||||
+])
|
||||
+FWD_CHECK([--ipset=foobar --add-entry=10.2.0.0/22], 0, [ignore])
|
||||
FWD_CHECK([--ipset=foobar --get-entries], 0, [dnl
|
||||
-10.1.1.0/22
|
||||
+10.1.0.0/22
|
||||
10.2.0.0/22
|
||||
])
|
||||
NFT_LIST_SET([foobar], 0, [dnl
|
||||
@@ -31,6 +34,9 @@ Members:
|
||||
])
|
||||
|
||||
FWD_CHECK([-q --ipset=foobar --remove-entry=10.1.1.0/22])
|
||||
+FWD_CHECK([--ipset=foobar --query-entry 10.1.1.0/22], 1, [ignore])
|
||||
+FWD_CHECK([--ipset=foobar --query-entry 10.1.2.0/22], 1, [ignore])
|
||||
+FWD_CHECK([--ipset=foobar --query-entry 10.2.0.0/22], 0, [ignore])
|
||||
FWD_CHECK([--ipset=foobar --get-entries], 0, [dnl
|
||||
10.2.0.0/22
|
||||
])
|
||||
@@ -52,7 +58,7 @@ Members:
|
||||
|
||||
FWD_CHECK([-q --permanent --ipset=foobar --add-entry=10.1.1.0/22])
|
||||
FWD_CHECK([--permanent --ipset=foobar --get-entries], 0, [dnl
|
||||
-10.1.1.0/22
|
||||
+10.1.0.0/22
|
||||
])
|
||||
FWD_CHECK([-q --permanent --ipset=foobar --remove-entry=10.1.1.0/22])
|
||||
FWD_CHECK([--permanent --ipset=foobar --get-entries], 0, [
|
||||
@@ -101,4 +107,5 @@ Members:
|
||||
|
||||
FWD_END_TEST([-e '/ERROR: COMMAND_FAILED:.*already added.*/d'dnl
|
||||
-e '/ERROR: COMMAND_FAILED:.*element.*exists/d'dnl
|
||||
- -e '/Kernel support protocol versions/d'])
|
||||
+ -e '/Kernel support protocol versions/d'dnl
|
||||
+ -e '/WARNING: ALREADY_ENABLED:/d'])
|
||||
--
|
||||
2.39.1
|
||||
|
@ -1,32 +1,32 @@
|
||||
From 2e34d7361f8a7528f5e5d86f794bc87c94f8214e Mon Sep 17 00:00:00 2001
|
||||
From 6069c94b5033fc82d8f35f2068a61374559d22de Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 30 Jan 2023 14:43:18 -0500
|
||||
Subject: [PATCH 30/30] v2.0.0: test(direct): avoid iptables flush if using
|
||||
Subject: [PATCH 4/4] v1.4.0: test(direct): avoid iptables flush if using
|
||||
nftables backend
|
||||
|
||||
Coverage: #863
|
||||
(cherry picked from commit dcd0dd3674ea8ef757a1b41f6b53717a45e821aa)
|
||||
---
|
||||
src/tests/features/features.at | 1 +
|
||||
.../features/iptables_no_flush_on_shutdown.at | 143 ++++++++++++++++++
|
||||
2 files changed, 144 insertions(+)
|
||||
.../features/iptables_no_flush_on_shutdown.at | 144 ++++++++++++++++++
|
||||
2 files changed, 145 insertions(+)
|
||||
create mode 100644 src/tests/features/iptables_no_flush_on_shutdown.at
|
||||
|
||||
diff --git a/src/tests/features/features.at b/src/tests/features/features.at
|
||||
index 381bf6dba0e4..cfe8e88b46a9 100644
|
||||
index 78fe78c483ad..f59baea1cd70 100644
|
||||
--- a/src/tests/features/features.at
|
||||
+++ b/src/tests/features/features.at
|
||||
@@ -14,3 +14,4 @@ m4_include([features/icmp_blocks.at])
|
||||
m4_include([features/rpfilter.at])
|
||||
m4_include([features/zone_combine.at])
|
||||
m4_include([features/rich_destination_ipset.at])
|
||||
@@ -19,3 +19,4 @@ m4_include([features/zone_combine.at])
|
||||
m4_include([features/startup_failsafe.at])
|
||||
m4_include([features/ipset.at])
|
||||
m4_include([features/reset_defaults.at])
|
||||
+m4_include([features/iptables_no_flush_on_shutdown.at])
|
||||
diff --git a/src/tests/features/iptables_no_flush_on_shutdown.at b/src/tests/features/iptables_no_flush_on_shutdown.at
|
||||
new file mode 100644
|
||||
index 000000000000..a3bb1395791d
|
||||
index 000000000000..fbd7c793375c
|
||||
--- /dev/null
|
||||
+++ b/src/tests/features/iptables_no_flush_on_shutdown.at
|
||||
@@ -0,0 +1,143 @@
|
||||
@@ -0,0 +1,144 @@
|
||||
+m4_if(nftables, FIREWALL_BACKEND, [
|
||||
+
|
||||
+dnl If FirewallBackend=nftables, and there are no --direct rules, then we can
|
||||
@ -35,6 +35,7 @@ index 000000000000..a3bb1395791d
|
||||
+dnl first direct rule added.
|
||||
+FWD_START_TEST([avoid iptables flush if using nftables])
|
||||
+AT_KEYWORDS(direct gh863)
|
||||
+CHECK_IPTABLES()
|
||||
+
|
||||
+dnl no flush on reload if no direct rules
|
||||
+NS_CHECK([$IPTABLES -t filter -N firewalld_testsuite])
|
||||
@ -46,20 +47,20 @@ index 000000000000..a3bb1395791d
|
||||
+NS_CHECK([$EBTABLES -t filter -N firewalld_testsuite])
|
||||
+NS_CHECK([$EBTABLES -t filter -I firewalld_testsuite -j ACCEPT])
|
||||
+IPTABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
|
||||
+ ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
|
||||
+])
|
||||
+IP6TABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all ::/0 ::/0
|
||||
+ ACCEPT 0 -- ::/0 ::/0
|
||||
+])
|
||||
+EBTABLES_LIST_RULES([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ -j ACCEPT
|
||||
+])
|
||||
+FWD_RELOAD()
|
||||
+IPTABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
|
||||
+ ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
|
||||
+])
|
||||
+IP6TABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all ::/0 ::/0
|
||||
+ ACCEPT 0 -- ::/0 ::/0
|
||||
+])
|
||||
+EBTABLES_LIST_RULES([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ -j ACCEPT
|
||||
@ -68,10 +69,10 @@ index 000000000000..a3bb1395791d
|
||||
+dnl no flush on restart (or stop) if no direct rules
|
||||
+FWD_RESTART()
|
||||
+IPTABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
|
||||
+ ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
|
||||
+])
|
||||
+IP6TABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all ::/0 ::/0
|
||||
+ ACCEPT 0 -- ::/0 ::/0
|
||||
+])
|
||||
+EBTABLES_LIST_RULES([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ -j ACCEPT
|
||||
@ -83,7 +84,7 @@ index 000000000000..a3bb1395791d
|
||||
+IP6TABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 1, [ignore], [ignore])
|
||||
+EBTABLES_LIST_RULES([filter], [firewalld_testsuite], 1, [ignore], [ignore])
|
||||
+IPTABLES_LIST_RULES_ALWAYS([filter], [INPUT], 0, [dnl
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
|
||||
+ ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
|
||||
+])
|
||||
+NS_CHECK([$IPTABLES -t filter -N firewalld_testsuite])
|
||||
+NS_CHECK([$IPTABLES -t filter -I firewalld_testsuite -j ACCEPT])
|
||||
@ -94,10 +95,10 @@ index 000000000000..a3bb1395791d
|
||||
+NS_CHECK([$EBTABLES -t filter -N firewalld_testsuite])
|
||||
+NS_CHECK([$EBTABLES -t filter -I firewalld_testsuite -j ACCEPT])
|
||||
+IPTABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
|
||||
+ ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
|
||||
+])
|
||||
+IP6TABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ ACCEPT all ::/0 ::/0
|
||||
+ ACCEPT 0 -- ::/0 ::/0
|
||||
+])
|
||||
+EBTABLES_LIST_RULES([filter], [firewalld_testsuite], 0, [dnl
|
||||
+ -j ACCEPT
|
||||
@ -126,7 +127,7 @@ index 000000000000..a3bb1395791d
|
||||
+FWD_RELOAD()
|
||||
+IPTABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 1, [ignore], [ignore])
|
||||
+IPTABLES_LIST_RULES_ALWAYS([filter], [INPUT], 0, [dnl
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
|
||||
+ ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
|
||||
+])
|
||||
+IP6TABLES_LIST_RULES_ALWAYS([filter], [firewalld_testsuite], 1, [ignore], [ignore])
|
||||
+IP6TABLES_LIST_RULES_ALWAYS([filter], [INPUT], 0, [dnl
|
||||
@ -171,5 +172,5 @@ index 000000000000..a3bb1395791d
|
||||
+
|
||||
+])
|
||||
--
|
||||
2.43.0
|
||||
2.39.3
|
||||
|
@ -1,157 +0,0 @@
|
||||
From bba9a6860dd358791d0be3f075718d7cf8dca261 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 23 Feb 2021 09:18:33 -0500
|
||||
Subject: [PATCH 05/10] v1.0.0: fix(ipset): disallow overlapping entries
|
||||
|
||||
These are already being blocked by the ipset backend, but we should
|
||||
catch them higher up to avoid differences in the backends.
|
||||
|
||||
(cherry picked from commit 5b4e8918715a1d2e4abf77ed4eb3252486a19109)
|
||||
---
|
||||
src/firewall/client.py | 4 +++-
|
||||
src/firewall/core/fw_ipset.py | 4 +++-
|
||||
src/firewall/core/ipset.py | 13 +++++++++++++
|
||||
src/firewall/server/config_ipset.py | 5 ++++-
|
||||
src/tests/regression/ipset_netmask_allowed.at | 14 ++++++++------
|
||||
5 files changed, 31 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/client.py b/src/firewall/client.py
|
||||
index aa6bd7cd282b..3715ffd29316 100644
|
||||
--- a/src/firewall/client.py
|
||||
+++ b/src/firewall/client.py
|
||||
@@ -34,7 +34,7 @@ from firewall.core.base import DEFAULT_ZONE_TARGET, DEFAULT_POLICY_TARGET, DEFAU
|
||||
from firewall.dbus_utils import dbus_to_python
|
||||
from firewall.functions import b2u
|
||||
from firewall.core.rich import Rich_Rule
|
||||
-from firewall.core.ipset import normalize_ipset_entry
|
||||
+from firewall.core.ipset import normalize_ipset_entry, check_entry_overlaps_existing
|
||||
from firewall import errors
|
||||
from firewall.errors import FirewallError
|
||||
|
||||
@@ -1619,6 +1619,7 @@ class FirewallClientIPSetSettings(object):
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
_entries = set()
|
||||
for _entry in dbus_to_python(entries, list):
|
||||
+ check_entry_overlaps_existing(_entry, _entries)
|
||||
_entries.add(normalize_ipset_entry(_entry))
|
||||
self.settings[5] = list(_entries)
|
||||
@handle_exceptions
|
||||
@@ -1628,6 +1629,7 @@ class FirewallClientIPSetSettings(object):
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
entry = normalize_ipset_entry(entry)
|
||||
if entry not in self.settings[5]:
|
||||
+ check_entry_overlaps_existing(entry, self.settings[5])
|
||||
self.settings[5].append(entry)
|
||||
else:
|
||||
raise FirewallError(errors.ALREADY_ENABLED, entry)
|
||||
diff --git a/src/firewall/core/fw_ipset.py b/src/firewall/core/fw_ipset.py
|
||||
index 57e0e6cb51db..711c86a062be 100644
|
||||
--- a/src/firewall/core/fw_ipset.py
|
||||
+++ b/src/firewall/core/fw_ipset.py
|
||||
@@ -25,7 +25,7 @@ __all__ = [ "FirewallIPSet" ]
|
||||
|
||||
from firewall.core.logger import log
|
||||
from firewall.core.ipset import remove_default_create_options as rm_def_cr_opts, \
|
||||
- normalize_ipset_entry
|
||||
+ normalize_ipset_entry, check_entry_overlaps_existing
|
||||
from firewall.core.io.ipset import IPSet
|
||||
from firewall import errors
|
||||
from firewall.errors import FirewallError
|
||||
@@ -196,6 +196,7 @@ class FirewallIPSet(object):
|
||||
if entry in obj.entries:
|
||||
raise FirewallError(errors.ALREADY_ENABLED,
|
||||
"'%s' already is in '%s'" % (entry, name))
|
||||
+ check_entry_overlaps_existing(entry, obj.entries)
|
||||
|
||||
try:
|
||||
for backend in self.backends():
|
||||
@@ -243,6 +244,7 @@ class FirewallIPSet(object):
|
||||
|
||||
_entries = set()
|
||||
for _entry in entries:
|
||||
+ check_entry_overlaps_existing(_entry, _entries)
|
||||
_entries.add(normalize_ipset_entry(_entry))
|
||||
entries = list(_entries)
|
||||
|
||||
diff --git a/src/firewall/core/ipset.py b/src/firewall/core/ipset.py
|
||||
index 5bb21856f648..d6defa395241 100644
|
||||
--- a/src/firewall/core/ipset.py
|
||||
+++ b/src/firewall/core/ipset.py
|
||||
@@ -302,3 +302,16 @@ def normalize_ipset_entry(entry):
|
||||
_entry.append(_part)
|
||||
|
||||
return ",".join(_entry)
|
||||
+
|
||||
+def check_entry_overlaps_existing(entry, entries):
|
||||
+ """ Check if entry overlaps any entry in the list of entries """
|
||||
+ # Only check simple types
|
||||
+ if len(entry.split(",")) > 1:
|
||||
+ return
|
||||
+
|
||||
+ for itr in entries:
|
||||
+ try:
|
||||
+ if ipaddress.ip_network(itr, strict=False).overlaps(ipaddress.ip_network(entry, strict=False)):
|
||||
+ raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps with existing entry '{}'".format(itr, entry))
|
||||
+ except ValueError:
|
||||
+ pass
|
||||
diff --git a/src/firewall/server/config_ipset.py b/src/firewall/server/config_ipset.py
|
||||
index 18ef5783de62..f33c2a02926f 100644
|
||||
--- a/src/firewall/server/config_ipset.py
|
||||
+++ b/src/firewall/server/config_ipset.py
|
||||
@@ -33,7 +33,8 @@ from firewall.dbus_utils import dbus_to_python, \
|
||||
dbus_introspection_prepare_properties, \
|
||||
dbus_introspection_add_properties
|
||||
from firewall.core.io.ipset import IPSet
|
||||
-from firewall.core.ipset import IPSET_TYPES, normalize_ipset_entry
|
||||
+from firewall.core.ipset import IPSET_TYPES, normalize_ipset_entry, \
|
||||
+ check_entry_overlaps_existing
|
||||
from firewall.core.logger import log
|
||||
from firewall.server.decorators import handle_exceptions, \
|
||||
dbus_handle_exceptions, dbus_service_method
|
||||
@@ -408,6 +409,7 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
|
||||
def setEntries(self, entries, sender=None):
|
||||
_entries = set()
|
||||
for _entry in dbus_to_python(entries, list):
|
||||
+ check_entry_overlaps_existing(_entry, _entries)
|
||||
_entries.add(normalize_ipset_entry(_entry))
|
||||
entries = list(_entries)
|
||||
log.debug1("%s.setEntries('[%s]')", self._log_prefix,
|
||||
@@ -432,6 +434,7 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
if entry in settings[5]:
|
||||
raise FirewallError(errors.ALREADY_ENABLED, entry)
|
||||
+ check_entry_overlaps_existing(entry, settings[5])
|
||||
settings[5].append(entry)
|
||||
self.update(settings)
|
||||
|
||||
diff --git a/src/tests/regression/ipset_netmask_allowed.at b/src/tests/regression/ipset_netmask_allowed.at
|
||||
index b5165d94b220..fd08afd3b57c 100644
|
||||
--- a/src/tests/regression/ipset_netmask_allowed.at
|
||||
+++ b/src/tests/regression/ipset_netmask_allowed.at
|
||||
@@ -9,15 +9,17 @@ dnl an add for the whole range. i.e. 1.2.3.4/24 --> 1.2.3.[0.255] (256
|
||||
dnl entries).
|
||||
dnl
|
||||
dnl In nftables, we allow this by using actual intervals.
|
||||
-FWD_CHECK([--permanent --ipset foobar --add-entry 1.2.3.0/24], 0, [ignore])
|
||||
-FWD_CHECK([ --ipset foobar --add-entry 1.2.3.0/24], 0, [ignore])
|
||||
+FWD_CHECK([--permanent --ipset foobar --add-entry 1.2.3.4/24], 0, [ignore])
|
||||
+FWD_CHECK([ --ipset foobar --add-entry 1.2.3.4/24], 0, [ignore])
|
||||
|
||||
dnl check the edge case
|
||||
FWD_CHECK([--permanent --ipset foobar --add-entry 4.3.2.1/32], 0, [ignore])
|
||||
FWD_CHECK([ --ipset foobar --add-entry 4.3.2.1/32], 0, [ignore])
|
||||
|
||||
-dnl overlaps should be denied by ipset
|
||||
-FWD_CHECK([ --ipset foobar --add-entry 1.2.3.0/22], 13, [ignore], [ignore])
|
||||
-FWD_CHECK([ --ipset foobar --add-entry 1.2.3.0/30], 13, [ignore], [ignore])
|
||||
+dnl overlaps should be denied
|
||||
+FWD_CHECK([--permanent --ipset foobar --add-entry 1.2.3.0/22], 136, [ignore], [ignore])
|
||||
+FWD_CHECK([ --ipset foobar --add-entry 1.2.3.0/22], 136, [ignore], [ignore])
|
||||
+FWD_CHECK([--permanent --ipset foobar --add-entry 1.2.3.4/30], 136, [ignore], [ignore])
|
||||
+FWD_CHECK([ --ipset foobar --add-entry 1.2.3.4/30], 136, [ignore], [ignore])
|
||||
|
||||
-FWD_END_TEST([-e '/ERROR: COMMAND_FAILED:/d'])
|
||||
+FWD_END_TEST([-e '/ERROR: INVALID_ENTRY:/d'])
|
||||
--
|
||||
2.39.1
|
||||
|
@ -0,0 +1,53 @@
|
||||
From a27f6afa21de35aa98e5309430dbcab9e6056f9c Mon Sep 17 00:00:00 2001
|
||||
From: Pat Riehecky <riehecky@fnal.gov>
|
||||
Date: Wed, 1 Feb 2023 09:52:43 -0600
|
||||
Subject: [PATCH 05/22] v2.0.0: feat(service): add OpenTelemetry (OTLP) service
|
||||
|
||||
(cherry picked from commit 77c7061cc191bec6d8a36d2666c2d3c3e0ccbb4a)
|
||||
---
|
||||
config/Makefile.am | 1 +
|
||||
config/services/opentelemetry.xml | 7 +++++++
|
||||
po/POTFILES.in | 1 +
|
||||
3 files changed, 9 insertions(+)
|
||||
create mode 100644 config/services/opentelemetry.xml
|
||||
|
||||
diff --git a/config/Makefile.am b/config/Makefile.am
|
||||
index d66398563ff2..47f30c1566e0 100644
|
||||
--- a/config/Makefile.am
|
||||
+++ b/config/Makefile.am
|
||||
@@ -247,6 +247,7 @@ CONFIG_FILES = \
|
||||
services/ntp.xml \
|
||||
services/nut.xml \
|
||||
services/openvpn.xml \
|
||||
+ services/opentelemetry.xml \
|
||||
services/ovirt-imageio.xml \
|
||||
services/ovirt-storageconsole.xml \
|
||||
services/ovirt-vmconsole.xml \
|
||||
diff --git a/config/services/opentelemetry.xml b/config/services/opentelemetry.xml
|
||||
new file mode 100644
|
||||
index 000000000000..46c0e5258957
|
||||
--- /dev/null
|
||||
+++ b/config/services/opentelemetry.xml
|
||||
@@ -0,0 +1,7 @@
|
||||
+<?xml version="1.0" encoding="utf-8"?>
|
||||
+<service>
|
||||
+ <short>OTLP</short>
|
||||
+ <description>OpenTelemetry Protocol (OTLP) specification describes the encoding, transport, and delivery mechanism of telemetry data between telemetry sources, intermediate nodes such as collectors and telemetry backends.</description>
|
||||
+ <port protocol="tcp" port="4317"/>
|
||||
+ <port protocol="tcp" port="4318"/>
|
||||
+</service>
|
||||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||||
index f3c0595980f9..1c990542ac4d 100644
|
||||
--- a/po/POTFILES.in
|
||||
+++ b/po/POTFILES.in
|
||||
@@ -180,6 +180,7 @@ config/services/nrpe.xml
|
||||
config/services/ntp.xml
|
||||
config/services/nut.xml
|
||||
config/services/openvpn.xml
|
||||
+config/services/opentelemetry.xml
|
||||
config/services/ovirt-imageio.xml
|
||||
config/services/ovirt-storageconsole.xml
|
||||
config/services/ovirt-vmconsole.xml
|
||||
--
|
||||
2.43.5
|
||||
|
@ -1,302 +0,0 @@
|
||||
From 4779d5bf08ff1c24777df4b88b4af2e8e5918f84 Mon Sep 17 00:00:00 2001
|
||||
From: Paul Laufer <50234787+refual@users.noreply.github.com>
|
||||
Date: Fri, 27 Nov 2020 12:23:11 +0100
|
||||
Subject: [PATCH 06/10] v1.0.0: feat(config): add CleanupModulesOnExit
|
||||
configuration option
|
||||
|
||||
Fixes: rhbz 1520532
|
||||
Fixes: #533
|
||||
Closes: #721
|
||||
(cherry picked from commit 152a51537a7840afd0879ab4b60178bef4ec16a2)
|
||||
---
|
||||
config/firewalld.conf | 9 +++++++-
|
||||
doc/xml/firewalld.conf.xml | 11 ++++++++++
|
||||
doc/xml/firewalld.dbus.xml | 9 ++++++++
|
||||
src/firewall/config/__init__.py.in | 1 +
|
||||
src/firewall/core/fw.py | 29 +++++++++++++++++++-------
|
||||
src/firewall/core/io/firewalld_conf.py | 19 +++++++++++++----
|
||||
src/firewall/server/config.py | 23 +++++++++++++-------
|
||||
src/tests/dbus/firewalld.conf.at | 2 ++
|
||||
8 files changed, 82 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index a0556c0bbf5b..3abbc9c998c1 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -7,10 +7,17 @@ DefaultZone=public
|
||||
|
||||
# Clean up on exit
|
||||
# If set to no or false the firewall configuration will not get cleaned up
|
||||
-# on exit or stop of firewalld
|
||||
+# on exit or stop of firewalld.
|
||||
# Default: yes
|
||||
CleanupOnExit=yes
|
||||
|
||||
+# Clean up kernel modules on exit
|
||||
+# If set to yes or true the firewall related kernel modules will be
|
||||
+# unloaded on exit or stop of firewalld. This might attempt to unload
|
||||
+# modules not originally loaded by firewalld.
|
||||
+# Default: no
|
||||
+CleanupModulesOnExit=no
|
||||
+
|
||||
# Lockdown
|
||||
# If set to enabled, firewall changes with the D-Bus interface will be limited
|
||||
# to applications that are listed in the lockdown whitelist.
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index 0a6e8f2fdebf..3ae531bcd94a 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -88,6 +88,17 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
+ <varlistentry>
|
||||
+ <term><option>CleanupModulesOnExit</option></term>
|
||||
+ <listitem>
|
||||
+ <para>
|
||||
+ Setting this option to yes or true unloads all firewall-related
|
||||
+ kernel modules when firewalld is stopped. The default value is no
|
||||
+ or false.
|
||||
+ </para>
|
||||
+ </listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
<varlistentry>
|
||||
<term><option>CleanupOnExit</option></term>
|
||||
<listitem>
|
||||
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
|
||||
index 1c33ad5ee918..cc4593e1883f 100644
|
||||
--- a/doc/xml/firewalld.dbus.xml
|
||||
+++ b/doc/xml/firewalld.dbus.xml
|
||||
@@ -2798,6 +2798,15 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
+ <varlistentry id="FirewallD1.config.Properties.CleanupModulesOnExit">
|
||||
+ <term>CleanupModulesOnExit - s - (rw)</term>
|
||||
+ <listitem>
|
||||
+ <para>
|
||||
+ Setting this option to yes or true unloads all firewall-related
|
||||
+ kernel modules when firewalld is stopped.
|
||||
+ </para>
|
||||
+ </listitem>
|
||||
+ </varlistentry>
|
||||
<varlistentry id="FirewallD1.config.Properties.CleanupOnExit">
|
||||
<term>CleanupOnExit - s - (rw)</term>
|
||||
<listitem>
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index 0dec7913f694..5d6d769fbf15 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -125,6 +125,7 @@ FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
|
||||
FALLBACK_ZONE = "public"
|
||||
FALLBACK_MINIMAL_MARK = 100
|
||||
FALLBACK_CLEANUP_ON_EXIT = True
|
||||
+FALLBACK_CLEANUP_MODULES_ON_EXIT = False
|
||||
FALLBACK_LOCKDOWN = False
|
||||
FALLBACK_IPV6_RPFILTER = True
|
||||
FALLBACK_INDIVIDUAL_CALLS = False
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index 3eb54e37ab5c..4171697bdb94 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -105,12 +105,13 @@ class Firewall(object):
|
||||
self.__init_vars()
|
||||
|
||||
def __repr__(self):
|
||||
- return '%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)' % \
|
||||
+ return '%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)' % \
|
||||
(self.__class__, self.ip4tables_enabled, self.ip6tables_enabled,
|
||||
self.ebtables_enabled, self._state, self._panic,
|
||||
self._default_zone, self._module_refcount, self._marks,
|
||||
- self.cleanup_on_exit, self.ipv6_rpfilter_enabled,
|
||||
- self.ipset_enabled, self._individual_calls, self._log_denied)
|
||||
+ self.cleanup_on_exit, self.cleanup_modules_on_exit,
|
||||
+ self.ipv6_rpfilter_enabled, self.ipset_enabled,
|
||||
+ self._individual_calls, self._log_denied)
|
||||
|
||||
def __init_vars(self):
|
||||
self._state = "INIT"
|
||||
@@ -120,6 +121,7 @@ class Firewall(object):
|
||||
self._marks = [ ]
|
||||
# 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._individual_calls = config.FALLBACK_INDIVIDUAL_CALLS
|
||||
self._log_denied = config.FALLBACK_LOG_DENIED
|
||||
@@ -232,6 +234,13 @@ class Firewall(object):
|
||||
log.debug1("CleanupOnExit is set to '%s'",
|
||||
self.cleanup_on_exit)
|
||||
|
||||
+ if self._firewalld_conf.get("CleanupModulesOnExit"):
|
||||
+ value = self._firewalld_conf.get("CleanupModulesOnExit")
|
||||
+ if value is not None and value.lower() in [ "yes", "true" ]:
|
||||
+ self.cleanup_modules_on_exit = True
|
||||
+ log.debug1("CleanupModulesOnExit is set to '%s'",
|
||||
+ self.cleanup_modules_on_exit)
|
||||
+
|
||||
if self._firewalld_conf.get("Lockdown"):
|
||||
value = self._firewalld_conf.get("Lockdown")
|
||||
if value is not None and value.lower() in [ "yes", "true" ]:
|
||||
@@ -667,11 +676,15 @@ class Firewall(object):
|
||||
self.__init_vars()
|
||||
|
||||
def stop(self):
|
||||
- if self.cleanup_on_exit and not self._offline:
|
||||
- self.flush()
|
||||
- self.ipset.flush()
|
||||
- self.set_policy("ACCEPT")
|
||||
- self.modules_backend.unload_firewall_modules()
|
||||
+ if not self._offline:
|
||||
+ if self.cleanup_on_exit:
|
||||
+ self.flush()
|
||||
+ self.ipset.flush()
|
||||
+ self.set_policy("ACCEPT")
|
||||
+
|
||||
+ if self.cleanup_modules_on_exit:
|
||||
+ log.debug1('Unloading firewall kernel modules')
|
||||
+ self.modules_backend.unload_firewall_modules()
|
||||
|
||||
self.cleanup()
|
||||
|
||||
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
|
||||
index 7c7092120676..70258400ef06 100644
|
||||
--- a/src/firewall/core/io/firewalld_conf.py
|
||||
+++ b/src/firewall/core/io/firewalld_conf.py
|
||||
@@ -28,10 +28,11 @@ from firewall import config
|
||||
from firewall.core.logger import log
|
||||
from firewall.functions import b2u, u2b, PY2
|
||||
|
||||
-valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit", "Lockdown",
|
||||
- "IPv6_rpfilter", "IndividualCalls", "LogDenied",
|
||||
- "AutomaticHelpers", "FirewallBackend", "FlushAllOnReload",
|
||||
- "RFC3964_IPv4", "AllowZoneDrifting" ]
|
||||
+valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit",
|
||||
+ "CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
+ "IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
+ "FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
+ "AllowZoneDrifting" ]
|
||||
|
||||
class firewalld_conf(object):
|
||||
def __init__(self, filename):
|
||||
@@ -75,6 +76,7 @@ class firewalld_conf(object):
|
||||
self.set("DefaultZone", config.FALLBACK_ZONE)
|
||||
self.set("MinimalMark", str(config.FALLBACK_MINIMAL_MARK))
|
||||
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("IndividualCalls", "yes" if config.FALLBACK_INDIVIDUAL_CALLS else "no")
|
||||
@@ -135,6 +137,15 @@ class firewalld_conf(object):
|
||||
config.FALLBACK_CLEANUP_ON_EXIT)
|
||||
self.set("CleanupOnExit", "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no")
|
||||
|
||||
+ # check module cleanup on exit
|
||||
+ value = self.get("CleanupModulesOnExit")
|
||||
+ if not value or value.lower() not in [ "no", "false", "yes", "true" ]:
|
||||
+ if value is not None:
|
||||
+ log.warning("CleanupModulesOnExit '%s' is not valid, using default "
|
||||
+ "value %s", value if value else '',
|
||||
+ config.FALLBACK_CLEANUP_MODULES_ON_EXIT)
|
||||
+ self.set("CleanupModulesOnExit", "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no")
|
||||
+
|
||||
# check lockdown
|
||||
value = self.get("Lockdown")
|
||||
if not value or value.lower() not in [ "yes", "true", "no", "false" ]:
|
||||
diff --git a/src/firewall/server/config.py b/src/firewall/server/config.py
|
||||
index 031ef5d1afaa..8815920c6893 100644
|
||||
--- a/src/firewall/server/config.py
|
||||
+++ b/src/firewall/server/config.py
|
||||
@@ -100,6 +100,7 @@ class FirewallDConfig(slip.dbus.service.Object):
|
||||
dbus_introspection_prepare_properties(self,
|
||||
config.dbus.DBUS_INTERFACE_CONFIG,
|
||||
{ "CleanupOnExit": "readwrite",
|
||||
+ "CleanupModulesOnExit": "readwrite",
|
||||
"IPv6_rpfilter": "readwrite",
|
||||
"Lockdown": "readwrite",
|
||||
"MinimalMark": "readwrite",
|
||||
@@ -554,9 +555,9 @@ class FirewallDConfig(slip.dbus.service.Object):
|
||||
@dbus_handle_exceptions
|
||||
def _get_property(self, prop):
|
||||
if prop not in [ "DefaultZone", "MinimalMark", "CleanupOnExit",
|
||||
- "Lockdown", "IPv6_rpfilter", "IndividualCalls",
|
||||
- "LogDenied", "AutomaticHelpers", "FirewallBackend",
|
||||
- "FlushAllOnReload", "RFC3964_IPv4",
|
||||
+ "CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
+ "IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
+ "FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
"AllowZoneDrifting" ]:
|
||||
raise dbus.exceptions.DBusException(
|
||||
"org.freedesktop.DBus.Error.InvalidArgs: "
|
||||
@@ -578,6 +579,10 @@ class FirewallDConfig(slip.dbus.service.Object):
|
||||
if value is None:
|
||||
value = "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no"
|
||||
return dbus.String(value)
|
||||
+ elif prop == "CleanupModulesOnExit":
|
||||
+ if value is None:
|
||||
+ value = "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no"
|
||||
+ return dbus.String(value)
|
||||
elif prop == "Lockdown":
|
||||
if value is None:
|
||||
value = "yes" if config.FALLBACK_LOCKDOWN else "no"
|
||||
@@ -623,6 +628,8 @@ class FirewallDConfig(slip.dbus.service.Object):
|
||||
return dbus.Int32(self._get_property(prop))
|
||||
elif prop == "CleanupOnExit":
|
||||
return dbus.String(self._get_property(prop))
|
||||
+ elif prop == "CleanupModulesOnExit":
|
||||
+ return dbus.String(self._get_property(prop))
|
||||
elif prop == "Lockdown":
|
||||
return dbus.String(self._get_property(prop))
|
||||
elif prop == "IPv6_rpfilter":
|
||||
@@ -679,9 +686,9 @@ class FirewallDConfig(slip.dbus.service.Object):
|
||||
ret = { }
|
||||
if interface_name == config.dbus.DBUS_INTERFACE_CONFIG:
|
||||
for x in [ "DefaultZone", "MinimalMark", "CleanupOnExit",
|
||||
- "Lockdown", "IPv6_rpfilter", "IndividualCalls",
|
||||
- "LogDenied", "AutomaticHelpers", "FirewallBackend",
|
||||
- "FlushAllOnReload", "RFC3964_IPv4",
|
||||
+ "CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
+ "IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
+ "FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
"AllowZoneDrifting" ]:
|
||||
ret[x] = self._get_property(x)
|
||||
elif interface_name in [ config.dbus.DBUS_INTERFACE_CONFIG_DIRECT,
|
||||
@@ -706,12 +713,12 @@ class FirewallDConfig(slip.dbus.service.Object):
|
||||
self.accessCheck(sender)
|
||||
|
||||
if interface_name == config.dbus.DBUS_INTERFACE_CONFIG:
|
||||
- if property_name in [ "CleanupOnExit", "Lockdown",
|
||||
+ if property_name in [ "CleanupOnExit", "Lockdown", "CleanupModulesOnExit",
|
||||
"IPv6_rpfilter", "IndividualCalls",
|
||||
"LogDenied",
|
||||
"FirewallBackend", "FlushAllOnReload",
|
||||
"RFC3964_IPv4", "AllowZoneDrifting" ]:
|
||||
- if property_name in [ "CleanupOnExit", "Lockdown",
|
||||
+ if property_name in [ "CleanupOnExit", "Lockdown", "CleanupModulesOnExit",
|
||||
"IPv6_rpfilter", "IndividualCalls" ]:
|
||||
if new_value.lower() not in [ "yes", "no",
|
||||
"true", "false" ]:
|
||||
diff --git a/src/tests/dbus/firewalld.conf.at b/src/tests/dbus/firewalld.conf.at
|
||||
index 9fc5502a8d0b..9a04a3bd491c 100644
|
||||
--- a/src/tests/dbus/firewalld.conf.at
|
||||
+++ b/src/tests/dbus/firewalld.conf.at
|
||||
@@ -17,6 +17,7 @@ dnl Verify defaults over dbus. Should be inline with default firewalld.conf.
|
||||
DBUS_GETALL([config], [config], 0, [dnl
|
||||
string "AllowZoneDrifting" : variant string "no"
|
||||
string "AutomaticHelpers" : variant string "no"
|
||||
+string "CleanupModulesOnExit" : variant string "no"
|
||||
string "CleanupOnExit" : variant string "no"
|
||||
string "DefaultZone" : variant string "public"
|
||||
string "FirewallBackend" : variant string "nftables"
|
||||
@@ -45,6 +46,7 @@ _helper([IPv6_rpfilter], [string:"yes"], [variant string "yes"])
|
||||
_helper([IndividualCalls], [string:"yes"], [variant string "yes"])
|
||||
_helper([FirewallBackend], [string:"iptables"], [variant string "iptables"])
|
||||
_helper([FlushAllOnReload], [string:"no"], [variant string "no"])
|
||||
+_helper([CleanupModulesOnExit], [string:"yes"], [variant string "yes"])
|
||||
_helper([CleanupOnExit], [string:"yes"], [variant string "yes"])
|
||||
_helper([RFC3964_IPv4], [string:"no"], [variant string "no"])
|
||||
_helper([AllowZoneDrifting], [string:"yes"], [variant string "yes"])
|
||||
--
|
||||
2.39.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From b18ab581731a302ddba0428b685360d315293e73 Mon Sep 17 00:00:00 2001
|
||||
From 6f221d65193cda838e241a18dd07b6da2ae22f78 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Wed, 29 Nov 2023 17:02:07 +0100
|
||||
Subject: [PATCH 21/26] v2.1.0: feat(icmp): add ICMPv6 Multicast Listener
|
||||
Subject: [PATCH 06/22] v2.1.0: feat(icmp): add ICMPv6 Multicast Listener
|
||||
Discovery (MLD) types
|
||||
|
||||
Note that ip6tables does not support these ICMPv6 types. Currently,
|
||||
@ -30,7 +30,7 @@ name as from RFC 4890.
|
||||
create mode 100644 config/icmptypes/mld2-listener-report.xml
|
||||
|
||||
diff --git a/config/Makefile.am b/config/Makefile.am
|
||||
index f844a5a00e2f..a11c6abae583 100644
|
||||
index 47f30c1566e0..edae25fd9de0 100644
|
||||
--- a/config/Makefile.am
|
||||
+++ b/config/Makefile.am
|
||||
@@ -83,6 +83,10 @@ CONFIG_FILES = \
|
||||
@ -97,7 +97,7 @@ index 000000000000..faee68c95b20
|
||||
+ <destination ipv6="yes"/>
|
||||
+</icmptype>
|
||||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||||
index 249cff8d0d2f..3bb71fd3d332 100644
|
||||
index 1c990542ac4d..adeebdee3f55 100644
|
||||
--- a/po/POTFILES.in
|
||||
+++ b/po/POTFILES.in
|
||||
@@ -15,6 +15,10 @@ config/icmptypes/host-redirect.xml
|
||||
@ -112,7 +112,7 @@ index 249cff8d0d2f..3bb71fd3d332 100644
|
||||
config/icmptypes/neighbour-solicitation.xml
|
||||
config/icmptypes/network-prohibited.xml
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index d238451ebd5d..67fb6457e86c 100644
|
||||
index 6ad4b9168403..3df3fa3c3742 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -140,6 +140,10 @@ ICMP_TYPES_FRAGMENTS = {
|
||||
@ -127,5 +127,5 @@ index d238451ebd5d..67fb6457e86c 100644
|
||||
"neighbour-solicitation": _icmp_types_fragments("icmpv6", "nd-neighbor-solicit"),
|
||||
"no-route": _icmp_types_fragments("icmpv6", "destination-unreachable", 0),
|
||||
--
|
||||
2.43.0
|
||||
2.43.5
|
||||
|
@ -1,95 +0,0 @@
|
||||
From 82b49bd47d0073f2c2bc4bd296c1a52e4d4d3732 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <egarver@redhat.com>
|
||||
Date: Mon, 20 Dec 2021 13:56:55 -0500
|
||||
Subject: [PATCH 07/10] RHEL only: default to CleanupModulesOnExit=yes
|
||||
|
||||
Resolves: rhbz1980206
|
||||
---
|
||||
config/firewalld.conf | 4 ++--
|
||||
doc/xml/firewalld.conf.xml | 4 ++--
|
||||
src/firewall/config/__init__.py.in | 2 +-
|
||||
src/firewall/core/fw.py | 2 ++
|
||||
src/tests/dbus/firewalld.conf.at | 4 ++--
|
||||
5 files changed, 9 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index 3abbc9c998c1..c387f87c28be 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -15,8 +15,8 @@ CleanupOnExit=yes
|
||||
# If set to yes or true the firewall related kernel modules will be
|
||||
# unloaded on exit or stop of firewalld. This might attempt to unload
|
||||
# modules not originally loaded by firewalld.
|
||||
-# Default: no
|
||||
-CleanupModulesOnExit=no
|
||||
+# Default: yes
|
||||
+CleanupModulesOnExit=yes
|
||||
|
||||
# Lockdown
|
||||
# If set to enabled, firewall changes with the D-Bus interface will be limited
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index 3ae531bcd94a..c94073dbf84f 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -93,8 +93,8 @@
|
||||
<listitem>
|
||||
<para>
|
||||
Setting this option to yes or true unloads all firewall-related
|
||||
- kernel modules when firewalld is stopped. The default value is no
|
||||
- or false.
|
||||
+ kernel modules when firewalld is stopped. The default value is yes
|
||||
+ or true.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index 5d6d769fbf15..285e2f034b6b 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -125,7 +125,7 @@ FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
|
||||
FALLBACK_ZONE = "public"
|
||||
FALLBACK_MINIMAL_MARK = 100
|
||||
FALLBACK_CLEANUP_ON_EXIT = True
|
||||
-FALLBACK_CLEANUP_MODULES_ON_EXIT = False
|
||||
+FALLBACK_CLEANUP_MODULES_ON_EXIT = True
|
||||
FALLBACK_LOCKDOWN = False
|
||||
FALLBACK_IPV6_RPFILTER = True
|
||||
FALLBACK_INDIVIDUAL_CALLS = False
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index 4171697bdb94..5cef18b5f889 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -238,6 +238,8 @@ class Firewall(object):
|
||||
value = self._firewalld_conf.get("CleanupModulesOnExit")
|
||||
if value is not None and value.lower() in [ "yes", "true" ]:
|
||||
self.cleanup_modules_on_exit = True
|
||||
+ if value is not None and value.lower() in [ "no", "false" ]:
|
||||
+ self.cleanup_modules_on_exit = False
|
||||
log.debug1("CleanupModulesOnExit is set to '%s'",
|
||||
self.cleanup_modules_on_exit)
|
||||
|
||||
diff --git a/src/tests/dbus/firewalld.conf.at b/src/tests/dbus/firewalld.conf.at
|
||||
index 9a04a3bd491c..68832bca33bc 100644
|
||||
--- a/src/tests/dbus/firewalld.conf.at
|
||||
+++ b/src/tests/dbus/firewalld.conf.at
|
||||
@@ -17,7 +17,7 @@ dnl Verify defaults over dbus. Should be inline with default firewalld.conf.
|
||||
DBUS_GETALL([config], [config], 0, [dnl
|
||||
string "AllowZoneDrifting" : variant string "no"
|
||||
string "AutomaticHelpers" : variant string "no"
|
||||
-string "CleanupModulesOnExit" : variant string "no"
|
||||
+string "CleanupModulesOnExit" : variant string "yes"
|
||||
string "CleanupOnExit" : variant string "no"
|
||||
string "DefaultZone" : variant string "public"
|
||||
string "FirewallBackend" : variant string "nftables"
|
||||
@@ -46,7 +46,7 @@ _helper([IPv6_rpfilter], [string:"yes"], [variant string "yes"])
|
||||
_helper([IndividualCalls], [string:"yes"], [variant string "yes"])
|
||||
_helper([FirewallBackend], [string:"iptables"], [variant string "iptables"])
|
||||
_helper([FlushAllOnReload], [string:"no"], [variant string "no"])
|
||||
-_helper([CleanupModulesOnExit], [string:"yes"], [variant string "yes"])
|
||||
+_helper([CleanupModulesOnExit], [string:"no"], [variant string "no"])
|
||||
_helper([CleanupOnExit], [string:"yes"], [variant string "yes"])
|
||||
_helper([RFC3964_IPv4], [string:"no"], [variant string "no"])
|
||||
_helper([AllowZoneDrifting], [string:"yes"], [variant string "yes"])
|
||||
--
|
||||
2.39.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 5266735bf4827178ddd9a12edc37b1b0a93e0d3a Mon Sep 17 00:00:00 2001
|
||||
From 22b100b8ac9aeeacae851e2b9f11e4dc1741cd85 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Tue, 12 Dec 2023 14:58:07 +0100
|
||||
Subject: [PATCH 22/26] v2.1.0: fix(rich): validate service name of rich rule
|
||||
Subject: [PATCH 07/22] v2.1.0: fix(rich): validate service name of rich rule
|
||||
|
||||
Previously, validation of valid service names was not done.
|
||||
That meant:
|
||||
@ -21,30 +21,20 @@ Now:
|
||||
https://issues.redhat.com/browse/RHEL-5790
|
||||
(cherry picked from commit fbcdddd3e38c31a7b8325bf02764b84344c216b0)
|
||||
---
|
||||
src/firewall/core/io/policy.py | 11 +++++++++++
|
||||
src/tests/features/rich_rules.at | 8 +++++++-
|
||||
2 files changed, 18 insertions(+), 1 deletion(-)
|
||||
src/firewall/core/io/policy.py | 8 ++++++++
|
||||
src/tests/features/rich_rules.at | 7 ++++++-
|
||||
2 files changed, 14 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/firewall/core/io/policy.py b/src/firewall/core/io/policy.py
|
||||
index 3b951545e975..514a20251ef4 100644
|
||||
index 7d383abb0a2d..f9a1114d7969 100644
|
||||
--- a/src/firewall/core/io/policy.py
|
||||
+++ b/src/firewall/core/io/policy.py
|
||||
@@ -304,6 +304,8 @@ def common_endElement(obj, name):
|
||||
obj._limit_ok = None
|
||||
|
||||
def common_check_config(obj, config, item, all_config):
|
||||
+ obj_type = "Policy" if isinstance(obj, Policy) else "Zone"
|
||||
+
|
||||
if item == "services" and obj.fw_config:
|
||||
existing_services = obj.fw_config.get_services()
|
||||
for service in config:
|
||||
@@ -360,6 +362,15 @@ def common_check_config(obj, config, item, all_config):
|
||||
raise FirewallError(errors.INVALID_ICMPTYPE,
|
||||
"rich rule family '%s' conflicts with icmp type '%s'" % \
|
||||
(obj_rich.family, obj_rich.element.name))
|
||||
+ elif obj.fw_config and isinstance(obj_rich.element, rich.Rich_Service):
|
||||
+ existing_services = obj.fw_config.get_services()
|
||||
+ if obj_rich.element.name not in existing_services:
|
||||
@@ -471,6 +471,14 @@ def common_check_config(obj, config, item, all_config, all_io_objects):
|
||||
log.debug1("{} (unsupported)".format(ex))
|
||||
else:
|
||||
raise ex
|
||||
+ elif isinstance(obj_rich.element, rich.Rich_Service):
|
||||
+ if obj_rich.element.name not in all_io_objects["services"]:
|
||||
+ raise FirewallError(
|
||||
+ errors.INVALID_SERVICE,
|
||||
+ "{} '{}': '{}' not among existing services".format(
|
||||
@ -55,28 +45,27 @@ index 3b951545e975..514a20251ef4 100644
|
||||
def common_writer(obj, handler):
|
||||
# short
|
||||
diff --git a/src/tests/features/rich_rules.at b/src/tests/features/rich_rules.at
|
||||
index bb5e4b72a516..de98bf0ce268 100644
|
||||
index aadc76da57b4..f7d1a1d0abf4 100644
|
||||
--- a/src/tests/features/rich_rules.at
|
||||
+++ b/src/tests/features/rich_rules.at
|
||||
@@ -46,6 +46,11 @@ FWD_CHECK([--permanent --policy foobar --add-rich-rule='rule family=ipv4 priorit
|
||||
@@ -46,6 +46,10 @@ FWD_CHECK([--permanent --policy foobar --add-rich-rule='rule family=ipv4 priorit
|
||||
FWD_CHECK([--permanent --policy foobar --add-rich-rule='rule family=ipv4 priority=0 source address=10.10.10.13 drop'], 0, ignore)
|
||||
FWD_CHECK([--permanent --policy foobar --add-rich-rule='rule family=ipv4 priority=-1 source address=10.10.10.14 accept'], 0, ignore)
|
||||
FWD_CHECK([--permanent --policy foobar --add-rich-rule='rule family=ipv4 priority=1 source address=10.10.10.15 accept'], 0, ignore)
|
||||
+
|
||||
+dnl Invalid service name is rejected.
|
||||
+FWD_CHECK([--permanent --policy foobar --add-rich-rule='rule priority="-100" family="ipv4" source address="10.0.0.10" service name="bogusservice" accept'], 101, ignore, ignore)
|
||||
+FWD_CHECK([--policy foobar --add-rich-rule='rule priority="-100" family="ipv4" source address="10.0.0.10" service name="bogusservice" accept'], 101, ignore, ignore)
|
||||
+
|
||||
FWD_RELOAD
|
||||
NFT_LIST_RULES([inet], [filter_IN_policy_foobar_pre], 0, [dnl
|
||||
table inet firewalld {
|
||||
@@ -289,4 +294,5 @@ IP6TABLES_LIST_RULES([filter], [IN_foobar_post], 0, [dnl
|
||||
ACCEPT all ::/0 ::/0
|
||||
@@ -319,4 +323,5 @@ IP6TABLES_LIST_RULES([filter], [IN_foobar_post], 0, [dnl
|
||||
ACCEPT 0 -- ::/0 ::/0
|
||||
])
|
||||
|
||||
-FWD_END_TEST([-e '/ERROR: INVALID_ZONE:/d'])
|
||||
+FWD_END_TEST([-e '/ERROR: INVALID_ZONE:/d' dnl
|
||||
+ -e "/ERROR: INVALID_SERVICE: Policy 'foobar': 'bogusservice' not among existing services/d"])
|
||||
--
|
||||
2.43.0
|
||||
2.43.5
|
||||
|
@ -1,141 +0,0 @@
|
||||
From ae057df0222e6e1dd1556436fad93b669da8f653 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 30 Nov 2021 14:54:20 -0500
|
||||
Subject: [PATCH 08/10] v1.1.0: fix(ipset): reduce cost of entry overlap
|
||||
detection
|
||||
|
||||
This increases peak memory usage to reduce the duration it takes to
|
||||
apply the set entries. Building the list of IPv4Network objects up front
|
||||
means we don't have to build them multiple times inside the for loop.
|
||||
|
||||
Fixes: #881
|
||||
(cherry picked from commit 7f5b736378c0133f46470c42e0c1fb3b95087de5)
|
||||
---
|
||||
src/firewall/client.py | 10 ++++------
|
||||
src/firewall/core/fw_ipset.py | 9 +++------
|
||||
src/firewall/core/ipset.py | 27 ++++++++++++++++++++++-----
|
||||
src/firewall/server/config_ipset.py | 10 ++++------
|
||||
4 files changed, 33 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/client.py b/src/firewall/client.py
|
||||
index 3715ffd29316..fdc88ac7946b 100644
|
||||
--- a/src/firewall/client.py
|
||||
+++ b/src/firewall/client.py
|
||||
@@ -34,7 +34,8 @@ from firewall.core.base import DEFAULT_ZONE_TARGET, DEFAULT_POLICY_TARGET, DEFAU
|
||||
from firewall.dbus_utils import dbus_to_python
|
||||
from firewall.functions import b2u
|
||||
from firewall.core.rich import Rich_Rule
|
||||
-from firewall.core.ipset import normalize_ipset_entry, check_entry_overlaps_existing
|
||||
+from firewall.core.ipset import normalize_ipset_entry, check_entry_overlaps_existing, \
|
||||
+ check_for_overlapping_entries
|
||||
from firewall import errors
|
||||
from firewall.errors import FirewallError
|
||||
|
||||
@@ -1617,11 +1618,8 @@ class FirewallClientIPSetSettings(object):
|
||||
if "timeout" in self.settings[4] and \
|
||||
self.settings[4]["timeout"] != "0":
|
||||
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
|
||||
- _entries = set()
|
||||
- for _entry in dbus_to_python(entries, list):
|
||||
- check_entry_overlaps_existing(_entry, _entries)
|
||||
- _entries.add(normalize_ipset_entry(_entry))
|
||||
- self.settings[5] = list(_entries)
|
||||
+ check_for_overlapping_entries(entries)
|
||||
+ self.settings[5] = entries
|
||||
@handle_exceptions
|
||||
def addEntry(self, entry):
|
||||
if "timeout" in self.settings[4] and \
|
||||
diff --git a/src/firewall/core/fw_ipset.py b/src/firewall/core/fw_ipset.py
|
||||
index 711c86a062be..d4bf99eaadcc 100644
|
||||
--- a/src/firewall/core/fw_ipset.py
|
||||
+++ b/src/firewall/core/fw_ipset.py
|
||||
@@ -25,7 +25,8 @@ __all__ = [ "FirewallIPSet" ]
|
||||
|
||||
from firewall.core.logger import log
|
||||
from firewall.core.ipset import remove_default_create_options as rm_def_cr_opts, \
|
||||
- normalize_ipset_entry, check_entry_overlaps_existing
|
||||
+ normalize_ipset_entry, check_entry_overlaps_existing, \
|
||||
+ check_for_overlapping_entries
|
||||
from firewall.core.io.ipset import IPSet
|
||||
from firewall import errors
|
||||
from firewall.errors import FirewallError
|
||||
@@ -242,11 +243,7 @@ class FirewallIPSet(object):
|
||||
def set_entries(self, name, entries):
|
||||
obj = self.get_ipset(name, applied=True)
|
||||
|
||||
- _entries = set()
|
||||
- for _entry in entries:
|
||||
- check_entry_overlaps_existing(_entry, _entries)
|
||||
- _entries.add(normalize_ipset_entry(_entry))
|
||||
- entries = list(_entries)
|
||||
+ check_for_overlapping_entries(entries)
|
||||
|
||||
for entry in entries:
|
||||
IPSet.check_entry(entry, obj.options, obj.type)
|
||||
diff --git a/src/firewall/core/ipset.py b/src/firewall/core/ipset.py
|
||||
index d6defa395241..66ea4335536d 100644
|
||||
--- a/src/firewall/core/ipset.py
|
||||
+++ b/src/firewall/core/ipset.py
|
||||
@@ -309,9 +309,26 @@ def check_entry_overlaps_existing(entry, entries):
|
||||
if len(entry.split(",")) > 1:
|
||||
return
|
||||
|
||||
+ try:
|
||||
+ entry_network = ipaddress.ip_network(entry, strict=False)
|
||||
+ except ValueError:
|
||||
+ # could not parse the new IP address, maybe a MAC
|
||||
+ return
|
||||
+
|
||||
for itr in entries:
|
||||
- try:
|
||||
- if ipaddress.ip_network(itr, strict=False).overlaps(ipaddress.ip_network(entry, strict=False)):
|
||||
- raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps with existing entry '{}'".format(itr, entry))
|
||||
- except ValueError:
|
||||
- pass
|
||||
+ if entry_network.overlaps(ipaddress.ip_network(itr, strict=False)):
|
||||
+ raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps with existing entry '{}'".format(entry, itr))
|
||||
+
|
||||
+def check_for_overlapping_entries(entries):
|
||||
+ """ Check if any entry overlaps any entry in the list of entries """
|
||||
+ try:
|
||||
+ entries = [ipaddress.ip_network(x, strict=False) for x in entries]
|
||||
+ except ValueError:
|
||||
+ # at least one entry can not be parsed
|
||||
+ return
|
||||
+
|
||||
+ while entries:
|
||||
+ entry = entries.pop()
|
||||
+ for itr in entries:
|
||||
+ if entry.overlaps(itr):
|
||||
+ raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps entry '{}'".format(entry, itr))
|
||||
diff --git a/src/firewall/server/config_ipset.py b/src/firewall/server/config_ipset.py
|
||||
index f33c2a02926f..499ffcb9227a 100644
|
||||
--- a/src/firewall/server/config_ipset.py
|
||||
+++ b/src/firewall/server/config_ipset.py
|
||||
@@ -34,7 +34,8 @@ from firewall.dbus_utils import dbus_to_python, \
|
||||
dbus_introspection_add_properties
|
||||
from firewall.core.io.ipset import IPSet
|
||||
from firewall.core.ipset import IPSET_TYPES, normalize_ipset_entry, \
|
||||
- check_entry_overlaps_existing
|
||||
+ check_entry_overlaps_existing, \
|
||||
+ check_for_overlapping_entries
|
||||
from firewall.core.logger import log
|
||||
from firewall.server.decorators import handle_exceptions, \
|
||||
dbus_handle_exceptions, dbus_service_method
|
||||
@@ -407,11 +408,8 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
|
||||
in_signature='as')
|
||||
@dbus_handle_exceptions
|
||||
def setEntries(self, entries, sender=None):
|
||||
- _entries = set()
|
||||
- for _entry in dbus_to_python(entries, list):
|
||||
- check_entry_overlaps_existing(_entry, _entries)
|
||||
- _entries.add(normalize_ipset_entry(_entry))
|
||||
- entries = list(_entries)
|
||||
+ entries = dbus_to_python(entries, list)
|
||||
+ check_for_overlapping_entries(entries)
|
||||
log.debug1("%s.setEntries('[%s]')", self._log_prefix,
|
||||
",".join(entries))
|
||||
self.parent.accessCheck(sender)
|
||||
--
|
||||
2.39.1
|
||||
|
@ -0,0 +1,66 @@
|
||||
From 11ee9b9ed8da78bfc11edffc2c9386efa41be1cf Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 18 Dec 2023 18:22:38 -0500
|
||||
Subject: [PATCH 08/22] v2.1.0: improvement(nftables): do not track rule
|
||||
handles for policy table
|
||||
|
||||
It's not necessary. This table is transient and we simply delete the
|
||||
entire table when we're done with it.
|
||||
|
||||
(cherry picked from commit 119dff1d86f841cd2f33ddbab278bc9257dae7b0)
|
||||
---
|
||||
src/firewall/core/nftables.py | 24 +++++++-----------------
|
||||
1 file changed, 7 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 3df3fa3c3742..690a5dc067ab 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -386,6 +386,11 @@ class nftables(object):
|
||||
if verb not in output["nftables"][index]:
|
||||
continue
|
||||
|
||||
+ # don't bother tracking handles for the policy table as we simply
|
||||
+ # delete the entire table.
|
||||
+ if TABLE_NAME_POLICY == output["nftables"][index][verb]["rule"]["table"]:
|
||||
+ continue
|
||||
+
|
||||
self.rule_to_handle[rule_key] = output["nftables"][index][verb]["rule"]["handle"]
|
||||
|
||||
def set_rule(self, rule, log_denied):
|
||||
@@ -408,18 +413,8 @@ class nftables(object):
|
||||
"name": table}}}]
|
||||
|
||||
def build_flush_rules(self):
|
||||
- # Policy is stashed in a separate table that we're _not_ going to
|
||||
- # flush. As such, we retain the policy rule handles and ref counts.
|
||||
- saved_rule_to_handle = {}
|
||||
- saved_rule_ref_count = {}
|
||||
- for rule in self._build_set_policy_rules_ct_rules(True):
|
||||
- policy_key = self._get_rule_key(rule)
|
||||
- if policy_key in self.rule_to_handle:
|
||||
- saved_rule_to_handle[policy_key] = self.rule_to_handle[policy_key]
|
||||
- saved_rule_ref_count[policy_key] = self.rule_ref_count[policy_key]
|
||||
-
|
||||
- self.rule_to_handle = saved_rule_to_handle
|
||||
- self.rule_ref_count = saved_rule_ref_count
|
||||
+ self.rule_to_handle = {}
|
||||
+ self.rule_ref_count = {}
|
||||
self.rich_rule_priority_counts = {}
|
||||
self.policy_priority_counts = {}
|
||||
self.zone_source_index_cache = {}
|
||||
@@ -475,11 +470,6 @@ class nftables(object):
|
||||
|
||||
rules += self._build_set_policy_rules_ct_rules(True)
|
||||
elif policy == "ACCEPT":
|
||||
- for rule in self._build_set_policy_rules_ct_rules(False):
|
||||
- policy_key = self._get_rule_key(rule)
|
||||
- if policy_key in self.rule_to_handle:
|
||||
- rules.append(rule)
|
||||
-
|
||||
rules += self._build_delete_table_rules(TABLE_NAME_POLICY)
|
||||
else:
|
||||
raise FirewallError(UNKNOWN_ERROR, "not implemented")
|
||||
--
|
||||
2.43.5
|
||||
|
@ -1,56 +0,0 @@
|
||||
From 885d308c1457e9ea0d839d852dd98a1c134b448c Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 30 Nov 2021 14:50:17 -0500
|
||||
Subject: [PATCH 09/10] v1.1.0: test(ipset): huge set of entries benchmark
|
||||
|
||||
Coverage: #881
|
||||
(cherry picked from commit 114936c71ab1b12a5598d06805b7e9e13f7ee190)
|
||||
---
|
||||
src/tests/regression/gh881.at | 25 +++++++++++++++++++++++++
|
||||
src/tests/regression/regression.at | 1 +
|
||||
2 files changed, 26 insertions(+)
|
||||
create mode 100644 src/tests/regression/gh881.at
|
||||
|
||||
diff --git a/src/tests/regression/gh881.at b/src/tests/regression/gh881.at
|
||||
new file mode 100644
|
||||
index 000000000000..c7326805b555
|
||||
--- /dev/null
|
||||
+++ b/src/tests/regression/gh881.at
|
||||
@@ -0,0 +1,25 @@
|
||||
+FWD_START_TEST([ipset entry overlap detect perf])
|
||||
+AT_KEYWORDS(ipset gh881)
|
||||
+
|
||||
+dnl build a large ipset
|
||||
+dnl
|
||||
+AT_DATA([./deny_cidr], [])
|
||||
+NS_CHECK([sh -c '
|
||||
+for I in $(seq 10); do
|
||||
+ for J in $(seq 250); do
|
||||
+ echo "10.${I}.${J}.0/24" >> ./deny_cidr
|
||||
+ done
|
||||
+done
|
||||
+'])
|
||||
+
|
||||
+dnl verify non-overlapping does not error
|
||||
+dnl
|
||||
+FWD_CHECK([--permanent --new-ipset=deny_set --type=hash:net --option=family=inet --option=hashsize=16384 --option=maxelem=20000], 0, [ignore])
|
||||
+NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
|
||||
+
|
||||
+dnl verify overlap detection actually detects an overlap
|
||||
+dnl
|
||||
+NS_CHECK([echo "10.1.0.0/16" >> ./deny_cidr])
|
||||
+NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
|
||||
+
|
||||
+FWD_END_TEST()
|
||||
diff --git a/src/tests/regression/regression.at b/src/tests/regression/regression.at
|
||||
index 104f784cbe93..143298d3235f 100644
|
||||
--- a/src/tests/regression/regression.at
|
||||
+++ b/src/tests/regression/regression.at
|
||||
@@ -50,3 +50,4 @@ m4_include([regression/gh874.at])
|
||||
m4_include([regression/service_includes_for_builtin.at])
|
||||
m4_include([regression/rhbz2181406.at])
|
||||
m4_include([regression/ipset_scale.at])
|
||||
+m4_include([regression/gh881.at])
|
||||
--
|
||||
2.39.1
|
||||
|
@ -0,0 +1,203 @@
|
||||
From c53dabcb9ca5c6d9ab2b076d961127a67afe8f8f Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Fri, 11 Aug 2023 18:16:20 +0200
|
||||
Subject: [PATCH 09/22] v2.1.0: improvement(fw): make set_policy("DROP") more
|
||||
flexible
|
||||
|
||||
We will add a reload-policy via
|
||||
|
||||
ReloadPolicy=OUTPUT:{ACCEPT,REJECT,DROP},INPUT:{ACCEPT,REJECT,DROP},FORWARD:{ACCEPT,REJECT,DROP}
|
||||
|
||||
Extend set_policy() so that the "DROP" policy can be overridden.
|
||||
|
||||
(cherry picked from commit e3bb468ff469373d193398b471a59f7ab7d29f27)
|
||||
---
|
||||
src/firewall/core/ebtables.py | 2 +-
|
||||
src/firewall/core/fw.py | 27 +++++++++++++---
|
||||
src/firewall/core/ipXtables.py | 11 +++++--
|
||||
src/firewall/core/nftables.py | 56 ++++++++++++++++++++++++----------
|
||||
4 files changed, 72 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/ebtables.py b/src/firewall/core/ebtables.py
|
||||
index c1c0b8587a5c..f059975724a5 100644
|
||||
--- a/src/firewall/core/ebtables.py
|
||||
+++ b/src/firewall/core/ebtables.py
|
||||
@@ -237,7 +237,7 @@ class ebtables(object):
|
||||
rules.append(["-t", table, flag])
|
||||
return rules
|
||||
|
||||
- def build_set_policy_rules(self, policy):
|
||||
+ def build_set_policy_rules(self, policy, policy_details):
|
||||
rules = []
|
||||
_policy = "DROP" if policy == "PANIC" else policy
|
||||
for table in BUILT_IN_CHAINS.keys():
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index f1bc124b9443..ccec875f3c3c 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -983,7 +983,18 @@ class Firewall(object):
|
||||
if use_transaction is None:
|
||||
transaction.execute(True)
|
||||
|
||||
- def set_policy(self, policy, use_transaction=None):
|
||||
+ def _set_policy_build_rules(self, backend, policy, policy_details=None):
|
||||
+ assert policy in ("ACCEPT", "DROP", "PANIC")
|
||||
+ if policy_details is None:
|
||||
+ dp = "ACCEPT" if policy == "ACCEPT" else "DROP"
|
||||
+ policy_details = {
|
||||
+ "INPUT": dp,
|
||||
+ "OUTPUT": dp,
|
||||
+ "FORWARD": dp,
|
||||
+ }
|
||||
+ return backend.build_set_policy_rules(policy, policy_details)
|
||||
+
|
||||
+ def set_policy(self, policy, policy_details=None, use_transaction=None):
|
||||
if use_transaction is None:
|
||||
transaction = FirewallTransaction(self)
|
||||
else:
|
||||
@@ -992,7 +1003,7 @@ class Firewall(object):
|
||||
log.debug1("Setting policy to '%s'", policy)
|
||||
|
||||
for backend in self.enabled_backends():
|
||||
- rules = backend.build_set_policy_rules(policy)
|
||||
+ rules = self._set_policy_build_rules(backend, policy, policy_details)
|
||||
transaction.add_rules(backend, rules)
|
||||
|
||||
if use_transaction is None:
|
||||
@@ -1224,13 +1235,19 @@ class Firewall(object):
|
||||
# for the old backend that was set to DROP above.
|
||||
if not self._panic and old_firewall_backend != self._firewall_backend:
|
||||
if old_firewall_backend == "nftables":
|
||||
- for rule in self.nftables_backend.build_set_policy_rules("ACCEPT"):
|
||||
+ for rule in self._set_policy_build_rules(
|
||||
+ self.nftables_backend, "ACCEPT"
|
||||
+ ):
|
||||
self.nftables_backend.set_rule(rule, self._log_denied)
|
||||
else:
|
||||
- for rule in self.ip4tables_backend.build_set_policy_rules("ACCEPT"):
|
||||
+ for rule in self._set_policy_build_rules(
|
||||
+ self.ip4tables_backend, "ACCEPT"
|
||||
+ ):
|
||||
self.ip4tables_backend.set_rule(rule, self._log_denied)
|
||||
if self.ip6tables_enabled:
|
||||
- for rule in self.ip6tables_backend.build_set_policy_rules("ACCEPT"):
|
||||
+ for rule in self._set_policy_build_rules(
|
||||
+ self.ip6tables_backend, "ACCEPT"
|
||||
+ ):
|
||||
self.ip6tables_backend.set_rule(rule, self._log_denied)
|
||||
|
||||
if start_exception:
|
||||
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
|
||||
index e05a2bd4d7ed..1a0cea7a3b4e 100644
|
||||
--- a/src/firewall/core/ipXtables.py
|
||||
+++ b/src/firewall/core/ipXtables.py
|
||||
@@ -578,7 +578,7 @@ class ip4tables(object):
|
||||
rules.append(["-t", table, flag])
|
||||
return rules
|
||||
|
||||
- def build_set_policy_rules(self, policy):
|
||||
+ def build_set_policy_rules(self, policy, policy_details):
|
||||
rules = []
|
||||
_policy = "DROP" if policy == "PANIC" else policy
|
||||
for table in BUILT_IN_CHAINS.keys():
|
||||
@@ -587,7 +587,14 @@ class ip4tables(object):
|
||||
if table == "nat":
|
||||
continue
|
||||
for chain in BUILT_IN_CHAINS[table]:
|
||||
- rules.append(["-t", table, "-P", chain, _policy])
|
||||
+ if table == "filter":
|
||||
+ p = policy_details[chain]
|
||||
+ if p == "REJECT":
|
||||
+ rules.append(["-t", table, "-A", chain, "-j", "REJECT"])
|
||||
+ p = "DROP"
|
||||
+ else:
|
||||
+ p = _policy
|
||||
+ rules.append(["-t", table, "-P", chain, p])
|
||||
return rules
|
||||
|
||||
def supported_icmp_types(self, ipv=None):
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 690a5dc067ab..e9816147ef8e 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -421,20 +421,17 @@ class nftables(object):
|
||||
|
||||
return self._build_delete_table_rules(TABLE_NAME)
|
||||
|
||||
- def _build_set_policy_rules_ct_rules(self, enable):
|
||||
+ def _build_set_policy_rules_ct_rule(self, enable, hook):
|
||||
add_del = { True: "add", False: "delete" }[enable]
|
||||
- rules = []
|
||||
- for hook in ["input", "forward", "output"]:
|
||||
- rules.append({add_del: {"rule": {"family": "inet",
|
||||
- "table": TABLE_NAME_POLICY,
|
||||
- "chain": "%s_%s" % ("filter", hook),
|
||||
- "expr": [{"match": {"left": {"ct": {"key": "state"}},
|
||||
- "op": "in",
|
||||
- "right": {"set": ["established", "related"]}}},
|
||||
- {"accept": None}]}}})
|
||||
- return rules
|
||||
-
|
||||
- def build_set_policy_rules(self, policy):
|
||||
+ return {add_del: {"rule": {"family": "inet",
|
||||
+ "table": TABLE_NAME_POLICY,
|
||||
+ "chain": "%s_%s" % ("filter", hook),
|
||||
+ "expr": [{"match": {"left": {"ct": {"key": "state"}},
|
||||
+ "op": "in",
|
||||
+ "right": {"set": ["established", "related"]}}},
|
||||
+ {"accept": None}]}}}
|
||||
+
|
||||
+ def build_set_policy_rules(self, policy, policy_details):
|
||||
# Policy is not exposed to the user. It's only to make sure we DROP
|
||||
# packets while reloading and for panic mode. As such, using hooks with
|
||||
# a higher priority than our base chains is sufficient.
|
||||
@@ -459,16 +456,43 @@ class nftables(object):
|
||||
|
||||
# To drop everything except existing connections we use
|
||||
# "filter" because it occurs _after_ conntrack.
|
||||
- for hook in ["input", "forward", "output"]:
|
||||
+ for hook in ("INPUT", "FORWARD", "OUTPUT"):
|
||||
+ d_policy = policy_details[hook]
|
||||
+ assert d_policy in ("ACCEPT", "REJECT", "DROP")
|
||||
+ hook = hook.lower()
|
||||
+ chain_name = f"filter_{hook}"
|
||||
+
|
||||
rules.append({"add": {"chain": {"family": "inet",
|
||||
"table": TABLE_NAME_POLICY,
|
||||
- "name": "%s_%s" % ("filter", hook),
|
||||
+ "name": chain_name,
|
||||
"type": "filter",
|
||||
"hook": hook,
|
||||
"prio": 0 + NFT_HOOK_OFFSET - 1,
|
||||
"policy": "drop"}}})
|
||||
|
||||
- rules += self._build_set_policy_rules_ct_rules(True)
|
||||
+ rules.append(self._build_set_policy_rules_ct_rule(True, hook))
|
||||
+
|
||||
+ if d_policy == "ACCEPT":
|
||||
+ expr_fragment = {"accept": None}
|
||||
+ elif d_policy == "DROP":
|
||||
+ expr_fragment = {"drop": None}
|
||||
+ else:
|
||||
+ expr_fragment = {
|
||||
+ "reject": {"type": "icmpx", "expr": "admin-prohibited"}
|
||||
+ }
|
||||
+
|
||||
+ rules.append(
|
||||
+ {
|
||||
+ "add": {
|
||||
+ "rule": {
|
||||
+ "family": "inet",
|
||||
+ "table": TABLE_NAME_POLICY,
|
||||
+ "chain": chain_name,
|
||||
+ "expr": [expr_fragment],
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ )
|
||||
elif policy == "ACCEPT":
|
||||
rules += self._build_delete_table_rules(TABLE_NAME_POLICY)
|
||||
else:
|
||||
--
|
||||
2.43.5
|
||||
|
@ -1,150 +0,0 @@
|
||||
From d8d6d313acd50aa1c87c42fb7a7334b01c516227 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 25 Jan 2022 09:29:32 -0500
|
||||
Subject: [PATCH 10/10] v1.1.0: fix(ipset): further reduce cost of entry
|
||||
overlap detection
|
||||
|
||||
This makes the complexity linear by sorting the networks ahead of time.
|
||||
|
||||
Fixes: #881
|
||||
Fixes: rhbz2043289
|
||||
(cherry picked from commit 36c170db265265e838a089858be4b20dbbd582eb)
|
||||
---
|
||||
src/firewall/core/ipset.py | 59 ++++++++++++++++++++++++++++++++---
|
||||
src/tests/regression/gh881.at | 42 ++++++++++++++++++++++---
|
||||
2 files changed, 92 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/ipset.py b/src/firewall/core/ipset.py
|
||||
index 66ea4335536d..b160d8345669 100644
|
||||
--- a/src/firewall/core/ipset.py
|
||||
+++ b/src/firewall/core/ipset.py
|
||||
@@ -327,8 +327,57 @@ def check_for_overlapping_entries(entries):
|
||||
# at least one entry can not be parsed
|
||||
return
|
||||
|
||||
- while entries:
|
||||
- entry = entries.pop()
|
||||
- for itr in entries:
|
||||
- if entry.overlaps(itr):
|
||||
- raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps entry '{}'".format(entry, itr))
|
||||
+ # We can take advantage of some facts of IPv4Network/IPv6Network and
|
||||
+ # how Python sorts the networks to quickly detect overlaps.
|
||||
+ #
|
||||
+ # Facts:
|
||||
+ #
|
||||
+ # 1. IPv{4,6}Network are normalized to remove host bits, e.g.
|
||||
+ # 10.1.1.0/16 will become 10.1.0.0/16.
|
||||
+ #
|
||||
+ # 2. IPv{4,6}Network objects are sorted by:
|
||||
+ # a. IP address (network bits)
|
||||
+ # then
|
||||
+ # b. netmask (significant bits count)
|
||||
+ #
|
||||
+ # Because of the above we have these properties:
|
||||
+ #
|
||||
+ # 1. big networks (netA) are sorted before smaller networks (netB)
|
||||
+ # that overlap the big network (netA)
|
||||
+ # - e.g. 10.1.128.0/17 (netA) sorts before 10.1.129.0/24 (netB)
|
||||
+ # 2. same value addresses (network bits) are grouped together even
|
||||
+ # if the number of network bits vary. e.g. /16 vs /24
|
||||
+ # - recall that address are normalized to remove host bits
|
||||
+ # - e.g. 10.1.128.0/17 (netA) sorts before 10.1.128.0/24 (netC)
|
||||
+ # 3. non-overlapping networks (netD, netE) are always sorted before or
|
||||
+ # after networks that overlap (netB, netC) the current one (netA)
|
||||
+ # - e.g. 10.1.128.0/17 (netA) sorts before 10.2.128.0/16 (netD)
|
||||
+ # - e.g. 10.1.128.0/17 (netA) sorts after 9.1.128.0/17 (netE)
|
||||
+ # - e.g. 9.1.128.0/17 (netE) sorts before 10.1.129.0/24 (netB)
|
||||
+ #
|
||||
+ # With this we know the sorted list looks like:
|
||||
+ #
|
||||
+ # list: [ netE, netA, netB, netC, netD ]
|
||||
+ #
|
||||
+ # netE = non-overlapping network
|
||||
+ # netA = big network
|
||||
+ # netB = smaller network that overlaps netA (subnet)
|
||||
+ # netC = smaller network that overlaps netA (subnet)
|
||||
+ # netD = non-overlapping network
|
||||
+ #
|
||||
+ # If networks netB and netC exist in the list, they overlap and are
|
||||
+ # adjacent to netA.
|
||||
+ #
|
||||
+ # Checking for overlaps on a sorted list is thus:
|
||||
+ #
|
||||
+ # 1. compare adjacent elements in the list for overlaps
|
||||
+ #
|
||||
+ # Recall that we only need to detect a single overlap. We do not need to
|
||||
+ # detect them all.
|
||||
+ #
|
||||
+ entries.sort()
|
||||
+ prev_network = entries.pop(0)
|
||||
+ for current_network in entries:
|
||||
+ if prev_network.overlaps(current_network):
|
||||
+ raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps entry '{}'".format(prev_network, current_network))
|
||||
+ prev_network = current_network
|
||||
diff --git a/src/tests/regression/gh881.at b/src/tests/regression/gh881.at
|
||||
index c7326805b555..a5cf7e4eb912 100644
|
||||
--- a/src/tests/regression/gh881.at
|
||||
+++ b/src/tests/regression/gh881.at
|
||||
@@ -5,21 +5,55 @@ dnl build a large ipset
|
||||
dnl
|
||||
AT_DATA([./deny_cidr], [])
|
||||
NS_CHECK([sh -c '
|
||||
-for I in $(seq 10); do
|
||||
+for I in $(seq 250); do
|
||||
for J in $(seq 250); do
|
||||
echo "10.${I}.${J}.0/24" >> ./deny_cidr
|
||||
done
|
||||
done
|
||||
'])
|
||||
+NS_CHECK([echo "10.254.0.0/16" >> ./deny_cidr])
|
||||
|
||||
dnl verify non-overlapping does not error
|
||||
dnl
|
||||
FWD_CHECK([--permanent --new-ipset=deny_set --type=hash:net --option=family=inet --option=hashsize=16384 --option=maxelem=20000], 0, [ignore])
|
||||
-NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
|
||||
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
|
||||
+
|
||||
+dnl still no overlap
|
||||
+dnl
|
||||
+AT_DATA([./deny_cidr], [
|
||||
+9.0.0.0/8
|
||||
+11.1.0.0/16
|
||||
+])
|
||||
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
|
||||
|
||||
dnl verify overlap detection actually detects an overlap
|
||||
dnl
|
||||
-NS_CHECK([echo "10.1.0.0/16" >> ./deny_cidr])
|
||||
-NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
|
||||
+AT_DATA([./deny_cidr], [
|
||||
+10.1.0.0/16
|
||||
+10.2.0.0/16
|
||||
+10.250.0.0/16
|
||||
+])
|
||||
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
|
||||
+
|
||||
+AT_DATA([./deny_cidr], [
|
||||
+10.253.0.0/16
|
||||
+10.253.128.0/17
|
||||
+])
|
||||
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
|
||||
+
|
||||
+AT_DATA([./deny_cidr], [
|
||||
+10.1.1.1/32
|
||||
+])
|
||||
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
|
||||
+
|
||||
+AT_DATA([./deny_cidr], [
|
||||
+10.0.0.0/8
|
||||
+10.0.0.0/25
|
||||
+])
|
||||
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
|
||||
+
|
||||
+dnl empty file, no additions, but previous ones will remain
|
||||
+AT_DATA([./deny_cidr], [])
|
||||
+FWD_CHECK([--permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
|
||||
|
||||
FWD_END_TEST()
|
||||
--
|
||||
2.39.1
|
||||
|
@ -0,0 +1,303 @@
|
||||
From 67c8a0010ba6244c40e48a93560eb66d91a2ca09 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Mon, 14 Aug 2023 14:53:55 +0200
|
||||
Subject: [PATCH 10/22] v2.1.0: feat(fw): add ReloadPolicy option in
|
||||
firewalld.conf
|
||||
|
||||
One interesting aspect is that during `firewall-cmd --reload`, the code
|
||||
first sets the policy to "DROP", before reloading "firewalld.conf". That
|
||||
means, changing the value only takes effect after the next reload. But
|
||||
that seems expected as we set the policy before starting to reload.
|
||||
|
||||
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2149039
|
||||
(cherry picked from commit 0019371a8f42d376ac9cce79cc5e1e7d2049f021)
|
||||
---
|
||||
config/firewalld.conf | 8 +++
|
||||
doc/xml/firewalld.conf.xml | 16 ++++++
|
||||
src/firewall/config/__init__.py.in | 1 +
|
||||
src/firewall/core/fw.py | 13 ++++-
|
||||
src/firewall/core/io/firewalld_conf.py | 56 ++++++++++++++++++++-
|
||||
src/tests/features/features.at | 1 +
|
||||
src/tests/features/reloadpolicy.at | 12 +++++
|
||||
src/tests/unit/test_firewalld_conf.py | 68 ++++++++++++++++++++++++++
|
||||
8 files changed, 172 insertions(+), 3 deletions(-)
|
||||
create mode 100644 src/tests/features/reloadpolicy.at
|
||||
create mode 100644 src/tests/unit/test_firewalld_conf.py
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index f8caf11c8a86..7a0be1ff1b76 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -66,6 +66,14 @@ FirewallBackend=nftables
|
||||
# Default: yes
|
||||
FlushAllOnReload=yes
|
||||
|
||||
+# ReloadPolicy
|
||||
+# Policy during reload. By default all traffic except for established
|
||||
+# connections is dropped while the rules are updated. Set to "DROP", "REJECT"
|
||||
+# or "ACCEPT". Alternatively, specify it per table, like
|
||||
+# "OUTPUT:ACCEPT,INPUT:DROP,FORWARD:REJECT".
|
||||
+# Default: ReloadPolicy=INPUT:DROP,FORWARD:DROP,OUTPUT:DROP
|
||||
+ReloadPolicy=INPUT:DROP,FORWARD:DROP,OUTPUT:DROP
|
||||
+
|
||||
# RFC3964_IPv4
|
||||
# As per RFC 3964, filter IPv6 traffic with 6to4 destination addresses that
|
||||
# correspond to IPv4 addresses that should not be routed over the public
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index e4312acc8e1c..022569ccf502 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -195,6 +195,22 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
+ <varlistentry>
|
||||
+ <term><option>ReloadPolicy</option></term>
|
||||
+ <listitem>
|
||||
+ <para>
|
||||
+ The policy during reload. By default, all traffic except
|
||||
+ established connections is dropped while reloading the
|
||||
+ firewall rules. This can be overridden for INPUT, FORWARD
|
||||
+ and OUTPUT. The accepted values are "DROP", "REJECT" and
|
||||
+ "ACCEPT", which then applies to all tables. Alternatively,
|
||||
+ the policy can be specified per table, like
|
||||
+ "INPUT:REJECT,FORWARD:DROP,OUTPUT:ACCEPT".
|
||||
+ Defaults to "INPUT:DROP,FORWARD:DROP,OUTPUT:DROP".
|
||||
+ </para>
|
||||
+ </listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
<varlistentry>
|
||||
<term><option>RFC3964_IPv4</option></term>
|
||||
<listitem>
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index d982384a0382..da1e31e10e58 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -133,5 +133,6 @@ FALLBACK_LOG_DENIED = "off"
|
||||
FALLBACK_AUTOMATIC_HELPERS = "no"
|
||||
FALLBACK_FIREWALL_BACKEND = "nftables"
|
||||
FALLBACK_FLUSH_ALL_ON_RELOAD = True
|
||||
+FALLBACK_RELOAD_POLICY = "INPUT:DROP,FORWARD:DROP,OUTPUT:DROP"
|
||||
FALLBACK_RFC3964_IPV4 = True
|
||||
FALLBACK_ALLOW_ZONE_DRIFTING = False
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index ccec875f3c3c..ac13be122b66 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -1000,7 +1000,13 @@ class Firewall(object):
|
||||
else:
|
||||
transaction = use_transaction
|
||||
|
||||
- log.debug1("Setting policy to '%s'", policy)
|
||||
+ log.debug1(
|
||||
+ "Setting policy to '%s'%s",
|
||||
+ policy,
|
||||
+ f" (ReloadPolicy={firewalld_conf._unparse_reload_policy(policy_details)})"
|
||||
+ if policy == "DROP"
|
||||
+ else "",
|
||||
+ )
|
||||
|
||||
for backend in self.enabled_backends():
|
||||
rules = self._set_policy_build_rules(backend, policy, policy_details)
|
||||
@@ -1146,7 +1152,10 @@ class Firewall(object):
|
||||
_ipset_objs.append(self.ipset.get_ipset(_name))
|
||||
|
||||
if not _panic:
|
||||
- self.set_policy("DROP")
|
||||
+ reload_policy = firewalld_conf._parse_reload_policy(
|
||||
+ self._firewalld_conf.get("ReloadPolicy")
|
||||
+ )
|
||||
+ self.set_policy("DROP", policy_details=reload_policy)
|
||||
|
||||
self.flush()
|
||||
self.cleanup()
|
||||
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
|
||||
index b907c5b1e60b..d2879b319d1f 100644
|
||||
--- a/src/firewall/core/io/firewalld_conf.py
|
||||
+++ b/src/firewall/core/io/firewalld_conf.py
|
||||
@@ -31,7 +31,7 @@ valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit",
|
||||
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
- "AllowZoneDrifting" ]
|
||||
+ "AllowZoneDrifting", "ReloadPolicy" ]
|
||||
|
||||
class firewalld_conf(object):
|
||||
def __init__(self, filename):
|
||||
@@ -77,6 +77,7 @@ class firewalld_conf(object):
|
||||
self.set("AutomaticHelpers", config.FALLBACK_AUTOMATIC_HELPERS)
|
||||
self.set("FirewallBackend", config.FALLBACK_FIREWALL_BACKEND)
|
||||
self.set("FlushAllOnReload", "yes" if config.FALLBACK_FLUSH_ALL_ON_RELOAD else "no")
|
||||
+ self.set("ReloadPolicy", config.FALLBACK_RELOAD_POLICY)
|
||||
self.set("RFC3964_IPv4", "yes" if config.FALLBACK_RFC3964_IPV4 else "no")
|
||||
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
|
||||
|
||||
@@ -208,6 +209,17 @@ class firewalld_conf(object):
|
||||
config.FALLBACK_FLUSH_ALL_ON_RELOAD)
|
||||
self.set("FlushAllOnReload", str(config.FALLBACK_FLUSH_ALL_ON_RELOAD))
|
||||
|
||||
+ value = self.get("ReloadPolicy")
|
||||
+ try:
|
||||
+ value = self._parse_reload_policy(value)
|
||||
+ except ValueError:
|
||||
+ log.warning(
|
||||
+ "ReloadPolicy '%s' is not valid, using default value '%s'",
|
||||
+ value,
|
||||
+ config.FALLBACK_RELOAD_POLICY,
|
||||
+ )
|
||||
+ self.set("ReloadPolicy", config.FALLBACK_RELOAD_POLICY)
|
||||
+
|
||||
value = self.get("RFC3964_IPv4")
|
||||
if not value or value.lower() not in [ "yes", "true", "no", "false" ]:
|
||||
if value is not None:
|
||||
@@ -330,3 +342,45 @@ class firewalld_conf(object):
|
||||
raise IOError("Failed to create '%s': %s" % (self.filename, msg))
|
||||
else:
|
||||
os.chmod(self.filename, 0o600)
|
||||
+
|
||||
+ @staticmethod
|
||||
+ def _parse_reload_policy(value):
|
||||
+ valid = True
|
||||
+ result = {
|
||||
+ "INPUT": "DROP",
|
||||
+ "FORWARD": "DROP",
|
||||
+ "OUTPUT": "DROP",
|
||||
+ }
|
||||
+ if value:
|
||||
+ value = value.strip()
|
||||
+ v = value.upper()
|
||||
+ if v in ("ACCEPT", "REJECT", "DROP"):
|
||||
+ for k in result:
|
||||
+ result[k] = v
|
||||
+ else:
|
||||
+ for a in value.replace(";", ",").split(","):
|
||||
+ a = a.strip()
|
||||
+ if not a:
|
||||
+ continue
|
||||
+ a2 = a.replace("=", ":").split(":", 2)
|
||||
+ if len(a2) != 2:
|
||||
+ valid = False
|
||||
+ continue
|
||||
+ k = a2[0].strip().upper()
|
||||
+ if k not in result:
|
||||
+ valid = False
|
||||
+ continue
|
||||
+ v = a2[1].strip().upper()
|
||||
+ if v not in ("ACCEPT", "REJECT", "DROP"):
|
||||
+ valid = False
|
||||
+ continue
|
||||
+ result[k] = v
|
||||
+
|
||||
+ if not valid:
|
||||
+ raise ValueError("Invalid ReloadPolicy")
|
||||
+
|
||||
+ return result
|
||||
+
|
||||
+ @staticmethod
|
||||
+ def _unparse_reload_policy(value):
|
||||
+ return ",".join(f"{k}:{v}" for k, v in value.items())
|
||||
diff --git a/src/tests/features/features.at b/src/tests/features/features.at
|
||||
index f59baea1cd70..065cb2872e88 100644
|
||||
--- a/src/tests/features/features.at
|
||||
+++ b/src/tests/features/features.at
|
||||
@@ -20,3 +20,4 @@ m4_include([features/startup_failsafe.at])
|
||||
m4_include([features/ipset.at])
|
||||
m4_include([features/reset_defaults.at])
|
||||
m4_include([features/iptables_no_flush_on_shutdown.at])
|
||||
+m4_include([features/reloadpolicy.at])
|
||||
diff --git a/src/tests/features/reloadpolicy.at b/src/tests/features/reloadpolicy.at
|
||||
new file mode 100644
|
||||
index 000000000000..fea1aa26aab4
|
||||
--- /dev/null
|
||||
+++ b/src/tests/features/reloadpolicy.at
|
||||
@@ -0,0 +1,12 @@
|
||||
+FWD_START_TEST([check ReloadPolicy])
|
||||
+AT_KEYWORDS(reloadpolicy rhbz2149039)
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^ReloadPolicy=.*/ReloadPolicy=INPUT:REJECT,FORWARD:ACCEPT/' ./firewalld.conf])
|
||||
+dnl call RELOAD twice, to see more action about the ReloadPolicy.
|
||||
+FWD_RELOAD()
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^ReloadPolicy=.*/ReloadPolicy=REJECT/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+FWD_END_TEST()
|
||||
diff --git a/src/tests/unit/test_firewalld_conf.py b/src/tests/unit/test_firewalld_conf.py
|
||||
new file mode 100644
|
||||
index 000000000000..0ce1fd279f91
|
||||
--- /dev/null
|
||||
+++ b/src/tests/unit/test_firewalld_conf.py
|
||||
@@ -0,0 +1,68 @@
|
||||
+# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import firewall.core.io.firewalld_conf
|
||||
+import firewall.config
|
||||
+
|
||||
+
|
||||
+def test_reload_policy():
|
||||
+ def t(value, expected_valid=True, **kw):
|
||||
+
|
||||
+ expected = {
|
||||
+ "INPUT": "DROP",
|
||||
+ "FORWARD": "DROP",
|
||||
+ "OUTPUT": "DROP",
|
||||
+ }
|
||||
+ for k, v in kw.items():
|
||||
+ assert k in expected
|
||||
+ expected[k] = v
|
||||
+
|
||||
+ try:
|
||||
+ parsed = (
|
||||
+ firewall.core.io.firewalld_conf.firewalld_conf._parse_reload_policy(
|
||||
+ value
|
||||
+ )
|
||||
+ )
|
||||
+ except ValueError:
|
||||
+ assert not expected_valid
|
||||
+ return
|
||||
+
|
||||
+ assert parsed == expected
|
||||
+ assert expected_valid
|
||||
+
|
||||
+ unparsed = (
|
||||
+ firewall.core.io.firewalld_conf.firewalld_conf._unparse_reload_policy(
|
||||
+ parsed
|
||||
+ )
|
||||
+ )
|
||||
+ parsed2 = firewall.core.io.firewalld_conf.firewalld_conf._parse_reload_policy(
|
||||
+ unparsed
|
||||
+ )
|
||||
+ assert parsed2 == parsed
|
||||
+
|
||||
+ t(None)
|
||||
+ t("")
|
||||
+ t(" ")
|
||||
+ t(" input: ACCept ", INPUT="ACCEPT")
|
||||
+ t(
|
||||
+ "forward:DROP, forward : REJEct; input: ACCept ",
|
||||
+ INPUT="ACCEPT",
|
||||
+ FORWARD="REJECT",
|
||||
+ )
|
||||
+ t(" accept ", INPUT="ACCEPT", FORWARD="ACCEPT", OUTPUT="ACCEPT")
|
||||
+ t("REJECT", INPUT="REJECT", FORWARD="REJECT", OUTPUT="REJECT")
|
||||
+ t("forward=REJECT", FORWARD="REJECT")
|
||||
+ t("forward=REJECT , input=accept", FORWARD="REJECT", INPUT="ACCEPT")
|
||||
+ t("forward=REJECT , xinput=accept", expected_valid=False)
|
||||
+ t("forward=REJECT, ACCEPT", expected_valid=False)
|
||||
+
|
||||
+ def _norm(reload_policy):
|
||||
+ parsed = firewall.core.io.firewalld_conf.firewalld_conf._parse_reload_policy(
|
||||
+ reload_policy
|
||||
+ )
|
||||
+ return firewall.core.io.firewalld_conf.firewalld_conf._unparse_reload_policy(
|
||||
+ parsed
|
||||
+ )
|
||||
+
|
||||
+ assert firewall.config.FALLBACK_RELOAD_POLICY == _norm(
|
||||
+ firewall.config.FALLBACK_RELOAD_POLICY
|
||||
+ )
|
||||
--
|
||||
2.43.5
|
||||
|
@ -1,32 +0,0 @@
|
||||
From e9e1edef3af8bd1a6b7c27fdd2d580e2f1571440 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fran=C3=A7ois=20Rigault?= <rigault.francois@gmail.com>
|
||||
Date: Sun, 28 Aug 2022 10:25:33 +0200
|
||||
Subject: [PATCH 11/17] v1.1.0: fix(ipset): exception on overlap checking empty
|
||||
set
|
||||
|
||||
In the case of --remove-entries-from-file, check_for_overlapping_entries
|
||||
can be called with no entry in input, which fails with an exception.
|
||||
|
||||
Fixes: rhbz2121985
|
||||
(cherry picked from commit 1ea554e6263ed21aa9ae6e5f0abb629d53b4a7bc)
|
||||
---
|
||||
src/firewall/core/ipset.py | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/src/firewall/core/ipset.py b/src/firewall/core/ipset.py
|
||||
index b160d8345669..d8e0a1ab1e56 100644
|
||||
--- a/src/firewall/core/ipset.py
|
||||
+++ b/src/firewall/core/ipset.py
|
||||
@@ -327,6 +327,9 @@ def check_for_overlapping_entries(entries):
|
||||
# at least one entry can not be parsed
|
||||
return
|
||||
|
||||
+ if len(entries) == 0:
|
||||
+ return
|
||||
+
|
||||
# We can take advantage of some facts of IPv4Network/IPv6Network and
|
||||
# how Python sorts the networks to quickly detect overlaps.
|
||||
#
|
||||
--
|
||||
2.39.3
|
||||
|
@ -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
|
||||
|
@ -1,48 +0,0 @@
|
||||
From a7b4212df4e1aa05d8dcb8fd4cf5e353a84d3481 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 29 Aug 2022 08:37:50 -0400
|
||||
Subject: [PATCH 12/17] v1.1.0: test(ipset): verify --remove-entries-from-file
|
||||
|
||||
Specifically if it results in an empty set.
|
||||
|
||||
Coverage: rhbz2121985
|
||||
(cherry picked from commit edea40189e10d3f7777e69746592fb5e2e0e36ea)
|
||||
---
|
||||
src/tests/regression/gh1011.at | 15 +++++++++++++++
|
||||
src/tests/regression/regression.at | 1 +
|
||||
2 files changed, 16 insertions(+)
|
||||
create mode 100644 src/tests/regression/gh1011.at
|
||||
|
||||
diff --git a/src/tests/regression/gh1011.at b/src/tests/regression/gh1011.at
|
||||
new file mode 100644
|
||||
index 000000000000..037ab70648eb
|
||||
--- /dev/null
|
||||
+++ b/src/tests/regression/gh1011.at
|
||||
@@ -0,0 +1,15 @@
|
||||
+FWD_START_TEST([remove entries results in empty])
|
||||
+AT_KEYWORDS(ipset gh1011 rhbz2121985)
|
||||
+
|
||||
+FWD_CHECK([--permanent --new-ipset foobar --type hash:net], 0, [ignore])
|
||||
+AT_DATA([./empty], [dnl
|
||||
+10.10.10.0/24
|
||||
+])
|
||||
+FWD_CHECK([--permanent --ipset foobar --add-entry 10.10.10.0/24], 0, [ignore])
|
||||
+FWD_CHECK([--permanent --ipset foobar --remove-entries-from-file ./empty], 0, [ignore])
|
||||
+
|
||||
+FWD_RELOAD()
|
||||
+FWD_CHECK([--ipset foobar --add-entry 10.10.10.0/24], 0, [ignore])
|
||||
+FWD_CHECK([--ipset foobar --remove-entries-from-file ./empty], 0, [ignore])
|
||||
+
|
||||
+FWD_END_TEST()
|
||||
diff --git a/src/tests/regression/regression.at b/src/tests/regression/regression.at
|
||||
index 143298d3235f..889c66dd175d 100644
|
||||
--- a/src/tests/regression/regression.at
|
||||
+++ b/src/tests/regression/regression.at
|
||||
@@ -51,3 +51,4 @@ m4_include([regression/service_includes_for_builtin.at])
|
||||
m4_include([regression/rhbz2181406.at])
|
||||
m4_include([regression/ipset_scale.at])
|
||||
m4_include([regression/gh881.at])
|
||||
+m4_include([regression/gh1011.at])
|
||||
--
|
||||
2.39.3
|
||||
|
@ -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
|
||||
|
@ -1,138 +0,0 @@
|
||||
From 90412a5fae831dcb1a8c9d9f4a798efabcc46567 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Tue, 11 Jul 2023 15:26:56 +0200
|
||||
Subject: [PATCH 13/17] v1.2.0: fix(ipset): fix configuring IP range for ipsets
|
||||
with nftables
|
||||
|
||||
Setting an IP range with nftables did not work:
|
||||
|
||||
firewall-cmd --permanent --delete-ipset=testipset || :
|
||||
firewall-cmd --permanent --delete-zone=testzone || :
|
||||
|
||||
ENTRY=1.1.1.1-1.1.1.10
|
||||
|
||||
firewall-cmd --permanent --new-ipset=testipset --type=hash:ip
|
||||
firewall-cmd --permanent --ipset=testipset --add-entry="$ENTRY"
|
||||
firewall-cmd --permanent --info-ipset=testipset
|
||||
firewall-cmd --permanent --new-zone=testzone
|
||||
firewall-cmd --permanent --zone=testzone --add-rich-rule='rule family="ipv4" source ipset="testipset" service name="ssh" accept'
|
||||
|
||||
firewall-cmd --reload &
|
||||
|
||||
This would generate the following JSON request:
|
||||
|
||||
{
|
||||
"add": {
|
||||
"element": {
|
||||
"family": "inet",
|
||||
"table": "firewalld",
|
||||
"name": "testipset",
|
||||
"elem": [
|
||||
"1.1.1.1-1.1.1.10"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libnftables will try to resolve "1.1.1.1-1.1.1.10" via getaddrinfo(). Calling
|
||||
getaddrinfo() to resolve names is bound to fail, and it blocks the process for
|
||||
a very long time. libnftables should not block the calling process ([1]).
|
||||
|
||||
We need to generate the correct JSON request, which is
|
||||
|
||||
{
|
||||
"add": {
|
||||
"element": {
|
||||
"family": "inet",
|
||||
"table": "firewalld",
|
||||
"name": "testipset",
|
||||
"elem": [
|
||||
{
|
||||
"range": [
|
||||
"1.1.1.1",
|
||||
"1.1.1.10"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
This is an ugly fix, because the parsing of ipset entries is duplicated
|
||||
and inconsistent. A better solution for that shall follow.
|
||||
|
||||
[1] https://marc.info/?l=netfilter-devel&m=168901121103612
|
||||
|
||||
https://bugzilla.redhat.com/show_bug.cgi?id=2028748
|
||||
|
||||
Fixes: 1582c5dd736a ('feat: nftables: convert to libnftables JSON interface')
|
||||
(cherry picked from commit 4db89e316f2d60f3cf856a7025a96a61e40b1e5a)
|
||||
---
|
||||
src/firewall/core/nftables.py | 27 +++++++++++++++------------
|
||||
src/tests/cli/firewall-cmd.at | 4 ++--
|
||||
2 files changed, 17 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 19a649aaaa71..2764bcf93645 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1850,19 +1850,22 @@ class nftables(object):
|
||||
fragment.append({"range": [port_str[:index], port_str[index+1:]]})
|
||||
|
||||
elif type_format[i] in ["ip", "net"]:
|
||||
- try:
|
||||
- index = entry_tokens[i].index("/")
|
||||
- except ValueError:
|
||||
- addr = entry_tokens[i]
|
||||
- if "family" in obj.options and obj.options["family"] == "inet6":
|
||||
- addr = normalizeIP6(addr)
|
||||
- fragment.append(addr)
|
||||
+ if '-' in entry_tokens[i]:
|
||||
+ fragment.append({"range": entry_tokens[i].split('-') })
|
||||
else:
|
||||
- addr = entry_tokens[i][:index]
|
||||
- if "family" in obj.options and obj.options["family"] == "inet6":
|
||||
- addr = normalizeIP6(addr)
|
||||
- fragment.append({"prefix": {"addr": addr,
|
||||
- "len": int(entry_tokens[i][index+1:])}})
|
||||
+ try:
|
||||
+ index = entry_tokens[i].index("/")
|
||||
+ except ValueError:
|
||||
+ addr = entry_tokens[i]
|
||||
+ if "family" in obj.options and obj.options["family"] == "inet6":
|
||||
+ addr = normalizeIP6(addr)
|
||||
+ fragment.append(addr)
|
||||
+ else:
|
||||
+ addr = entry_tokens[i][:index]
|
||||
+ if "family" in obj.options and obj.options["family"] == "inet6":
|
||||
+ addr = normalizeIP6(addr)
|
||||
+ fragment.append({"prefix": {"addr": addr,
|
||||
+ "len": int(entry_tokens[i][index+1:])}})
|
||||
else:
|
||||
fragment.append(entry_tokens[i])
|
||||
return [{"concat": fragment}] if len(type_format) > 1 else fragment
|
||||
diff --git a/src/tests/cli/firewall-cmd.at b/src/tests/cli/firewall-cmd.at
|
||||
index 47bdd81f5194..c4ab3108d37c 100644
|
||||
--- a/src/tests/cli/firewall-cmd.at
|
||||
+++ b/src/tests/cli/firewall-cmd.at
|
||||
@@ -908,7 +908,7 @@ FWD_START_TEST([ipset])
|
||||
|
||||
dnl multi dimensional sets
|
||||
FWD_CHECK([--permanent --new-ipset=foobar --type=hash:ip,port], 0, ignore)
|
||||
- FWD_CHECK([--permanent --ipset=foobar --add-entry=10.10.10.10,1234], 0, ignore)
|
||||
+ FWD_CHECK([--permanent --ipset=foobar --add-entry=10.10.10.10-10.10.10.12,1234], 0, ignore)
|
||||
FWD_CHECK([--permanent --ipset=foobar --add-entry=10.10.10.10,2000-2100], 0, ignore)
|
||||
FWD_RELOAD
|
||||
NFT_LIST_SET([foobar], 0, [dnl
|
||||
@@ -916,7 +916,7 @@ FWD_START_TEST([ipset])
|
||||
set foobar {
|
||||
type ipv4_addr . inet_proto . inet_service
|
||||
flags interval
|
||||
- elements = { 10.10.10.10 . tcp . 1234,
|
||||
+ elements = { 10.10.10.10-10.10.10.12 . tcp . 1234,
|
||||
10.10.10.10 . tcp . 2000-2100 }
|
||||
}
|
||||
}
|
||||
--
|
||||
2.39.3
|
||||
|
51
SOURCES/0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
Normal file
51
SOURCES/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
|
||||
|
@ -1,45 +0,0 @@
|
||||
From 08f76e2aa6d7ca35cfb626f20ace1f9036cda3a0 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 14 Aug 2023 09:13:29 -0400
|
||||
Subject: [PATCH 14/17] v1.2.0: chore(nftables): add delete table helper
|
||||
|
||||
This is to workaround an nftables issue where using the "delete" verb on
|
||||
a table that does not exist will throw ENOENT. We can't use the newer
|
||||
"destroy" verb because it's too new to rely upon.
|
||||
|
||||
A simple hack is to always add the table before deleting it. The "add"
|
||||
is ignored if the table already exists.
|
||||
|
||||
(cherry picked from commit 8be561d26931832f000526cc41293700faa6c877)
|
||||
---
|
||||
src/firewall/core/nftables.py | 14 ++++++++++++++
|
||||
1 file changed, 14 insertions(+)
|
||||
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 2764bcf93645..1959bdce73be 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -396,6 +396,20 @@ class nftables(object):
|
||||
# Tables always exist in nftables
|
||||
return [table] if table else IPTABLES_TO_NFT_HOOK.keys()
|
||||
|
||||
+ def _build_delete_table_rules(self, table):
|
||||
+ # To avoid nftables returning ENOENT we always add the table before
|
||||
+ # deleting to guarantee it will exist.
|
||||
+ #
|
||||
+ # In the future, this add+delete should be replaced with "destroy", but
|
||||
+ # that verb is too new to rely upon.
|
||||
+ rules = []
|
||||
+ for family in ["inet", "ip", "ip6"]:
|
||||
+ rules.append({"add": {"table": {"family": family,
|
||||
+ "name": table}}})
|
||||
+ rules.append({"delete": {"table": {"family": family,
|
||||
+ "name": table}}})
|
||||
+ return rules
|
||||
+
|
||||
def build_flush_rules(self):
|
||||
# Policy is stashed in a separate table that we're _not_ going to
|
||||
# flush. As such, we retain the policy rule handles and ref counts.
|
||||
--
|
||||
2.39.3
|
||||
|
@ -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
|
||||
|
@ -1,38 +0,0 @@
|
||||
From 0704ea3fef79cc1532f913ac1598e297016e1905 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Thu, 10 Aug 2023 08:43:03 -0400
|
||||
Subject: [PATCH 15/17] v1.2.0: fix(nftables): always flush main table on start
|
||||
|
||||
On start created_tables will not contain the main "firewalld" table so a
|
||||
flush command is not issued. We should always attempt to flush. If
|
||||
CleanupOnExit=no, then not flushing causes duplicate rules on restart.
|
||||
|
||||
Fixes: rhbz2222044
|
||||
(cherry picked from commit 6a155ea7195f2c720625e2452afa41544b4b4227)
|
||||
---
|
||||
src/firewall/core/nftables.py | 6 ++----
|
||||
1 file changed, 2 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 1959bdce73be..e3e06d75f663 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -427,13 +427,11 @@ class nftables(object):
|
||||
self.policy_priority_counts = {}
|
||||
self.zone_source_index_cache = {}
|
||||
|
||||
- rules = []
|
||||
for family in ["inet", "ip", "ip6"]:
|
||||
if TABLE_NAME in self.created_tables[family]:
|
||||
- rules.append({"delete": {"table": {"family": family,
|
||||
- "name": TABLE_NAME}}})
|
||||
self.created_tables[family].remove(TABLE_NAME)
|
||||
- return rules
|
||||
+
|
||||
+ return self._build_delete_table_rules(TABLE_NAME)
|
||||
|
||||
def _build_set_policy_rules_ct_rules(self, enable):
|
||||
add_del = { True: "add", False: "delete" }[enable]
|
||||
--
|
||||
2.39.3
|
||||
|
@ -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
|
||||
|
@ -1,82 +0,0 @@
|
||||
From 8c79246dbc5b8945c22b313ad51be698f2b61316 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Wed, 9 Aug 2023 14:39:08 -0400
|
||||
Subject: [PATCH 16/17] v1.2.0: test(CleanUpOnExit): verify restart does not
|
||||
duplicate rules
|
||||
|
||||
Coverage: rhbz2222044
|
||||
(cherry picked from commit c66e752a00c05a5afa58904850d244f50528059e)
|
||||
---
|
||||
src/tests/regression/regression.at | 1 +
|
||||
src/tests/regression/rhbz2222044.at | 50 +++++++++++++++++++++++++++++
|
||||
2 files changed, 51 insertions(+)
|
||||
create mode 100644 src/tests/regression/rhbz2222044.at
|
||||
|
||||
diff --git a/src/tests/regression/regression.at b/src/tests/regression/regression.at
|
||||
index 889c66dd175d..bc9aeb1a8624 100644
|
||||
--- a/src/tests/regression/regression.at
|
||||
+++ b/src/tests/regression/regression.at
|
||||
@@ -52,3 +52,4 @@ m4_include([regression/rhbz2181406.at])
|
||||
m4_include([regression/ipset_scale.at])
|
||||
m4_include([regression/gh881.at])
|
||||
m4_include([regression/gh1011.at])
|
||||
+m4_include([regression/rhbz2222044.at])
|
||||
diff --git a/src/tests/regression/rhbz2222044.at b/src/tests/regression/rhbz2222044.at
|
||||
new file mode 100644
|
||||
index 000000000000..9f3b1615b2f9
|
||||
--- /dev/null
|
||||
+++ b/src/tests/regression/rhbz2222044.at
|
||||
@@ -0,0 +1,50 @@
|
||||
+FWD_START_TEST([duplicate rules after restart])
|
||||
+AT_KEYWORDS(rhbz2222044)
|
||||
+AT_SKIP_IF([! NS_CMD([command -v wc >/dev/null 2>&1])])
|
||||
+
|
||||
+dnl rules have not changed so rule count should not change
|
||||
+m4_define([check_rule_count], [
|
||||
+m4_if(nftables, FIREWALL_BACKEND, [
|
||||
+NS_CHECK([nft list table inet firewalld | wc -l], 0, [dnl
|
||||
+237
|
||||
+])
|
||||
+NS_CHECK([nft list table ip firewalld | wc -l], 0, [dnl
|
||||
+105
|
||||
+])
|
||||
+NS_CHECK([nft list table ip6 firewalld | wc -l], 0, [dnl
|
||||
+105
|
||||
+])
|
||||
+], [ dnl iptables
|
||||
+NS_CHECK([iptables-save | wc -l], 0, [dnl
|
||||
+256
|
||||
+])
|
||||
+])])
|
||||
+
|
||||
+dnl --------------------------
|
||||
+dnl --------------------------
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^CleanupOnExit.*/CleanupOnExit=yes/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+check_rule_count()
|
||||
+FWD_RESTART()
|
||||
+check_rule_count()
|
||||
+
|
||||
+check_rule_count()
|
||||
+FWD_RELOAD()
|
||||
+check_rule_count()
|
||||
+
|
||||
+dnl Now do it again, but with CleanupOnExit=no
|
||||
+AT_CHECK([sed -i 's/^CleanupOnExit.*/CleanupOnExit=no/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+check_rule_count()
|
||||
+FWD_RESTART()
|
||||
+check_rule_count()
|
||||
+
|
||||
+check_rule_count()
|
||||
+FWD_RELOAD()
|
||||
+check_rule_count()
|
||||
+
|
||||
+m4_undefine([check_rule_count])
|
||||
+FWD_END_TEST()
|
||||
--
|
||||
2.39.3
|
||||
|
@ -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
|
||||
|
@ -1,32 +0,0 @@
|
||||
From 2ca79f8ebbadcf39f9b378b7fd296fcef13a4c54 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Mon, 14 Aug 2023 09:21:17 -0400
|
||||
Subject: [PATCH 17/17] v1.2.0: chore(nftables): policy: use delete table
|
||||
helper
|
||||
|
||||
Use the new table delete helper when deleting the policy table.
|
||||
|
||||
(cherry picked from commit a291a5d2f03711c2c6b0079128626204229ad79e)
|
||||
---
|
||||
src/firewall/core/nftables.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index e3e06d75f663..2a13b2678a94 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -489,9 +489,9 @@ class nftables(object):
|
||||
if policy_key in self.rule_to_handle:
|
||||
rules.append(rule)
|
||||
|
||||
+ rules += self._build_delete_table_rules(TABLE_NAME_POLICY)
|
||||
+
|
||||
if TABLE_NAME_POLICY in self.created_tables["inet"]:
|
||||
- rules.append({"delete": {"table": {"family": "inet",
|
||||
- "name": TABLE_NAME_POLICY}}})
|
||||
self.created_tables["inet"].remove(TABLE_NAME_POLICY)
|
||||
else:
|
||||
FirewallError(UNKNOWN_ERROR, "not implemented")
|
||||
--
|
||||
2.39.3
|
||||
|
@ -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
|
||||
|
@ -1,242 +0,0 @@
|
||||
From 0715e07a68d50d33797a724d24157a96afee3de6 Mon Sep 17 00:00:00 2001
|
||||
From: Derek Dai <daiderek@gmail.com>
|
||||
Date: Tue, 10 Nov 2020 20:37:36 +0800
|
||||
Subject: [PATCH 18/26] v1.0.0: feat(rich): support using ipset in destination
|
||||
|
||||
Fixes: #706
|
||||
Closes: #711
|
||||
(cherry picked from commit 286d00031f431f3c3d0f94028975a409e78be8c8)
|
||||
---
|
||||
doc/xml/firewalld.richlanguage.xml | 2 +-
|
||||
src/firewall/core/io/policy.py | 21 ++++++++++----
|
||||
src/firewall/core/io/zone.py | 4 +--
|
||||
src/firewall/core/ipXtables.py | 25 +++++++++++------
|
||||
src/firewall/core/nftables.py | 7 ++++-
|
||||
src/firewall/core/rich.py | 44 ++++++++++++++++++++++--------
|
||||
6 files changed, 74 insertions(+), 29 deletions(-)
|
||||
|
||||
diff --git a/doc/xml/firewalld.richlanguage.xml b/doc/xml/firewalld.richlanguage.xml
|
||||
index e336bfd0b464..19bd038fc1fd 100644
|
||||
--- a/doc/xml/firewalld.richlanguage.xml
|
||||
+++ b/doc/xml/firewalld.richlanguage.xml
|
||||
@@ -129,7 +129,7 @@ source [not] address="address[/mask]"|mac="mac-address"|ipset="ipset"
|
||||
<title>Destination</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
-destination [not] address="address[/mask]"
|
||||
+destination [not] address="address[/mask]"|ipset="ipset"
|
||||
</programlisting>
|
||||
With the destination address the target can be limited to the destination address. The destination address is using the same syntax as the source address.
|
||||
</para>
|
||||
diff --git a/src/firewall/core/io/policy.py b/src/firewall/core/io/policy.py
|
||||
index c543aa1b42a6..3b951545e975 100644
|
||||
--- a/src/firewall/core/io/policy.py
|
||||
+++ b/src/firewall/core/io/policy.py
|
||||
@@ -186,11 +186,18 @@ def common_startElement(obj, name, attrs):
|
||||
str(obj._rule))
|
||||
return True
|
||||
invert = False
|
||||
+ address = None
|
||||
+ if "address" in attrs:
|
||||
+ address = attrs["address"]
|
||||
+ ipset = None
|
||||
+ if "ipset" in attrs:
|
||||
+ ipset = attrs["ipset"]
|
||||
if "invert" in attrs and \
|
||||
attrs["invert"].lower() in [ "yes", "true" ]:
|
||||
invert = True
|
||||
- obj._rule.destination = rich.Rich_Destination(attrs["address"],
|
||||
- invert)
|
||||
+ obj._rule.destination = rich.Rich_Destination(address,
|
||||
+ ipset,
|
||||
+ invert)
|
||||
|
||||
elif name in [ "accept", "reject", "drop", "mark" ]:
|
||||
if not obj._rule:
|
||||
@@ -447,7 +454,11 @@ def common_writer(obj, handler):
|
||||
|
||||
# destination
|
||||
if rule.destination:
|
||||
- attrs = { "address": rule.destination.addr }
|
||||
+ attrs = { }
|
||||
+ if rule.destination.addr:
|
||||
+ attrs["address"] = rule.destination.addr
|
||||
+ if rule.destination.ipset:
|
||||
+ attrs["ipset"] = rule.destination.ipset
|
||||
if rule.destination.invert:
|
||||
attrs["invert"] = "True"
|
||||
handler.ignorableWhitespace(" ")
|
||||
@@ -607,7 +618,7 @@ class Policy(IO_Object):
|
||||
"forward-port": [ "port", "protocol" ],
|
||||
"rule": None,
|
||||
"source": None,
|
||||
- "destination": [ "address" ],
|
||||
+ "destination": None,
|
||||
"protocol": [ "value" ],
|
||||
"source-port": [ "port", "protocol" ],
|
||||
"log": None,
|
||||
@@ -625,7 +636,7 @@ class Policy(IO_Object):
|
||||
"forward-port": [ "to-port", "to-addr" ],
|
||||
"rule": [ "family", "priority" ],
|
||||
"source": [ "address", "mac", "invert", "family", "ipset" ],
|
||||
- "destination": [ "invert" ],
|
||||
+ "destination": [ "address", "invert", "ipset" ],
|
||||
"log": [ "prefix", "level" ],
|
||||
"reject": [ "type" ],
|
||||
}
|
||||
diff --git a/src/firewall/core/io/zone.py b/src/firewall/core/io/zone.py
|
||||
index 4291ec9cba00..0c419ee0f2bd 100644
|
||||
--- a/src/firewall/core/io/zone.py
|
||||
+++ b/src/firewall/core/io/zone.py
|
||||
@@ -73,7 +73,7 @@ class Zone(IO_Object):
|
||||
"interface": [ "name" ],
|
||||
"rule": None,
|
||||
"source": None,
|
||||
- "destination": [ "address" ],
|
||||
+ "destination": None,
|
||||
"protocol": [ "value" ],
|
||||
"source-port": [ "port", "protocol" ],
|
||||
"log": None,
|
||||
@@ -91,7 +91,7 @@ class Zone(IO_Object):
|
||||
"forward-port": [ "to-port", "to-addr" ],
|
||||
"rule": [ "family", "priority" ],
|
||||
"source": [ "address", "mac", "invert", "family", "ipset" ],
|
||||
- "destination": [ "invert" ],
|
||||
+ "destination": [ "address", "invert", "ipset" ],
|
||||
"log": [ "prefix", "level" ],
|
||||
"reject": [ "type" ],
|
||||
}
|
||||
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
|
||||
index cf6c6e03e7ad..401377104ce1 100644
|
||||
--- a/src/firewall/core/ipXtables.py
|
||||
+++ b/src/firewall/core/ipXtables.py
|
||||
@@ -1093,15 +1093,22 @@ class ip4tables(object):
|
||||
return []
|
||||
|
||||
rule_fragment = []
|
||||
- if rich_dest.invert:
|
||||
- rule_fragment.append("!")
|
||||
- if check_single_address("ipv6", rich_dest.addr):
|
||||
- rule_fragment += [ "-d", normalizeIP6(rich_dest.addr) ]
|
||||
- elif check_address("ipv6", rich_dest.addr):
|
||||
- addr_split = rich_dest.addr.split("/")
|
||||
- rule_fragment += [ "-d", normalizeIP6(addr_split[0]) + "/" + addr_split[1] ]
|
||||
- else:
|
||||
- rule_fragment += [ "-d", rich_dest.addr ]
|
||||
+ if rich_dest.addr:
|
||||
+ if rich_dest.invert:
|
||||
+ rule_fragment.append("!")
|
||||
+ if check_single_address("ipv6", rich_dest.addr):
|
||||
+ rule_fragment += [ "-d", normalizeIP6(rich_dest.addr) ]
|
||||
+ elif check_address("ipv6", rich_dest.addr):
|
||||
+ addr_split = rich_dest.addr.split("/")
|
||||
+ rule_fragment += [ "-d", normalizeIP6(addr_split[0]) + "/" + addr_split[1] ]
|
||||
+ else:
|
||||
+ rule_fragment += [ "-d", rich_dest.addr ]
|
||||
+ elif rich_dest.ipset:
|
||||
+ rule_fragment += [ "-m", "set" ]
|
||||
+ if rich_dest.invert:
|
||||
+ rule_fragment.append("!")
|
||||
+ flags = self._fw.zone._ipset_match_flags(rich_dest.ipset, "dst")
|
||||
+ rule_fragment += [ "--match-set", rich_dest.ipset, flags ]
|
||||
|
||||
return rule_fragment
|
||||
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 2a13b2678a94..d238451ebd5d 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1253,7 +1253,12 @@ class nftables(object):
|
||||
def _rich_rule_destination_fragment(self, rich_dest):
|
||||
if not rich_dest:
|
||||
return {}
|
||||
- return self._rule_addr_fragment("daddr", rich_dest.addr, invert=rich_dest.invert)
|
||||
+ if rich_dest.addr:
|
||||
+ address = rich_dest.addr
|
||||
+ elif rich_dest.ipset:
|
||||
+ address = "ipset:" + rich_dest.ipset
|
||||
+
|
||||
+ return self._rule_addr_fragment("daddr", address, invert=rich_dest.invert)
|
||||
|
||||
def _rich_rule_source_fragment(self, rich_source):
|
||||
if not rich_source:
|
||||
diff --git a/src/firewall/core/rich.py b/src/firewall/core/rich.py
|
||||
index 03bc194c2b28..6a03eeca5d8a 100644
|
||||
--- a/src/firewall/core/rich.py
|
||||
+++ b/src/firewall/core/rich.py
|
||||
@@ -63,13 +63,27 @@ class Rich_Source(object):
|
||||
"no address, mac and ipset")
|
||||
|
||||
class Rich_Destination(object):
|
||||
- def __init__(self, addr, invert=False):
|
||||
+ def __init__(self, addr, ipset, invert=False):
|
||||
self.addr = addr
|
||||
+ if self.addr == "":
|
||||
+ self.addr = None
|
||||
+ self.ipset = ipset
|
||||
+ if self.ipset == "":
|
||||
+ self.ipset = None
|
||||
self.invert = invert
|
||||
+ if self.addr is None and self.ipset is None:
|
||||
+ raise FirewallError(errors.INVALID_RULE,
|
||||
+ "no address and ipset")
|
||||
|
||||
def __str__(self):
|
||||
- return 'destination %saddress="%s"' % ("not " if self.invert else "",
|
||||
- self.addr)
|
||||
+ ret = 'destination%s ' % (" NOT" if self.invert else "")
|
||||
+ if self.addr is not None:
|
||||
+ return ret + 'address="%s"' % self.addr
|
||||
+ elif self.ipset is not None:
|
||||
+ return ret + 'ipset="%s"' % self.ipset
|
||||
+ else:
|
||||
+ raise FirewallError(errors.INVALID_RULE,
|
||||
+ "no address and ipset")
|
||||
|
||||
class Rich_Service(object):
|
||||
def __init__(self, name):
|
||||
@@ -404,12 +418,12 @@ class Rich_Rule(object):
|
||||
attrs.clear()
|
||||
index = index -1 # return token to input
|
||||
elif in_element == 'destination':
|
||||
- if attr_name in ['address', 'invert']:
|
||||
+ if attr_name in ['address', 'ipset', 'invert']:
|
||||
attrs[attr_name] = attr_value
|
||||
elif element in ['not', 'NOT']:
|
||||
attrs['invert'] = True
|
||||
else:
|
||||
- self.destination = Rich_Destination(attrs.get('address'), attrs.get('invert'))
|
||||
+ self.destination = Rich_Destination(attrs.get('address'), attrs.get('ipset'), attrs.get('invert', False))
|
||||
in_elements.pop() # destination
|
||||
attrs.clear()
|
||||
index = index -1 # return token to input
|
||||
@@ -587,12 +601,20 @@ class Rich_Rule(object):
|
||||
|
||||
# destination
|
||||
if self.destination is not None:
|
||||
- if self.family is None:
|
||||
- raise FirewallError(errors.INVALID_FAMILY)
|
||||
- if self.destination.addr is None or \
|
||||
- not functions.check_address(self.family,
|
||||
- self.destination.addr):
|
||||
- raise FirewallError(errors.INVALID_ADDR, str(self.destination.addr))
|
||||
+ if self.destination.addr is not None:
|
||||
+ if self.family is None:
|
||||
+ raise FirewallError(errors.INVALID_FAMILY)
|
||||
+ if self.destination.ipset is not None:
|
||||
+ raise FirewallError(errors.INVALID_DESTINATION, "address and ipset")
|
||||
+ if not functions.check_address(self.family, self.destination.addr):
|
||||
+ raise FirewallError(errors.INVALID_ADDR, str(self.destination.addr))
|
||||
+
|
||||
+ elif self.destination.ipset is not None:
|
||||
+ if not check_ipset_name(self.destination.ipset):
|
||||
+ raise FirewallError(errors.INVALID_IPSET, str(self.destination.ipset))
|
||||
+
|
||||
+ else:
|
||||
+ raise FirewallError(errors.INVALID_RULE, "invalid destination")
|
||||
|
||||
# service
|
||||
if type(self.element) == Rich_Service:
|
||||
--
|
||||
2.43.0
|
||||
|
@ -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,60 +0,0 @@
|
||||
From cf8a55d1fe769a9e4632fbccf5ae4738ab661421 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Thu, 12 Nov 2020 17:11:58 -0500
|
||||
Subject: [PATCH 19/26] v1.0.0: test(rich): destination ipset
|
||||
|
||||
(cherry picked from commit f274bfd0f7bc0e466c42b732e03002e11e99ed88)
|
||||
---
|
||||
src/tests/features/features.at | 1 +
|
||||
src/tests/features/rich_destination_ipset.at | 30 ++++++++++++++++++++
|
||||
2 files changed, 31 insertions(+)
|
||||
create mode 100644 src/tests/features/rich_destination_ipset.at
|
||||
|
||||
diff --git a/src/tests/features/features.at b/src/tests/features/features.at
|
||||
index 2340853aeca7..381bf6dba0e4 100644
|
||||
--- a/src/tests/features/features.at
|
||||
+++ b/src/tests/features/features.at
|
||||
@@ -13,3 +13,4 @@ m4_include([features/rich_rules.at])
|
||||
m4_include([features/icmp_blocks.at])
|
||||
m4_include([features/rpfilter.at])
|
||||
m4_include([features/zone_combine.at])
|
||||
+m4_include([features/rich_destination_ipset.at])
|
||||
diff --git a/src/tests/features/rich_destination_ipset.at b/src/tests/features/rich_destination_ipset.at
|
||||
new file mode 100644
|
||||
index 000000000000..c07809141851
|
||||
--- /dev/null
|
||||
+++ b/src/tests/features/rich_destination_ipset.at
|
||||
@@ -0,0 +1,30 @@
|
||||
+FWD_START_TEST([rich destination ipset])
|
||||
+AT_KEYWORDS(rich ipset)
|
||||
+
|
||||
+FWD_CHECK([--permanent --new-ipset=foobar --type=hash:ip], 0, [ignore])
|
||||
+FWD_RELOAD
|
||||
+
|
||||
+FWD_CHECK([--permanent --add-rich-rule='rule family=ipv4 destination ipset=foobar accept'], 0, [ignore])
|
||||
+FWD_CHECK([ --add-rich-rule='rule family=ipv4 destination ipset=foobar accept'], 0, [ignore])
|
||||
+NFT_LIST_RULES([inet], [filter_IN_public_allow], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_IN_public_allow {
|
||||
+ tcp dport 22 ct state new,untracked accept
|
||||
+ ip6 daddr fe80::/64 udp dport 546 ct state new,untracked accept
|
||||
+ ip daddr @foobar accept
|
||||
+ }
|
||||
+ }
|
||||
+])
|
||||
+IPTABLES_LIST_RULES([filter], [IN_public_allow], 0, [dnl
|
||||
+ ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ctstate NEW,UNTRACKED
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set foobar dst
|
||||
+])
|
||||
+
|
||||
+dnl negative tests
|
||||
+FWD_CHECK([--permanent --add-rich-rule='rule family=ipv4 destination bogus=foobar accept'], 122, [ignore], [ignore])
|
||||
+FWD_CHECK([ --add-rich-rule='rule family=ipv4 destination bogus=foobar accept'], 122, [ignore], [ignore])
|
||||
+FWD_CHECK([--permanent --add-rich-rule='rule family=ipv4 destination address=10.0.0.1 ipset=foobar accept'], 121, [ignore], [ignore])
|
||||
+FWD_CHECK([ --add-rich-rule='rule family=ipv4 destination address=10.0.0.1 ipset=foobar accept'], 121, [ignore], [ignore])
|
||||
+
|
||||
+FWD_END_TEST([-e '/ERROR: INVALID_RULE: bad attribute/d'dnl
|
||||
+ -e '/ERROR: INVALID_DESTINATION: address and ipset/d'])
|
||||
--
|
||||
2.43.0
|
||||
|
@ -0,0 +1,31 @@
|
||||
From e031ce8e41d2fc23735be91f7070127f8812b490 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Fri, 21 Jun 2024 16:17:44 -0400
|
||||
Subject: [PATCH 19/22] v2.2.0: test(functions): start firewalld with file
|
||||
logging
|
||||
|
||||
This is a test environment. Trying to log to syslog does not make sense.
|
||||
The default, mixed, sends to both file and syslog, but with different
|
||||
log level settings.
|
||||
|
||||
(cherry picked from commit 5e0c28d5f7f71a216485169610d8c72a3a3518d1)
|
||||
---
|
||||
src/tests/functions.at | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index b2372dd4075b..d1c89ed5b982 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -9,7 +9,7 @@ m4_define([FWD_STOP_FIREWALLD], [
|
||||
])
|
||||
|
||||
m4_define([FWD_START_FIREWALLD], [
|
||||
- FIREWALLD_ARGS="--nofork --nopid --log-file ./firewalld.log --system-config ./"
|
||||
+ FIREWALLD_ARGS="--nofork --nopid --log-file ./firewalld.log --log-target file --system-config ./"
|
||||
dnl if testsuite ran with debug flag, add debug output
|
||||
${at_debug_p} && FIREWALLD_ARGS="--debug=9 ${FIREWALLD_ARGS}"
|
||||
if test "x${FIREWALLD_DEFAULT_CONFIG}" != x ; then
|
||||
--
|
||||
2.43.5
|
||||
|
@ -1,63 +0,0 @@
|
||||
From 63100ca625942e6be2c68422e7a48bc68f8d01c5 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Fri, 13 Nov 2020 13:32:22 -0500
|
||||
Subject: [PATCH 20/26] v1.0.0: test(rich): destination ipset: verify policy
|
||||
support
|
||||
|
||||
(cherry picked from commit fdd120572cd45a6ea2515bc906b89482de6560ea)
|
||||
---
|
||||
src/tests/features/rich_destination_ipset.at | 23 ++++++++++++++++++++
|
||||
1 file changed, 23 insertions(+)
|
||||
|
||||
diff --git a/src/tests/features/rich_destination_ipset.at b/src/tests/features/rich_destination_ipset.at
|
||||
index c07809141851..3286755d2252 100644
|
||||
--- a/src/tests/features/rich_destination_ipset.at
|
||||
+++ b/src/tests/features/rich_destination_ipset.at
|
||||
@@ -1,9 +1,14 @@
|
||||
FWD_START_TEST([rich destination ipset])
|
||||
AT_KEYWORDS(rich ipset)
|
||||
|
||||
+FWD_CHECK([--permanent --new-policy=mypolicy], 0, [ignore])
|
||||
+FWD_CHECK([--permanent --policy=mypolicy --add-ingress-zone ANY], 0, [ignore])
|
||||
+FWD_CHECK([--permanent --policy=mypolicy --add-egress-zone HOST], 0, [ignore])
|
||||
+
|
||||
FWD_CHECK([--permanent --new-ipset=foobar --type=hash:ip], 0, [ignore])
|
||||
FWD_RELOAD
|
||||
|
||||
+dnl zone
|
||||
FWD_CHECK([--permanent --add-rich-rule='rule family=ipv4 destination ipset=foobar accept'], 0, [ignore])
|
||||
FWD_CHECK([ --add-rich-rule='rule family=ipv4 destination ipset=foobar accept'], 0, [ignore])
|
||||
NFT_LIST_RULES([inet], [filter_IN_public_allow], 0, [dnl
|
||||
@@ -20,11 +25,29 @@ IPTABLES_LIST_RULES([filter], [IN_public_allow], 0, [dnl
|
||||
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set foobar dst
|
||||
])
|
||||
|
||||
+dnl policy
|
||||
+FWD_CHECK([--permanent --policy mypolicy --add-rich-rule='rule family=ipv4 destination ipset=foobar accept'], 0, [ignore])
|
||||
+FWD_CHECK([ --policy mypolicy --add-rich-rule='rule family=ipv4 destination ipset=foobar accept'], 0, [ignore])
|
||||
+NFT_LIST_RULES([inet], [filter_IN_policy_mypolicy_allow], 0, [dnl
|
||||
+ table inet firewalld {
|
||||
+ chain filter_IN_policy_mypolicy_allow {
|
||||
+ ip daddr @foobar accept
|
||||
+ }
|
||||
+ }
|
||||
+])
|
||||
+IPTABLES_LIST_RULES([filter], [IN_mypolicy_allow], 0, [dnl
|
||||
+ ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set foobar dst
|
||||
+])
|
||||
+
|
||||
dnl negative tests
|
||||
FWD_CHECK([--permanent --add-rich-rule='rule family=ipv4 destination bogus=foobar accept'], 122, [ignore], [ignore])
|
||||
FWD_CHECK([ --add-rich-rule='rule family=ipv4 destination bogus=foobar accept'], 122, [ignore], [ignore])
|
||||
FWD_CHECK([--permanent --add-rich-rule='rule family=ipv4 destination address=10.0.0.1 ipset=foobar accept'], 121, [ignore], [ignore])
|
||||
FWD_CHECK([ --add-rich-rule='rule family=ipv4 destination address=10.0.0.1 ipset=foobar accept'], 121, [ignore], [ignore])
|
||||
+FWD_CHECK([--permanent --policy mypolicy --add-rich-rule='rule family=ipv4 destination bogus=foobar accept'], 122, [ignore], [ignore])
|
||||
+FWD_CHECK([ --policy mypolicy --add-rich-rule='rule family=ipv4 destination bogus=foobar accept'], 122, [ignore], [ignore])
|
||||
+FWD_CHECK([--permanent --policy mypolicy --add-rich-rule='rule family=ipv4 destination address=10.0.0.1 ipset=foobar accept'], 121, [ignore], [ignore])
|
||||
+FWD_CHECK([ --policy mypolicy --add-rich-rule='rule family=ipv4 destination address=10.0.0.1 ipset=foobar accept'], 121, [ignore], [ignore])
|
||||
|
||||
FWD_END_TEST([-e '/ERROR: INVALID_RULE: bad attribute/d'dnl
|
||||
-e '/ERROR: INVALID_DESTINATION: address and ipset/d'])
|
||||
--
|
||||
2.43.0
|
||||
|
417
SOURCES/0020-v2.2.0-feat-nftables-table-ownership.patch
Normal file
417
SOURCES/0020-v2.2.0-feat-nftables-table-ownership.patch
Normal file
@ -0,0 +1,417 @@
|
||||
From ce75ad2920f457d5b2ce578ead5d8c64f0daa490 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Fri, 21 Jun 2024 17:38:29 -0400
|
||||
Subject: [PATCH 20/22] v2.2.0: feat(nftables): table ownership
|
||||
|
||||
This allows using the nftables table flags "owner" and "persist". When
|
||||
these are in use ONLY firewalld will be able to change the firewalld
|
||||
table/rules. All other processes will be blocked. This enhances
|
||||
firewalld robustness by guaranteeing that firewalld's rules can not be
|
||||
modified without it's knowledge, e.g. by other entities.
|
||||
|
||||
This also allows firewalld and nftables services to coexist. An "nft
|
||||
flush ruleset" will NOT flush the "firewalld" table.
|
||||
|
||||
Fixes: RHEL-17002
|
||||
(cherry picked from commit becd083fc2905921651af73cb15ce8c9aba9203b)
|
||||
---
|
||||
config/firewalld.conf | 8 +++
|
||||
doc/xml/firewalld.conf.xml | 13 ++++
|
||||
doc/xml/firewalld.dbus.xml | 11 ++++
|
||||
src/firewall/config/__init__.py.in | 1 +
|
||||
src/firewall/core/fw.py | 22 +++++++
|
||||
src/firewall/core/io/firewalld_conf.py | 6 +-
|
||||
src/firewall/core/nftables.py | 83 ++++++++++++++++++++++----
|
||||
src/firewall/server/config.py | 16 +++--
|
||||
src/tests/dbus/firewalld.conf.at | 2 +
|
||||
src/tests/regression/rhbz2222044.at | 5 ++
|
||||
10 files changed, 150 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/config/firewalld.conf b/config/firewalld.conf
|
||||
index cf95af3eea8e..28345a13d54e 100644
|
||||
--- a/config/firewalld.conf
|
||||
+++ b/config/firewalld.conf
|
||||
@@ -93,3 +93,11 @@ ReloadPolicy=INPUT:DROP,FORWARD:DROP,OUTPUT:DROP
|
||||
# internet.
|
||||
# Defaults to "yes".
|
||||
RFC3964_IPv4=yes
|
||||
+
|
||||
+# NftablesTableOwner
|
||||
+# If set to yes, the generated nftables rule set will be owned exclusively by
|
||||
+# firewalld. This prevents other entities from mistakenly (or maliciously)
|
||||
+# modifying firewalld's rule set. If you intentionally modify firewalld's
|
||||
+# rules, then you will have to set this to "no".
|
||||
+# Defaults to "yes".
|
||||
+NftablesTableOwner=yes
|
||||
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
|
||||
index 4b9bfdad15be..cf7134c04012 100644
|
||||
--- a/doc/xml/firewalld.conf.xml
|
||||
+++ b/doc/xml/firewalld.conf.xml
|
||||
@@ -247,6 +247,19 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
+ <varlistentry>
|
||||
+ <term><option>NftablesTableOwner</option></term>
|
||||
+ <listitem>
|
||||
+ <para>
|
||||
+ If set to yes, the generated nftables rule set will be owned exclusively by
|
||||
+ firewalld. This prevents other entities from mistakenly (or maliciously)
|
||||
+ modifying firewalld's rule set. If you intentionally modify firewalld's
|
||||
+ rules, then you will have to set this to "no".
|
||||
+ Defaults to "yes".
|
||||
+ </para>
|
||||
+ </listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
|
||||
index f04cf5ae757b..00d7e8fbe5b1 100644
|
||||
--- a/doc/xml/firewalld.dbus.xml
|
||||
+++ b/doc/xml/firewalld.dbus.xml
|
||||
@@ -2905,6 +2905,17 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
+ <varlistentry id="FirewallD1.config.Properties.NftablesTableOwner">
|
||||
+ <term>NftablesTableOwner - s - (rw)</term>
|
||||
+ <listitem>
|
||||
+ <para>
|
||||
+ If set to yes, the generated nftables rule set will be owned exclusively by
|
||||
+ firewalld. This prevents other entities from mistakenly (or maliciously)
|
||||
+ modifying firewalld's rule set. If you intentionally modify firewalld's
|
||||
+ rules, then you will have to set this to "no".
|
||||
+ </para>
|
||||
+ </listitem>
|
||||
+ </varlistentry>
|
||||
</variablelist>
|
||||
</refsect3>
|
||||
</refsect2>
|
||||
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
|
||||
index 2eda337e8180..16291fcf795a 100644
|
||||
--- a/src/firewall/config/__init__.py.in
|
||||
+++ b/src/firewall/config/__init__.py.in
|
||||
@@ -138,3 +138,4 @@ FALLBACK_FLUSH_ALL_ON_RELOAD = True
|
||||
FALLBACK_RELOAD_POLICY = "INPUT:DROP,FORWARD:DROP,OUTPUT:DROP"
|
||||
FALLBACK_RFC3964_IPV4 = True
|
||||
FALLBACK_ALLOW_ZONE_DRIFTING = False
|
||||
+FALLBACK_NFTABLES_TABLE_OWNER = True
|
||||
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
|
||||
index f91ff53fc37a..557b6e527dbd 100644
|
||||
--- a/src/firewall/core/fw.py
|
||||
+++ b/src/firewall/core/fw.py
|
||||
@@ -119,6 +119,7 @@ class Firewall(object):
|
||||
self._flush_all_on_reload = config.FALLBACK_FLUSH_ALL_ON_RELOAD
|
||||
self._rfc3964_ipv4 = config.FALLBACK_RFC3964_IPV4
|
||||
self._allow_zone_drifting = config.FALLBACK_ALLOW_ZONE_DRIFTING
|
||||
+ self._nftables_table_owner = config.FALLBACK_NFTABLES_TABLE_OWNER
|
||||
|
||||
if self._offline:
|
||||
self.ip4tables_enabled = False
|
||||
@@ -290,6 +291,17 @@ class Firewall(object):
|
||||
log.debug1("ebtables-restore is not supporting the --noflush "
|
||||
"option, will therefore not be used")
|
||||
|
||||
+ self.nftables_backend.probe_support()
|
||||
+
|
||||
+ if (
|
||||
+ self._nftables_table_owner
|
||||
+ and not self.nftables_backend.supports_table_owner
|
||||
+ ):
|
||||
+ log.info1(
|
||||
+ "Configuration has NftablesTableOwner=True, but it's "
|
||||
+ "not supported by nftables. Table ownership will be disabled."
|
||||
+ )
|
||||
+
|
||||
def _start_load_firewalld_conf(self):
|
||||
# load firewalld config
|
||||
log.debug1("Loading firewalld config file '%s'", config.FIREWALLD_CONF)
|
||||
@@ -378,6 +390,16 @@ class Firewall(object):
|
||||
log.debug1("RFC3964_IPv4 is set to '%s'",
|
||||
self._rfc3964_ipv4)
|
||||
|
||||
+ if self._firewalld_conf.get("NftablesTableOwner"):
|
||||
+ value = self._firewalld_conf.get("NftablesTableOwner")
|
||||
+ if value.lower() in ["no", "false"]:
|
||||
+ self._nftables_table_owner = False
|
||||
+ else:
|
||||
+ self._nftables_table_owner = True
|
||||
+ log.debug1(
|
||||
+ "NftablesTableOwner is set to '%s'", self._nftables_table_owner
|
||||
+ )
|
||||
+
|
||||
self.config.set_firewalld_conf(copy.deepcopy(self._firewalld_conf))
|
||||
|
||||
def _start_load_lockdown_whitelist(self):
|
||||
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
|
||||
index 20072e26cfcd..d5b47666c0f0 100644
|
||||
--- a/src/firewall/core/io/firewalld_conf.py
|
||||
+++ b/src/firewall/core/io/firewalld_conf.py
|
||||
@@ -32,7 +32,7 @@ valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit",
|
||||
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
- "AllowZoneDrifting", "ReloadPolicy" ]
|
||||
+ "AllowZoneDrifting", "ReloadPolicy", "NftablesTableOwner" ]
|
||||
|
||||
class firewalld_conf(object):
|
||||
def __init__(self, filename):
|
||||
@@ -81,6 +81,10 @@ class firewalld_conf(object):
|
||||
self.set("ReloadPolicy", config.FALLBACK_RELOAD_POLICY)
|
||||
self.set("RFC3964_IPv4", "yes" if config.FALLBACK_RFC3964_IPV4 else "no")
|
||||
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
|
||||
+ self.set(
|
||||
+ "NftablesTableOwner",
|
||||
+ "yes" if config.FALLBACK_NFTABLES_TABLE_OWNER else "no",
|
||||
+ )
|
||||
|
||||
def sanity_check(self):
|
||||
if self.get("FirewallBackend") == "iptables" and self.get("IPv6_rpfilter") in (
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 5a49b34e3a4f..8115bcb9d7f4 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -36,6 +36,7 @@ from nftables.nftables import Nftables
|
||||
|
||||
TABLE_NAME = "firewalld"
|
||||
TABLE_NAME_POLICY = TABLE_NAME + "_" + "policy_drop"
|
||||
+TABLE_NAME_PROBE = TABLE_NAME + "_" + "probe"
|
||||
POLICY_CHAIN_PREFIX = "policy_"
|
||||
|
||||
# Map iptables (table, chain) to hooks and priorities.
|
||||
@@ -169,6 +170,7 @@ class nftables(object):
|
||||
def __init__(self, fw):
|
||||
self._fw = fw
|
||||
self.restore_command_exists = True
|
||||
+ self.supports_table_owner = False
|
||||
self.available_tables = []
|
||||
self.rule_to_handle = {}
|
||||
self.rule_ref_count = {}
|
||||
@@ -180,6 +182,57 @@ class nftables(object):
|
||||
self.nftables.set_echo_output(True)
|
||||
self.nftables.set_handle_output(True)
|
||||
|
||||
+ def _probe_support_table_owner(self):
|
||||
+ try:
|
||||
+ rules = {
|
||||
+ "nftables": [
|
||||
+ {"metainfo": {"json_schema_version": 1}},
|
||||
+ {
|
||||
+ "add": {
|
||||
+ "table": {
|
||||
+ "family": "inet",
|
||||
+ "name": TABLE_NAME_PROBE,
|
||||
+ "flags": ["owner", "persist"],
|
||||
+ }
|
||||
+ }
|
||||
+ },
|
||||
+ ]
|
||||
+ }
|
||||
+
|
||||
+ rc, output, _ = self.nftables.json_cmd(rules)
|
||||
+ if rc:
|
||||
+ raise ValueError("nftables probe table owner failed")
|
||||
+
|
||||
+ # old nftables versions would ignore table flags in JSON, so we
|
||||
+ # must parse back and verify the flags are set.
|
||||
+ rules = {
|
||||
+ "nftables": [
|
||||
+ {"metainfo": {"json_schema_version": 1}},
|
||||
+ {"list": {"table": {"family": "inet", "name": TABLE_NAME_PROBE}}},
|
||||
+ ]
|
||||
+ }
|
||||
+ self.nftables.set_echo_output(False)
|
||||
+ rc, output, _ = self.nftables.json_cmd(rules)
|
||||
+ self.nftables.set_echo_output(True)
|
||||
+ flags = output["nftables"][1]["table"]["flags"]
|
||||
+
|
||||
+ self.set_rule(
|
||||
+ {"delete": {"table": {"family": "inet", "name": TABLE_NAME_PROBE}}},
|
||||
+ self._fw.get_log_denied(),
|
||||
+ )
|
||||
+
|
||||
+ if "owner" not in flags or "persist" not in flags:
|
||||
+ raise ValueError("nftables probe table owner failed")
|
||||
+
|
||||
+ log.debug2("nftables: probe_support(): owner flag is supported.")
|
||||
+ self.supports_table_owner = True
|
||||
+ except:
|
||||
+ log.debug2("nftables: probe_support(): owner flag is NOT supported.")
|
||||
+ self.supports_table_owner = False
|
||||
+
|
||||
+ def probe_support(self):
|
||||
+ self._probe_support_table_owner()
|
||||
+
|
||||
def _run_replace_zone_source(self, rule, zone_source_index_cache):
|
||||
for verb in ["add", "insert", "delete"]:
|
||||
if verb in rule:
|
||||
@@ -401,16 +454,27 @@ class nftables(object):
|
||||
# Tables always exist in nftables
|
||||
return [table] if table else IPTABLES_TO_NFT_HOOK.keys()
|
||||
|
||||
+ def _build_add_table_rules(self, table):
|
||||
+ rule = {"add": {"table": {"family": "inet", "name": table}}}
|
||||
+
|
||||
+ if (
|
||||
+ table == TABLE_NAME
|
||||
+ and self._fw._nftables_table_owner
|
||||
+ and self.supports_table_owner
|
||||
+ ):
|
||||
+ rule["add"]["table"]["flags"] = ["owner", "persist"]
|
||||
+
|
||||
+ return [rule]
|
||||
+
|
||||
def _build_delete_table_rules(self, table):
|
||||
# To avoid nftables returning ENOENT we always add the table before
|
||||
# deleting to guarantee it will exist.
|
||||
#
|
||||
# In the future, this add+delete should be replaced with "destroy", but
|
||||
# that verb is too new to rely upon.
|
||||
- return [{"add": {"table": {"family": "inet",
|
||||
- "name": table}}},
|
||||
- {"delete": {"table": {"family": "inet",
|
||||
- "name": table}}}]
|
||||
+ return self._build_add_table_rules(table) + [
|
||||
+ {"delete": {"table": {"family": "inet", "name": table}}},
|
||||
+ ]
|
||||
|
||||
def build_flush_rules(self):
|
||||
self.rule_to_handle = {}
|
||||
@@ -437,8 +501,7 @@ class nftables(object):
|
||||
# a higher priority than our base chains is sufficient.
|
||||
rules = []
|
||||
if policy == "PANIC":
|
||||
- rules.append({"add": {"table": {"family": "inet",
|
||||
- "name": TABLE_NAME_POLICY}}})
|
||||
+ rules.extend(self._build_add_table_rules(TABLE_NAME_POLICY))
|
||||
|
||||
# Use "raw" priority for panic mode. This occurs before
|
||||
# conntrack, mangle, nat, etc
|
||||
@@ -451,8 +514,7 @@ class nftables(object):
|
||||
"prio": -300 + NFT_HOOK_OFFSET - 1,
|
||||
"policy": "drop"}}})
|
||||
elif policy == "DROP":
|
||||
- rules.append({"add": {"table": {"family": "inet",
|
||||
- "name": TABLE_NAME_POLICY}}})
|
||||
+ rules.extend(self._build_add_table_rules(TABLE_NAME_POLICY))
|
||||
|
||||
# To drop everything except existing connections we use
|
||||
# "filter" because it occurs _after_ conntrack.
|
||||
@@ -511,10 +573,7 @@ class nftables(object):
|
||||
return list(supported)
|
||||
|
||||
def build_default_tables(self):
|
||||
- default_tables = []
|
||||
- default_tables.append({"add": {"table": {"family": "inet",
|
||||
- "name": TABLE_NAME}}})
|
||||
- return default_tables
|
||||
+ return self._build_add_table_rules(TABLE_NAME)
|
||||
|
||||
def build_default_rules(self, log_denied="off"):
|
||||
default_rules = []
|
||||
diff --git a/src/firewall/server/config.py b/src/firewall/server/config.py
|
||||
index b805e497bb05..c07a1e6a2503 100644
|
||||
--- a/src/firewall/server/config.py
|
||||
+++ b/src/firewall/server/config.py
|
||||
@@ -110,6 +110,7 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"FlushAllOnReload": "readwrite",
|
||||
"RFC3964_IPv4": "readwrite",
|
||||
"AllowZoneDrifting": "readwrite",
|
||||
+ "NftablesTableOwner": "readwrite",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -565,7 +566,7 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
- "AllowZoneDrifting", "IPv6_rpfilter2" ]:
|
||||
+ "AllowZoneDrifting", "IPv6_rpfilter2", "NftablesTableOwner" ]:
|
||||
raise dbus.exceptions.DBusException(
|
||||
"org.freedesktop.DBus.Error.InvalidArgs: "
|
||||
"Property '%s' does not exist" % prop)
|
||||
@@ -631,6 +632,10 @@ class FirewallDConfig(DbusServiceObject):
|
||||
if value is None:
|
||||
value = "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no"
|
||||
return dbus.String(value)
|
||||
+ elif prop == "NftablesTableOwner":
|
||||
+ if value is None:
|
||||
+ value = "yes" if config.FALLBACK_NFTABLES_TABLE_OWNER else "no"
|
||||
+ return dbus.String(value)
|
||||
|
||||
@dbus_handle_exceptions
|
||||
def _get_dbus_property(self, prop):
|
||||
@@ -662,6 +667,8 @@ class FirewallDConfig(DbusServiceObject):
|
||||
return dbus.String(self._get_property(prop))
|
||||
elif prop == "AllowZoneDrifting":
|
||||
return dbus.String(self._get_property(prop))
|
||||
+ elif prop == "NftablesTableOwner":
|
||||
+ return dbus.String(self._get_property(prop))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
"org.freedesktop.DBus.Error.InvalidArgs: "
|
||||
@@ -701,7 +708,7 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "LogDenied", "AutomaticHelpers",
|
||||
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
|
||||
- "AllowZoneDrifting", "IPv6_rpfilter2" ]:
|
||||
+ "AllowZoneDrifting", "IPv6_rpfilter2", "NftablesTableOwner" ]:
|
||||
ret[x] = self._get_property(x)
|
||||
elif interface_name in [ config.dbus.DBUS_INTERFACE_CONFIG_DIRECT,
|
||||
config.dbus.DBUS_INTERFACE_CONFIG_POLICIES ]:
|
||||
@@ -730,11 +737,12 @@ class FirewallDConfig(DbusServiceObject):
|
||||
"IPv6_rpfilter", "IndividualCalls",
|
||||
"LogDenied",
|
||||
"FirewallBackend", "FlushAllOnReload",
|
||||
- "RFC3964_IPv4", "IPv6_rpfilter2"]:
|
||||
+ "RFC3964_IPv4", "IPv6_rpfilter2",
|
||||
+ "NftablesTableOwner" ]:
|
||||
if property_name in [ "CleanupOnExit", "CleanupModulesOnExit",
|
||||
"Lockdown", "IPv6_rpfilter",
|
||||
"IndividualCalls", "FlushAllOnReload",
|
||||
- "RFC3964_IPv4"]:
|
||||
+ "RFC3964_IPv4", "NftablesTableOwner" ]:
|
||||
if new_value.lower() not in [ "yes", "no",
|
||||
"true", "false" ]:
|
||||
raise FirewallError(errors.INVALID_VALUE,
|
||||
diff --git a/src/tests/dbus/firewalld.conf.at b/src/tests/dbus/firewalld.conf.at
|
||||
index 61c220f3e59c..2ead41baa00a 100644
|
||||
--- a/src/tests/dbus/firewalld.conf.at
|
||||
+++ b/src/tests/dbus/firewalld.conf.at
|
||||
@@ -30,6 +30,7 @@ string "IndividualCalls" : variant string m4_escape(["${EXPECTED_INDIVIDUAL_CALL
|
||||
string "Lockdown" : variant string "no"
|
||||
string "LogDenied" : variant string "off"
|
||||
string "MinimalMark" : variant int32 100
|
||||
+string "NftablesTableOwner" : variant string "yes"
|
||||
string "RFC3964_IPv4" : variant string "yes"
|
||||
])
|
||||
|
||||
@@ -54,6 +55,7 @@ _helper([CleanupModulesOnExit], [string:"yes"], [variant string "yes"])
|
||||
_helper([CleanupOnExit], [string:"no"], [variant string "no"])
|
||||
_helper([RFC3964_IPv4], [string:"no"], [variant string "no"])
|
||||
_helper([AllowZoneDrifting], [string:"yes"], [variant string "no"])
|
||||
+_helper([NftablesTableOwner], [string:"no"], [variant string "no"])
|
||||
dnl Note: DefaultZone is RO
|
||||
m4_undefine([_helper])
|
||||
|
||||
diff --git a/src/tests/regression/rhbz2222044.at b/src/tests/regression/rhbz2222044.at
|
||||
index 7e3454509188..2d0333865076 100644
|
||||
--- a/src/tests/regression/rhbz2222044.at
|
||||
+++ b/src/tests/regression/rhbz2222044.at
|
||||
@@ -2,6 +2,11 @@ FWD_START_TEST([duplicate rules after restart])
|
||||
AT_KEYWORDS(rhbz2222044)
|
||||
AT_SKIP_IF([! NS_CMD([command -v wc >/dev/null 2>&1])])
|
||||
|
||||
+dnl Disable for this test because CI do not support table owner. It's very new
|
||||
+dnl in nftables.
|
||||
+AT_CHECK([sed -i 's/^NftablesTableOwner=.*/NftablesTableOwner=no/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
dnl rules have not changed so rule count should not change
|
||||
m4_define([check_rule_count], [
|
||||
m4_if(nftables, FIREWALL_BACKEND, [
|
||||
--
|
||||
2.43.5
|
||||
|
69
SOURCES/0021-v2.2.0-test-nftables-table-ownership.patch
Normal file
69
SOURCES/0021-v2.2.0-test-nftables-table-ownership.patch
Normal file
@ -0,0 +1,69 @@
|
||||
From bf91ea35e7faf66484bdae7d0b3260c4717ee39a Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 18 Jun 2024 16:20:06 -0400
|
||||
Subject: [PATCH 21/22] v2.2.0: test(nftables): table ownership
|
||||
|
||||
Coverage: RHEL-17002
|
||||
(cherry picked from commit e7728b843c2ec3a61dbe436575c977e2ad9c8674)
|
||||
---
|
||||
src/tests/features/features.at | 1 +
|
||||
src/tests/features/nftables_table_owner.at | 38 ++++++++++++++++++++++
|
||||
2 files changed, 39 insertions(+)
|
||||
create mode 100644 src/tests/features/nftables_table_owner.at
|
||||
|
||||
diff --git a/src/tests/features/features.at b/src/tests/features/features.at
|
||||
index 065cb2872e88..83ad9d122189 100644
|
||||
--- a/src/tests/features/features.at
|
||||
+++ b/src/tests/features/features.at
|
||||
@@ -21,3 +21,4 @@ m4_include([features/ipset.at])
|
||||
m4_include([features/reset_defaults.at])
|
||||
m4_include([features/iptables_no_flush_on_shutdown.at])
|
||||
m4_include([features/reloadpolicy.at])
|
||||
+m4_include([features/nftables_table_owner.at])
|
||||
diff --git a/src/tests/features/nftables_table_owner.at b/src/tests/features/nftables_table_owner.at
|
||||
new file mode 100644
|
||||
index 000000000000..abc946da0ad7
|
||||
--- /dev/null
|
||||
+++ b/src/tests/features/nftables_table_owner.at
|
||||
@@ -0,0 +1,38 @@
|
||||
+m4_if(nftables, FIREWALL_BACKEND, [
|
||||
+FWD_START_TEST([nftables table owner])
|
||||
+AT_KEYWORDS(RHEL-17002)
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^NftablesTableOwner=.*/NftablesTableOwner=yes/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+AT_SKIP_IF([grep "Configuration has NftablesTableOwner=True, but it's not supported by nftables." ./firewalld.log])
|
||||
+
|
||||
+NS_CHECK([nft list table inet firewalld | TRIM_WHITESPACE | head -n 2], 0, [m4_strip([dnl
|
||||
+ table inet firewalld { # progname firewalld
|
||||
+ flags owner,persist
|
||||
+])])
|
||||
+
|
||||
+dnl Test the transitions from On to Off
|
||||
+dnl
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^NftablesTableOwner=.*/NftablesTableOwner=no/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+NS_CHECK([nft list table inet firewalld | TRIM_WHITESPACE | head -n 2], 0, [m4_strip([dnl
|
||||
+ table inet firewalld {
|
||||
+ chain mangle_PREROUTING {
|
||||
+])])
|
||||
+
|
||||
+dnl Test the transitions from Off to On
|
||||
+dnl
|
||||
+
|
||||
+AT_CHECK([sed -i 's/^NftablesTableOwner=.*/NftablesTableOwner=yes/' ./firewalld.conf])
|
||||
+FWD_RELOAD()
|
||||
+
|
||||
+NS_CHECK([nft list table inet firewalld | TRIM_WHITESPACE | head -n 2], 0, [m4_strip([dnl
|
||||
+ table inet firewalld { # progname firewalld
|
||||
+ flags owner,persist
|
||||
+])])
|
||||
+
|
||||
+FWD_END_TEST()
|
||||
+])
|
||||
--
|
||||
2.43.5
|
||||
|
@ -0,0 +1,29 @@
|
||||
From 8d87974a34c055e0daee3a83f11c539fc98fd6bf Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Wed, 26 Jun 2024 15:31:38 -0400
|
||||
Subject: [PATCH 22/22] v2.2.0: chore(service): remove Conflicts with nftables
|
||||
|
||||
Now that firewalld uses the table owner flag it can coexist with the
|
||||
nftables service.
|
||||
|
||||
(cherry picked from commit 9c6cb982981ad0aee5b85773823614ca7fd69073)
|
||||
---
|
||||
config/firewalld.service.in | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/config/firewalld.service.in b/config/firewalld.service.in
|
||||
index afbe0ac5def7..b757a08f28dc 100644
|
||||
--- a/config/firewalld.service.in
|
||||
+++ b/config/firewalld.service.in
|
||||
@@ -4,7 +4,7 @@ Before=network-pre.target
|
||||
Wants=network-pre.target
|
||||
After=dbus.service
|
||||
After=polkit.service
|
||||
-Conflicts=iptables.service ip6tables.service ebtables.service ipset.service nftables.service
|
||||
+Conflicts=iptables.service ip6tables.service ebtables.service ipset.service
|
||||
Documentation=man:firewalld(1)
|
||||
|
||||
[Service]
|
||||
--
|
||||
2.43.5
|
||||
|
@ -1,27 +0,0 @@
|
||||
From 39e8946ba75fc3ce36c3ff72e3af1fb2ae0d95ec Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Mon, 5 Feb 2024 13:24:25 +0100
|
||||
Subject: [PATCH 23/26] v2.2.0: fix(rich): fix range check for large rule limit
|
||||
|
||||
Fixes: 555ae1307a3e ('New rich language usable in zones')
|
||||
(cherry picked from commit e790c64ebb5760e8d8f8afd1b978baab891d5933)
|
||||
---
|
||||
src/firewall/core/rich.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/firewall/core/rich.py b/src/firewall/core/rich.py
|
||||
index 6a03eeca5d8a..b150a0dca402 100644
|
||||
--- a/src/firewall/core/rich.py
|
||||
+++ b/src/firewall/core/rich.py
|
||||
@@ -264,7 +264,7 @@ class Rich_Limit(object):
|
||||
elif duration == "d":
|
||||
mult = 24*60*60
|
||||
|
||||
- if 10000 * mult / rate == 0:
|
||||
+ if 10000 * mult // rate == 0:
|
||||
raise FirewallError(errors.INVALID_LIMIT,
|
||||
"%s too fast" % self.value)
|
||||
|
||||
--
|
||||
2.43.0
|
||||
|
@ -1,63 +0,0 @@
|
||||
From 028529e33ed45507bcb1f3eb2722de3344eea091 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Mon, 5 Feb 2024 13:09:02 +0100
|
||||
Subject: [PATCH 24/26] v2.2.0: improvement(policy): extract helper function
|
||||
for writing limit rule element
|
||||
|
||||
Soon the Rich_Limit will also get a burst attribute. Then _handler_add_rich_limit()
|
||||
will become more complicated. We wouldn't want to duplicated that code.
|
||||
|
||||
(cherry picked from commit f662606891569f09553c73023a2f70086d137512)
|
||||
---
|
||||
src/firewall/core/io/policy.py | 14 ++++++++------
|
||||
1 file changed, 8 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/io/policy.py b/src/firewall/core/io/policy.py
|
||||
index 514a20251ef4..66535e0d0368 100644
|
||||
--- a/src/firewall/core/io/policy.py
|
||||
+++ b/src/firewall/core/io/policy.py
|
||||
@@ -372,6 +372,11 @@ def common_check_config(obj, config, item, all_config):
|
||||
),
|
||||
)
|
||||
|
||||
+
|
||||
+def _handler_add_rich_limit(handler, limit):
|
||||
+ handler.simpleElement("limit", {"value": limit.value})
|
||||
+
|
||||
+
|
||||
def common_writer(obj, handler):
|
||||
# short
|
||||
if obj.short and obj.short != "":
|
||||
@@ -533,8 +538,7 @@ def common_writer(obj, handler):
|
||||
handler.ignorableWhitespace(" ")
|
||||
handler.startElement("log", attrs)
|
||||
handler.ignorableWhitespace("\n ")
|
||||
- handler.simpleElement("limit",
|
||||
- { "value": rule.log.limit.value })
|
||||
+ _handler_add_rich_limit(handler, rule.log.limit)
|
||||
handler.ignorableWhitespace("\n ")
|
||||
handler.endElement("log")
|
||||
else:
|
||||
@@ -549,8 +553,7 @@ def common_writer(obj, handler):
|
||||
handler.ignorableWhitespace(" ")
|
||||
handler.startElement("audit", { })
|
||||
handler.ignorableWhitespace("\n ")
|
||||
- handler.simpleElement("limit",
|
||||
- { "value": rule.audit.limit.value })
|
||||
+ _handler_add_rich_limit(handler, rule.audit.limit)
|
||||
handler.ignorableWhitespace("\n ")
|
||||
handler.endElement("audit")
|
||||
else:
|
||||
@@ -579,8 +582,7 @@ def common_writer(obj, handler):
|
||||
handler.ignorableWhitespace(" ")
|
||||
handler.startElement(action, attrs)
|
||||
handler.ignorableWhitespace("\n ")
|
||||
- handler.simpleElement("limit",
|
||||
- { "value": rule.action.limit.value })
|
||||
+ _handler_add_rich_limit(handler, rule.action.limit)
|
||||
handler.ignorableWhitespace("\n ")
|
||||
handler.endElement(action)
|
||||
else:
|
||||
--
|
||||
2.43.0
|
||||
|
@ -1,189 +0,0 @@
|
||||
From 2844fedea7b0c65d864f9960b513150c4468adb2 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Wed, 13 Dec 2023 19:42:37 +0100
|
||||
Subject: [PATCH 25/26] v2.2.0: improvement(rich): add Rich_Limit.value_parse()
|
||||
and normalize value
|
||||
|
||||
Instead of duplicating the parsing, add a Rich_Limit.value_parse()
|
||||
function that can be used to "understand" the value string.
|
||||
|
||||
Note that already previously, Rich_Limit.__init__() would normalize the
|
||||
value (e.g. modify "/minute" to "/m"). Go one step further with this.
|
||||
Now parse and stringify the value, so that it is normalized. Invalid
|
||||
values are left unnormalized, and Rich_Limit.__init__() still does not
|
||||
fail the operation (like before). For that we have check().
|
||||
|
||||
This normalization matters. For example, the parser is (rightfully)
|
||||
graceful and will accept 'limit value="1 /m"'. If we add two rules
|
||||
that are identical, except for the white space, we want that the
|
||||
normalize string is identical. That's useful, because the normalized
|
||||
string of a rule is used as identity for the rule.
|
||||
|
||||
(cherry picked from commit 8d2f9502db98b349cabf76bb9c0a303fe6e3512a)
|
||||
---
|
||||
src/firewall-config.in | 6 +--
|
||||
src/firewall/core/nftables.py | 9 ++---
|
||||
src/firewall/core/rich.py | 76 ++++++++++++++++++++++-------------
|
||||
3 files changed, 53 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/src/firewall-config.in b/src/firewall-config.in
|
||||
index f91e945ca7de..e4fbb029ac6d 100755
|
||||
--- a/src/firewall-config.in
|
||||
+++ b/src/firewall-config.in
|
||||
@@ -3245,7 +3245,7 @@ class FirewallConfig(object):
|
||||
|
||||
if old_obj.action.limit:
|
||||
self.richRuleDialogActionLimitCheck.set_active(True)
|
||||
- (rate, duration) = old_obj.action.limit.value.split("/")
|
||||
+ (rate, duration) = old_obj.action.limit.value_parse()
|
||||
self.richRuleDialogActionLimitRateEntry.set_text(rate)
|
||||
combobox_select_text( \
|
||||
self.richRuleDialogActionLimitDurationCombobox,
|
||||
@@ -3288,7 +3288,7 @@ class FirewallConfig(object):
|
||||
loglevel[log_level])
|
||||
if old_obj.log.limit:
|
||||
self.richRuleDialogLogLimitCheck.set_active(True)
|
||||
- (rate, duration) = old_obj.log.limit.value.split("/")
|
||||
+ (rate, duration) = old_obj.log.limit.value_parse()
|
||||
self.richRuleDialogLogLimitRateEntry.set_text(rate)
|
||||
combobox_select_text( \
|
||||
self.richRuleDialogLogLimitDurationCombobox,
|
||||
@@ -3299,7 +3299,7 @@ class FirewallConfig(object):
|
||||
self.richRuleDialogAuditCheck.set_active(True)
|
||||
if old_obj.audit.limit:
|
||||
self.richRuleDialogAuditLimitCheck.set_active(True)
|
||||
- (rate, duration) = old_obj.audit.limit.value.split("/")
|
||||
+ (rate, duration) = old_obj.audit.limit.value_parse()
|
||||
self.richRuleDialogAuditLimitRateEntry.set_text(rate)
|
||||
combobox_select_text( \
|
||||
self.richRuleDialogAuditLimitDurationCombobox,
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index 67fb6457e86c..f24095ce729c 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1071,13 +1071,10 @@ class nftables(object):
|
||||
"d" : "day",
|
||||
}
|
||||
|
||||
- try:
|
||||
- i = limit.value.index("/")
|
||||
- except ValueError:
|
||||
- raise FirewallError(INVALID_RULE, "Expected '/' in limit")
|
||||
+ rate, duration = limit.value_parse()
|
||||
|
||||
- return {"limit": {"rate": int(limit.value[0:i]),
|
||||
- "per": rich_to_nft[limit.value[i+1]]}}
|
||||
+ return {"limit": {"rate": rate,
|
||||
+ "per": rich_to_nft[duration]}}
|
||||
|
||||
def _rich_rule_chain_suffix(self, rich_rule):
|
||||
if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock]:
|
||||
diff --git a/src/firewall/core/rich.py b/src/firewall/core/rich.py
|
||||
index b150a0dca402..a77f2b4aa495 100644
|
||||
--- a/src/firewall/core/rich.py
|
||||
+++ b/src/firewall/core/rich.py
|
||||
@@ -230,54 +230,72 @@ class Rich_Mark(object):
|
||||
# value is uint32
|
||||
raise FirewallError(errors.INVALID_MARK, x)
|
||||
|
||||
+DURATION_TO_MULT = {
|
||||
+ "s": 1,
|
||||
+ "m": 60,
|
||||
+ "h": 60 * 60,
|
||||
+ "d": 24 * 60 * 60,
|
||||
+}
|
||||
+
|
||||
class Rich_Limit(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
- if "/" in self.value:
|
||||
- splits = self.value.split("/")
|
||||
- if len(splits) == 2 and \
|
||||
- splits[1] in [ "second", "minute", "hour", "day" ]:
|
||||
- self.value = "%s/%s" % (splits[0], splits[1][:1])
|
||||
|
||||
def check(self):
|
||||
+ self.value_parse()
|
||||
+
|
||||
+ @property
|
||||
+ def value(self):
|
||||
+ return self._value
|
||||
+
|
||||
+ @value.setter
|
||||
+ def value(self, value):
|
||||
+ if value is None:
|
||||
+ self._value = None
|
||||
+ return
|
||||
+ try:
|
||||
+ rate, duration = self._value_parse(value)
|
||||
+ except FirewallError:
|
||||
+ # The value is invalid. We cannot normalize it.
|
||||
+ v = value
|
||||
+ else:
|
||||
+ v = f"{rate}/{duration}"
|
||||
+ if getattr(self, "_value", None) != v:
|
||||
+ self._value = v
|
||||
+
|
||||
+ @staticmethod
|
||||
+ def _value_parse(value):
|
||||
splits = None
|
||||
- if "/" in self.value:
|
||||
- splits = self.value.split("/")
|
||||
+ if "/" in value:
|
||||
+ splits = value.split("/")
|
||||
if not splits or len(splits) != 2:
|
||||
- raise FirewallError(errors.INVALID_LIMIT, self.value)
|
||||
+ raise FirewallError(errors.INVALID_LIMIT, value)
|
||||
(rate, duration) = splits
|
||||
try:
|
||||
rate = int(rate)
|
||||
except:
|
||||
- raise FirewallError(errors.INVALID_LIMIT, self.value)
|
||||
+ raise FirewallError(errors.INVALID_LIMIT, value)
|
||||
|
||||
- if rate < 1 or duration not in [ "s", "m", "h", "d" ]:
|
||||
- raise FirewallError(errors.INVALID_LIMIT, self.value)
|
||||
+ if duration in ["second", "minute", "hour", "day"]:
|
||||
+ duration = duration[:1]
|
||||
|
||||
- mult = 1
|
||||
- if duration == "s":
|
||||
- mult = 1
|
||||
- elif duration == "m":
|
||||
- mult = 60
|
||||
- elif duration == "h":
|
||||
- mult = 60*60
|
||||
- elif duration == "d":
|
||||
- mult = 24*60*60
|
||||
+ if rate < 1 or duration not in ["s", "m", "h", "d"]:
|
||||
+ raise FirewallError(errors.INVALID_LIMIT, value)
|
||||
|
||||
- if 10000 * mult // rate == 0:
|
||||
- raise FirewallError(errors.INVALID_LIMIT,
|
||||
- "%s too fast" % self.value)
|
||||
+ if 10000 * DURATION_TO_MULT[duration] // rate == 0:
|
||||
+ raise FirewallError(errors.INVALID_LIMIT, "%s too fast" % (value,))
|
||||
|
||||
if rate == 1 and duration == "d":
|
||||
# iptables (v1.4.21) doesn't accept 1/d
|
||||
- raise FirewallError(errors.INVALID_LIMIT,
|
||||
- "%s too slow" % self.value)
|
||||
+ raise FirewallError(errors.INVALID_LIMIT, "%s too slow" % (value,))
|
||||
|
||||
- def __str__(self):
|
||||
- return 'limit value="%s"' % (self.value)
|
||||
+ return rate, duration
|
||||
|
||||
- def command(self):
|
||||
- return ''
|
||||
+ def value_parse(self):
|
||||
+ return self._value_parse(self._value)
|
||||
+
|
||||
+ def __str__(self):
|
||||
+ return f'limit value="{self._value}"'
|
||||
|
||||
class Rich_Rule(object):
|
||||
priority_min = -32768
|
||||
--
|
||||
2.43.0
|
||||
|
@ -1,238 +0,0 @@
|
||||
From 45ebffc5521db62064f365f4a9100b4ab40dce90 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Haller <thaller@redhat.com>
|
||||
Date: Wed, 13 Dec 2023 20:35:51 +0100
|
||||
Subject: [PATCH 26/26] v2.2.0: improvement(rich): support "burst" attribute to
|
||||
limit in rich rules
|
||||
|
||||
For iptables, this is `-m limit --limit rate/suffix --limit-burst burst`.
|
||||
|
||||
For nftables, this is `limit rate [over] packet_number / TIME_UNIT [burst packet_number packets]`
|
||||
|
||||
Not implemented in `src/firewall-config.in`.
|
||||
|
||||
https://issues.redhat.com/browse/RHEL-9316
|
||||
(cherry picked from commit 58dfdcafabaaad639bfcf389ebbd6b2c242965a4)
|
||||
---
|
||||
src/firewall/core/io/policy.py | 9 +++--
|
||||
src/firewall/core/io/zone.py | 1 +
|
||||
src/firewall/core/ipXtables.py | 9 +++--
|
||||
src/firewall/core/nftables.py | 12 +++++--
|
||||
src/firewall/core/rich.py | 63 ++++++++++++++++++++++++++++++----
|
||||
src/tests/cli/firewall-cmd.at | 4 +--
|
||||
6 files changed, 82 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/io/policy.py b/src/firewall/core/io/policy.py
|
||||
index 66535e0d0368..c732324c441b 100644
|
||||
--- a/src/firewall/core/io/policy.py
|
||||
+++ b/src/firewall/core/io/policy.py
|
||||
@@ -278,7 +278,7 @@ def common_startElement(obj, name, attrs):
|
||||
obj._rule_error = True
|
||||
return True
|
||||
value = attrs["value"]
|
||||
- obj._limit_ok.limit = rich.Rich_Limit(value)
|
||||
+ obj._limit_ok.limit = rich.Rich_Limit(value, attrs.get("burst"))
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -374,7 +374,11 @@ def common_check_config(obj, config, item, all_config):
|
||||
|
||||
|
||||
def _handler_add_rich_limit(handler, limit):
|
||||
- handler.simpleElement("limit", {"value": limit.value})
|
||||
+ d = {"value": limit.value}
|
||||
+ burst = limit.burst
|
||||
+ if burst is not None:
|
||||
+ d["burst"] = burst
|
||||
+ handler.simpleElement("limit", d)
|
||||
|
||||
|
||||
def common_writer(obj, handler):
|
||||
@@ -652,6 +656,7 @@ class Policy(IO_Object):
|
||||
"destination": [ "address", "invert", "ipset" ],
|
||||
"log": [ "prefix", "level" ],
|
||||
"reject": [ "type" ],
|
||||
+ "limit": ["burst"],
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
diff --git a/src/firewall/core/io/zone.py b/src/firewall/core/io/zone.py
|
||||
index 0c419ee0f2bd..753036e4fb55 100644
|
||||
--- a/src/firewall/core/io/zone.py
|
||||
+++ b/src/firewall/core/io/zone.py
|
||||
@@ -94,6 +94,7 @@ class Zone(IO_Object):
|
||||
"destination": [ "address", "invert", "ipset" ],
|
||||
"log": [ "prefix", "level" ],
|
||||
"reject": [ "type" ],
|
||||
+ "limit": ["burst"],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
|
||||
index 401377104ce1..0f9a1518380e 100644
|
||||
--- a/src/firewall/core/ipXtables.py
|
||||
+++ b/src/firewall/core/ipXtables.py
|
||||
@@ -967,9 +967,12 @@ class ip4tables(object):
|
||||
return rules
|
||||
|
||||
def _rule_limit(self, limit):
|
||||
- if limit:
|
||||
- return [ "-m", "limit", "--limit", limit.value ]
|
||||
- return []
|
||||
+ if not limit:
|
||||
+ return []
|
||||
+ s = ["-m", "limit", "--limit", limit.value]
|
||||
+ if limit.burst is not None:
|
||||
+ s += ["--limit-burst", limit.burst]
|
||||
+ return s
|
||||
|
||||
def _rich_rule_chain_suffix(self, rich_rule):
|
||||
if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock]:
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index f24095ce729c..834176c09cbc 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1073,8 +1073,16 @@ class nftables(object):
|
||||
|
||||
rate, duration = limit.value_parse()
|
||||
|
||||
- return {"limit": {"rate": rate,
|
||||
- "per": rich_to_nft[duration]}}
|
||||
+ d = {
|
||||
+ "rate": rate,
|
||||
+ "per": rich_to_nft[duration],
|
||||
+ }
|
||||
+
|
||||
+ burst = limit.burst_parse()
|
||||
+ if burst is not None:
|
||||
+ d["burst"] = burst
|
||||
+
|
||||
+ return {"limit": d}
|
||||
|
||||
def _rich_rule_chain_suffix(self, rich_rule):
|
||||
if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock]:
|
||||
diff --git a/src/firewall/core/rich.py b/src/firewall/core/rich.py
|
||||
index a77f2b4aa495..c561709af0e2 100644
|
||||
--- a/src/firewall/core/rich.py
|
||||
+++ b/src/firewall/core/rich.py
|
||||
@@ -238,11 +238,13 @@ DURATION_TO_MULT = {
|
||||
}
|
||||
|
||||
class Rich_Limit(object):
|
||||
- def __init__(self, value):
|
||||
+ def __init__(self, value, burst=None):
|
||||
self.value = value
|
||||
+ self.burst = burst
|
||||
|
||||
def check(self):
|
||||
self.value_parse()
|
||||
+ self.burst_parse()
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
@@ -263,6 +265,24 @@ class Rich_Limit(object):
|
||||
if getattr(self, "_value", None) != v:
|
||||
self._value = v
|
||||
|
||||
+ @property
|
||||
+ def burst(self):
|
||||
+ return self._burst
|
||||
+
|
||||
+ @burst.setter
|
||||
+ def burst(self, burst):
|
||||
+ if burst is None:
|
||||
+ self._burst = None
|
||||
+ return
|
||||
+ try:
|
||||
+ b = self._burst_parse(burst)
|
||||
+ except FirewallError:
|
||||
+ b = burst
|
||||
+ else:
|
||||
+ b = str(burst)
|
||||
+ if getattr(self, "_burst", None) != b:
|
||||
+ self._burst = b
|
||||
+
|
||||
@staticmethod
|
||||
def _value_parse(value):
|
||||
splits = None
|
||||
@@ -294,8 +314,28 @@ class Rich_Limit(object):
|
||||
def value_parse(self):
|
||||
return self._value_parse(self._value)
|
||||
|
||||
+ @staticmethod
|
||||
+ def _burst_parse(burst):
|
||||
+ if burst is None:
|
||||
+ return None
|
||||
+ try:
|
||||
+ b = int(burst)
|
||||
+ except:
|
||||
+ raise FirewallError(errors.INVALID_LIMIT, burst)
|
||||
+
|
||||
+ if b < 1 or b > 10_000_000:
|
||||
+ raise FirewallError(errors.INVALID_LIMIT, burst)
|
||||
+
|
||||
+ return b
|
||||
+
|
||||
+ def burst_parse(self):
|
||||
+ return self._burst_parse(self._burst)
|
||||
+
|
||||
def __str__(self):
|
||||
- return f'limit value="{self._value}"'
|
||||
+ s = f'limit value="{self._value}"'
|
||||
+ if self._burst is not None:
|
||||
+ s += f" burst={self._burst}"
|
||||
+ return s
|
||||
|
||||
class Rich_Rule(object):
|
||||
priority_min = -32768
|
||||
@@ -368,7 +408,7 @@ class Rich_Rule(object):
|
||||
'invert', 'value',
|
||||
'port', 'protocol', 'to-port', 'to-addr',
|
||||
'name', 'prefix', 'level', 'type',
|
||||
- 'set']:
|
||||
+ 'set', 'burst']:
|
||||
raise FirewallError(errors.INVALID_RULE, "bad attribute '%s'" % attr_name)
|
||||
else: # element
|
||||
if element in ['rule', 'source', 'destination', 'protocol',
|
||||
@@ -554,11 +594,20 @@ class Rich_Rule(object):
|
||||
attrs.clear()
|
||||
index = index -1 # return token to input
|
||||
elif in_element == 'limit':
|
||||
- if attr_name == 'value':
|
||||
- attrs['limit'] = Rich_Limit(attr_value)
|
||||
- in_elements.pop() # limit
|
||||
+ if attr_name in ["value", "burst"]:
|
||||
+ attrs[f"limit.{attr_name}"] = attr_value
|
||||
else:
|
||||
- raise FirewallError(errors.INVALID_RULE, "invalid 'limit' element")
|
||||
+ if "limit.value" not in attrs:
|
||||
+ raise FirewallError(
|
||||
+ errors.INVALID_RULE, "invalid 'limit' element"
|
||||
+ )
|
||||
+ attrs["limit"] = Rich_Limit(
|
||||
+ attrs["limit.value"], attrs.get("limit.burst")
|
||||
+ )
|
||||
+ attrs.pop("limit.value", None)
|
||||
+ attrs.pop("limit.burst", None)
|
||||
+ in_elements.pop() # limit
|
||||
+ index = index - 1 # return token to input
|
||||
|
||||
index = index + 1
|
||||
|
||||
diff --git a/src/tests/cli/firewall-cmd.at b/src/tests/cli/firewall-cmd.at
|
||||
index c4ab3108d37c..6c69f0ccebd4 100644
|
||||
--- a/src/tests/cli/firewall-cmd.at
|
||||
+++ b/src/tests/cli/firewall-cmd.at
|
||||
@@ -1356,8 +1356,8 @@ FWD_START_TEST([rich rules good])
|
||||
rich_rule_test([rule protocol value="ah" reject])
|
||||
rich_rule_test([rule protocol value="esp" accept])
|
||||
rich_rule_test([rule protocol value="sctp" log])
|
||||
- rich_rule_test([rule family="ipv4" source address="192.168.0.0/24" service name="tftp" log prefix="tftp: " level="info" limit value="1/m" accept])
|
||||
- rich_rule_test([rule family="ipv4" source not address="192.168.0.0/24" service name="dns" log prefix="dns: " level="info" limit value="2/m" drop])
|
||||
+ rich_rule_test([rule family="ipv4" source address="192.168.0.0/24" service name="tftp" log prefix="tftp: " level="info" limit value="1/m" burst=5 accept])
|
||||
+ rich_rule_test([rule family="ipv4" source not address="192.168.0.0/24" service name="dns" log prefix="dns: " level="info" limit value="2/m" burst=5 drop])
|
||||
IF_HOST_SUPPORTS_IPV6_RULES([
|
||||
rich_rule_test([rule family="ipv6" source address="1:2:3:4:6::" service name="radius" log prefix="dns -- " level="info" limit value="3/m" reject type="icmp6-addr-unreachable" limit value="20/m"])
|
||||
rich_rule_test([rule family="ipv6" source address="1:2:3:4:6::" port port="4011" protocol="tcp" log prefix="port 4011: " level="info" limit value="4/m" drop])
|
||||
--
|
||||
2.43.0
|
||||
|
@ -1,34 +0,0 @@
|
||||
From f61b27ffc91da3d5e634a2d90edd164ac4102086 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <egarver@redhat.com>
|
||||
Date: Wed, 26 Jun 2024 11:13:00 -0400
|
||||
Subject: [PATCH 28/30] v2.0.0: chore(direct): add has_runtime_configuration()
|
||||
|
||||
This is originally from cdd015475e83 ("fix(ipset): defer native ipset
|
||||
creation if nftables").
|
||||
---
|
||||
src/firewall/core/fw_direct.py | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/firewall/core/fw_direct.py b/src/firewall/core/fw_direct.py
|
||||
index 76aeda9f19cb..a35ebce1f276 100644
|
||||
--- a/src/firewall/core/fw_direct.py
|
||||
+++ b/src/firewall/core/fw_direct.py
|
||||
@@ -64,9 +64,14 @@ class FirewallDirect(object):
|
||||
def set_permanent_config(self, obj):
|
||||
self._obj = obj
|
||||
|
||||
- def has_configuration(self):
|
||||
+ def has_runtime_configuration(self):
|
||||
if len(self._chains) + len(self._rules) + len(self._passthroughs) > 0:
|
||||
return True
|
||||
+ return False
|
||||
+
|
||||
+ def has_configuration(self):
|
||||
+ if self.has_runtime_configuration():
|
||||
+ return True
|
||||
if len(self._obj.get_all_chains()) + \
|
||||
len(self._obj.get_all_rules()) + \
|
||||
len(self._obj.get_all_passthroughs()) > 0:
|
||||
--
|
||||
2.43.0
|
||||
|
1624
SPECS/firewalld.spec
1624
SPECS/firewalld.spec
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user