import UBI firewalld-1.3.4-7.el9
This commit is contained in:
parent
c56d981db8
commit
536a59d2c6
@ -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
|
||||
|
@ -0,0 +1,131 @@
|
||||
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 06/22] v2.1.0: feat(icmp): add ICMPv6 Multicast Listener
|
||||
Discovery (MLD) types
|
||||
|
||||
Note that ip6tables does not support these ICMPv6 types. Currently,
|
||||
the name of the ICMP types in firewalld must correspond to the names
|
||||
in iptables. As ip6tables doesn't support it, it does not. If ip6tables
|
||||
adds support for "mld-listener-query", but calls it differently, we have
|
||||
a problem. Nothing that can be done about that.
|
||||
|
||||
`man nft` also lists an alias "mld-listener-reduction" (for
|
||||
"mld-listener-done", type 132). That alias is not supported. Use the
|
||||
name as from RFC 4890.
|
||||
|
||||
(cherry picked from commit dd88bbf812e0a50766b69c2bf12470ecf9d2466a)
|
||||
---
|
||||
config/Makefile.am | 4 ++++
|
||||
config/icmptypes/mld-listener-done.xml | 7 +++++++
|
||||
config/icmptypes/mld-listener-query.xml | 7 +++++++
|
||||
config/icmptypes/mld-listener-report.xml | 7 +++++++
|
||||
config/icmptypes/mld2-listener-report.xml | 7 +++++++
|
||||
po/POTFILES.in | 4 ++++
|
||||
src/firewall/core/nftables.py | 4 ++++
|
||||
7 files changed, 40 insertions(+)
|
||||
create mode 100644 config/icmptypes/mld-listener-done.xml
|
||||
create mode 100644 config/icmptypes/mld-listener-query.xml
|
||||
create mode 100644 config/icmptypes/mld-listener-report.xml
|
||||
create mode 100644 config/icmptypes/mld2-listener-report.xml
|
||||
|
||||
diff --git a/config/Makefile.am b/config/Makefile.am
|
||||
index 47f30c1566e0..edae25fd9de0 100644
|
||||
--- a/config/Makefile.am
|
||||
+++ b/config/Makefile.am
|
||||
@@ -83,6 +83,10 @@ CONFIG_FILES = \
|
||||
icmptypes/host-unknown.xml \
|
||||
icmptypes/host-unreachable.xml \
|
||||
icmptypes/ip-header-bad.xml \
|
||||
+ icmptypes/mld-listener-done.xml \
|
||||
+ icmptypes/mld-listener-query.xml \
|
||||
+ icmptypes/mld-listener-report.xml \
|
||||
+ icmptypes/mld2-listener-report.xml \
|
||||
icmptypes/neighbour-advertisement.xml \
|
||||
icmptypes/neighbour-solicitation.xml \
|
||||
icmptypes/network-prohibited.xml \
|
||||
diff --git a/config/icmptypes/mld-listener-done.xml b/config/icmptypes/mld-listener-done.xml
|
||||
new file mode 100644
|
||||
index 000000000000..09b8bbba5b90
|
||||
--- /dev/null
|
||||
+++ b/config/icmptypes/mld-listener-done.xml
|
||||
@@ -0,0 +1,7 @@
|
||||
+<?xml version="1.0" encoding="utf-8"?>
|
||||
+<icmptype>
|
||||
+ <short>MLD Listener Done</short>
|
||||
+ <description>ICMPv6 Link-Local Multicast Listener Discovery (MDL) of type Multicast Listener Done (type 132) (RFC 4890 section 4.4.1). Also known as mld-listener-reduction to nft.</description>
|
||||
+ <destination ipv4="no"/>
|
||||
+ <destination ipv6="yes"/>
|
||||
+</icmptype>
|
||||
diff --git a/config/icmptypes/mld-listener-query.xml b/config/icmptypes/mld-listener-query.xml
|
||||
new file mode 100644
|
||||
index 000000000000..418685578d1d
|
||||
--- /dev/null
|
||||
+++ b/config/icmptypes/mld-listener-query.xml
|
||||
@@ -0,0 +1,7 @@
|
||||
+<?xml version="1.0" encoding="utf-8"?>
|
||||
+<icmptype>
|
||||
+ <short>MLD Listener Query</short>
|
||||
+ <description>ICMPv6 Link-Local Multicast Listener Discovery (MDL) of type Multicast Listener Query (type 130) (RFC 4890 section 4.4.1).</description>
|
||||
+ <destination ipv4="no"/>
|
||||
+ <destination ipv6="yes"/>
|
||||
+</icmptype>
|
||||
diff --git a/config/icmptypes/mld-listener-report.xml b/config/icmptypes/mld-listener-report.xml
|
||||
new file mode 100644
|
||||
index 000000000000..98fb4161b298
|
||||
--- /dev/null
|
||||
+++ b/config/icmptypes/mld-listener-report.xml
|
||||
@@ -0,0 +1,7 @@
|
||||
+<?xml version="1.0" encoding="utf-8"?>
|
||||
+<icmptype>
|
||||
+ <short>MLD Listener Report</short>
|
||||
+ <description>ICMPv6 Link-Local Multicast Listener Discovery (MDL) of type Multicast Listener Report (type 131) (RFC 4890 section 4.4.1).</description>
|
||||
+ <destination ipv4="no"/>
|
||||
+ <destination ipv6="yes"/>
|
||||
+</icmptype>
|
||||
diff --git a/config/icmptypes/mld2-listener-report.xml b/config/icmptypes/mld2-listener-report.xml
|
||||
new file mode 100644
|
||||
index 000000000000..faee68c95b20
|
||||
--- /dev/null
|
||||
+++ b/config/icmptypes/mld2-listener-report.xml
|
||||
@@ -0,0 +1,7 @@
|
||||
+<?xml version="1.0" encoding="utf-8"?>
|
||||
+<icmptype>
|
||||
+ <short>MLDv2 Multicast Listener Report</short>
|
||||
+ <description>ICMPv6 Link-Local Multicast Listener Discovery (MDLv2) of type Multicast Listener Report (type 143) (RFC 4890 section 4.4.1).</description>
|
||||
+ <destination ipv4="no"/>
|
||||
+ <destination ipv6="yes"/>
|
||||
+</icmptype>
|
||||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||||
index 1c990542ac4d..adeebdee3f55 100644
|
||||
--- a/po/POTFILES.in
|
||||
+++ b/po/POTFILES.in
|
||||
@@ -15,6 +15,10 @@ config/icmptypes/host-redirect.xml
|
||||
config/icmptypes/host-unknown.xml
|
||||
config/icmptypes/host-unreachable.xml
|
||||
config/icmptypes/ip-header-bad.xml
|
||||
+config/icmptypes/mld-listener-done.xml
|
||||
+config/icmptypes/mld-listener-query.xml
|
||||
+config/icmptypes/mld-listener-report.xml
|
||||
+config/icmptypes/mld2-listener-report.xml
|
||||
config/icmptypes/neighbour-advertisement.xml
|
||||
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 6ad4b9168403..3df3fa3c3742 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -140,6 +140,10 @@ ICMP_TYPES_FRAGMENTS = {
|
||||
"echo-reply": _icmp_types_fragments("icmpv6", "echo-reply"),
|
||||
"echo-request": _icmp_types_fragments("icmpv6", "echo-request"),
|
||||
"failed-policy": _icmp_types_fragments("icmpv6", "destination-unreachable", 5),
|
||||
+ "mld-listener-done": _icmp_types_fragments("icmpv6", "mld-listener-done"),
|
||||
+ "mld-listener-query": _icmp_types_fragments("icmpv6", "mld-listener-query"),
|
||||
+ "mld-listener-report": _icmp_types_fragments("icmpv6", "mld-listener-report"),
|
||||
+ "mld2-listener-report": _icmp_types_fragments("icmpv6", "mld2-listener-report"),
|
||||
"neighbour-advertisement": _icmp_types_fragments("icmpv6", "nd-neighbor-advert"),
|
||||
"neighbour-solicitation": _icmp_types_fragments("icmpv6", "nd-neighbor-solicit"),
|
||||
"no-route": _icmp_types_fragments("icmpv6", "destination-unreachable", 0),
|
||||
--
|
||||
2.43.5
|
||||
|
@ -0,0 +1,71 @@
|
||||
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 07/22] v2.1.0: fix(rich): validate service name of rich rule
|
||||
|
||||
Previously, validation of valid service names was not done.
|
||||
That meant:
|
||||
|
||||
$ firewall-cmd --add-rich-rule='rule priority="-100" family="ipv4" source address="10.0.0.10" service name="listen" accept' --permanent
|
||||
success
|
||||
$ firewall-cmd --reload
|
||||
Error: INVALID_SERVICE: listen
|
||||
|
||||
which left firewalld in a bad state.
|
||||
|
||||
Now:
|
||||
|
||||
$ firewall-cmd --add-rich-rule='rule priority="-100" family="ipv4" source address="10.0.0.10" service name="listen" accept' --permanent
|
||||
Error: INVALID_SERVICE: Zone 'public': 'listen' not among existing services
|
||||
|
||||
https://issues.redhat.com/browse/RHEL-5790
|
||||
(cherry picked from commit fbcdddd3e38c31a7b8325bf02764b84344c216b0)
|
||||
---
|
||||
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 7d383abb0a2d..f9a1114d7969 100644
|
||||
--- a/src/firewall/core/io/policy.py
|
||||
+++ b/src/firewall/core/io/policy.py
|
||||
@@ -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(
|
||||
+ obj_type, obj.name, obj_rich.element.name
|
||||
+ ),
|
||||
+ )
|
||||
|
||||
def common_writer(obj, handler):
|
||||
# short
|
||||
diff --git a/src/tests/features/rich_rules.at b/src/tests/features/rich_rules.at
|
||||
index aadc76da57b4..f7d1a1d0abf4 100644
|
||||
--- a/src/tests/features/rich_rules.at
|
||||
+++ b/src/tests/features/rich_rules.at
|
||||
@@ -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_RELOAD
|
||||
NFT_LIST_RULES([inet], [filter_IN_policy_foobar_pre], 0, [dnl
|
||||
table inet firewalld {
|
||||
@@ -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.5
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,27 @@
|
||||
From 55e40954a8c596fabe03371e9a508d3518273ac1 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 14 May 2024 16:27:54 -0400
|
||||
Subject: [PATCH 11/22] v2.2.0: test(functions): add macro CHECK_NFTABLES_FIB
|
||||
|
||||
(cherry picked from commit 0aeebef07bc57b1f56b107632cdfdd809384398c)
|
||||
---
|
||||
src/tests/functions.at | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index f454ca980046..65a4ce078e05 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -748,3 +748,9 @@ m4_define([CHECK_NM_CAPABILITY_OVS], [
|
||||
m4_define([IF_BACKEND_IS_DEFAULT], [
|
||||
m4_if(nftables, FIREWALL_BACKEND, [$1], [])
|
||||
])
|
||||
+
|
||||
+m4_define([CHECK_NFTABLES_FIB], [
|
||||
+ m4_if(nftables, FIREWALL_BACKEND, [
|
||||
+ IF_HOST_SUPPORTS_NFT_FIB([], [AT_SKIP_IF([:])])
|
||||
+ ])
|
||||
+])
|
||||
--
|
||||
2.43.5
|
||||
|
@ -0,0 +1,31 @@
|
||||
From d368d579c78652a68273897d5f8b5099d251a9b5 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Garver <eric@garver.life>
|
||||
Date: Tue, 14 May 2024 16:21:06 -0400
|
||||
Subject: [PATCH 12/22] v2.2.0: test(functions): add macro
|
||||
CHECK_NFTABLES_FIB_IN_FORWARD
|
||||
|
||||
(cherry picked from commit b9cf7b75c7d94efa98545a3b7ad5020b1896b22a)
|
||||
---
|
||||
src/tests/functions.at | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
diff --git a/src/tests/functions.at b/src/tests/functions.at
|
||||
index 65a4ce078e05..b2372dd4075b 100644
|
||||
--- a/src/tests/functions.at
|
||||
+++ b/src/tests/functions.at
|
||||
@@ -754,3 +754,12 @@ m4_define([CHECK_NFTABLES_FIB], [
|
||||
IF_HOST_SUPPORTS_NFT_FIB([], [AT_SKIP_IF([:])])
|
||||
])
|
||||
])
|
||||
+
|
||||
+m4_define([CHECK_NFTABLES_FIB_IN_FORWARD], [
|
||||
+ m4_if(nftables, FIREWALL_BACKEND, [
|
||||
+ NS_CHECK([nft add table inet firewalld_check])
|
||||
+ NS_CHECK([nft add chain inet firewalld_check foobar { type filter hook forward priority 0 \; }])
|
||||
+ AT_SKIP_IF([! NS_CMD([nft add rule inet firewalld_check foobar meta nfproto ipv6 fib saddr . mark . iif oif missing drop >/dev/null 2>&1])])
|
||||
+ NS_CHECK([nft delete table inet firewalld_check])
|
||||
+ ])
|
||||
+])
|
||||
--
|
||||
2.43.5
|
||||
|
51
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
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,7 +1,7 @@
|
||||
Summary: A firewall daemon with D-Bus interface providing a dynamic firewall
|
||||
Name: firewalld
|
||||
Version: 1.3.4
|
||||
Release: 1%{?dist}
|
||||
Release: 7%{?dist}
|
||||
URL: http://www.firewalld.org
|
||||
License: GPLv2+
|
||||
Source0: https://github.com/firewalld/firewalld/releases/download/v%{version}/firewalld-%{version}.tar.bz2
|
||||
@ -9,6 +9,24 @@ Patch1: 0001-RHEL-only-Add-cockpit-by-default-to-some-zones.patch
|
||||
Patch2: 0002-v1.4.0-test-atlocal-pass-EBTABLES-to-testsuite.patch
|
||||
Patch3: 0003-v1.4.0-feat-direct-avoid-iptables-flush-if-using-nft.patch
|
||||
Patch4: 0004-v1.4.0-test-direct-avoid-iptables-flush-if-using-nft.patch
|
||||
Patch5: 0005-v2.0.0-feat-service-add-OpenTelemetry-OTLP-service.patch
|
||||
Patch6: 0006-v2.1.0-feat-icmp-add-ICMPv6-Multicast-Listener-Disco.patch
|
||||
Patch7: 0007-v2.1.0-fix-rich-validate-service-name-of-rich-rule.patch
|
||||
Patch8: 0008-v2.1.0-improvement-nftables-do-not-track-rule-handle.patch
|
||||
Patch9: 0009-v2.1.0-improvement-fw-make-set_policy-DROP-more-flex.patch
|
||||
Patch10: 0010-v2.1.0-feat-fw-add-ReloadPolicy-option-in-firewalld..patch
|
||||
Patch11: 0011-v2.2.0-test-functions-add-macro-CHECK_NFTABLES_FIB.patch
|
||||
Patch12: 0012-v2.2.0-test-functions-add-macro-CHECK_NFTABLES_FIB_I.patch
|
||||
Patch13: 0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
|
||||
Patch14: 0014-v2.2.0-test-IPv6_rpfilter-verify-valid-values.patch
|
||||
Patch15: 0015-v2.2.0-chore-IPv6_rpfilter-prepare-for-new-config-va.patch
|
||||
Patch16: 0016-v2.2.0-feat-IPv6_rpfilter-support-loose-rpfilter.patch
|
||||
Patch17: 0017-v2.2.0-feat-IPv6_rpfilter-support-loose-forward-rpfi.patch
|
||||
Patch18: 0018-v2.2.0-feat-IPv6_rpfilter-support-strict-forward-rpf.patch
|
||||
Patch19: 0019-v2.2.0-test-functions-start-firewalld-with-file-logg.patch
|
||||
Patch20: 0020-v2.2.0-feat-nftables-table-ownership.patch
|
||||
Patch21: 0021-v2.2.0-test-nftables-table-ownership.patch
|
||||
Patch22: 0022-v2.2.0-chore-service-remove-Conflicts-with-nftables.patch
|
||||
BuildArch: noarch
|
||||
BuildRequires: autoconf
|
||||
BuildRequires: automake
|
||||
@ -111,6 +129,8 @@ end
|
||||
%autosetup -p1
|
||||
|
||||
%build
|
||||
# must run automake since patches touch .am files
|
||||
./autogen.sh
|
||||
%configure --enable-sysconfig --enable-rpmmacros PYTHON="%{__python3} %{py3_shbang_opts}"
|
||||
make %{?_smp_mflags}
|
||||
|
||||
@ -230,6 +250,26 @@ rm -rf %{buildroot}%{_datadir}/firewalld/testsuite
|
||||
%{_mandir}/man1/firewall-config*.1*
|
||||
|
||||
%changelog
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-7
|
||||
- feat(nftables): table ownership
|
||||
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-6
|
||||
- feat(IPv6_rpfilter): support loose rpfilter
|
||||
- feat(IPv6_rpfilter): support loose-forward rpfilter
|
||||
- feat(IPv6_rpfilter): support strict-forward rpfilter
|
||||
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-5
|
||||
- feat(fw): add ReloadPolicy option in firewalld.conf
|
||||
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-4
|
||||
- fix(rich): validate service name of rich rule
|
||||
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-3
|
||||
- feat(icmp): add ICMPv6 Multicast Listener Discovery (MLD) types
|
||||
|
||||
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-2
|
||||
- feat(service): add OpenTelemetry (OTLP) service
|
||||
|
||||
* Thu Oct 26 2023 Eric Garver <egarver@redhat.com> - 1.3.4-1
|
||||
- package rebase to v1.3.4
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user