mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
patches: lift iptables name length limit in firewalld
A legacy name length limit in firewalld triggered problems with longer policy names. This patch to firewalld lifts that limit by checking the backend in use, no limit for nftables. Fixes #1389 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
From 03f273fc540082d1eaa23bd9b5847e695afd8283 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Thu, 25 Sep 2025 15:00:54 +0200
|
||||
Subject: [PATCH] Silence warnings about old backends
|
||||
Subject: [PATCH 1/2] Silence warnings about old backends
|
||||
Organization: Wires
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
From 6ab218fe7f2c7027cc5347e3b082285870c502e6 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Fri, 6 Mar 2026 07:44:38 +0100
|
||||
Subject: [PATCH 2/2] fix(functions): lift iptables name length limit when
|
||||
using nftables
|
||||
Organization: Wires
|
||||
|
||||
The max_zone_name_len() and max_policy_name_len() functions return 17
|
||||
and 18 respectively, derived from iptables' 28-char netfilter chain name
|
||||
limit. These limits are applied unconditionally in Zone.check_name()
|
||||
and Policy.check_name() regardless of the active backend.
|
||||
|
||||
When FirewallBackend=nftables nftables imposes no such restriction, so
|
||||
user-defined zone and policy names (e.g. "appletv-to-lan-guest", 20
|
||||
chars) that exceed the iptables-derived limit are incorrectly rejected.
|
||||
|
||||
Add _nftables_backend() which reads firewalld.conf directly so the check
|
||||
can be skipped without threading backend context through to check_name()
|
||||
call sites, which have no access to all_io_objects. When nftables is
|
||||
active, both functions return sys.maxsize, effectively disabling the
|
||||
length check.
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
src/firewall/functions.py | 31 ++++++++++++++++++++++++++++++-
|
||||
1 file changed, 30 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/firewall/functions.py b/src/firewall/functions.py
|
||||
index 27c862fd..1b8a32ce 100644
|
||||
--- a/src/firewall/functions.py
|
||||
+++ b/src/firewall/functions.py
|
||||
@@ -10,9 +10,10 @@ import os
|
||||
import os.path
|
||||
import shlex
|
||||
import string
|
||||
+import sys
|
||||
import tempfile
|
||||
from firewall.core.logger import log
|
||||
-from firewall.config import FIREWALLD_TEMPDIR, FIREWALLD_PIDFILE
|
||||
+from firewall.config import FIREWALLD_CONF, FIREWALLD_TEMPDIR, FIREWALLD_PIDFILE
|
||||
|
||||
NOPRINT_TRANS_TABLE = {
|
||||
# Limit to C0 and C1 code points. Building entries for all unicode code
|
||||
@@ -576,12 +577,35 @@ def ppid_of_pid(pid):
|
||||
return pid
|
||||
|
||||
|
||||
+def _nftables_backend():
|
||||
+ """Return True if FirewallBackend=nftables is configured in firewalld.conf.
|
||||
+
|
||||
+ When using nftables the iptables-derived 28-char chain name limit does not
|
||||
+ apply. Reading the config file directly avoids threading backend context
|
||||
+ through check_name() call sites, which have no access to all_io_objects.
|
||||
+ """
|
||||
+ try:
|
||||
+ with open(FIREWALLD_CONF) as f:
|
||||
+ for line in f:
|
||||
+ line = line.strip()
|
||||
+ if line.startswith("FirewallBackend="):
|
||||
+ return line.split("=", 1)[1].strip() == "nftables"
|
||||
+ except OSError:
|
||||
+ pass
|
||||
+ return False
|
||||
+
|
||||
+
|
||||
def max_policy_name_len():
|
||||
"""
|
||||
iptables limits length of chain to (currently) 28 chars.
|
||||
The longest chain we create is POST_<policy>_allow,
|
||||
which leaves 28 - 11 = 17 chars for <policy>.
|
||||
+
|
||||
+ When using the nftables backend, nftables imposes no practical name length
|
||||
+ restriction, so we return sys.maxsize to lift the check entirely.
|
||||
"""
|
||||
+ if _nftables_backend():
|
||||
+ return sys.maxsize
|
||||
from firewall.core.ipXtables import POLICY_CHAIN_PREFIX
|
||||
from firewall.core.base import SHORTCUTS
|
||||
|
||||
@@ -594,7 +618,12 @@ def max_zone_name_len():
|
||||
Netfilter limits length of chain to (currently) 28 chars.
|
||||
The longest chain we create is POST_<zone>_allow,
|
||||
which leaves 28 - 11 = 17 chars for <zone>.
|
||||
+
|
||||
+ When using the nftables backend, nftables imposes no practical name length
|
||||
+ restriction, so we return sys.maxsize to lift the check entirely.
|
||||
"""
|
||||
+ if _nftables_backend():
|
||||
+ return sys.maxsize
|
||||
from firewall.core.base import SHORTCUTS
|
||||
|
||||
longest_shortcut = max(map(len, SHORTCUTS.values()))
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -8,7 +8,7 @@ Firewall configuration suitable for end devices on untrusted networks.
|
||||
|
||||
image::basic.svg[align=center, scaledwidth=50%]
|
||||
|
||||
- Single zone configuration, "public", with action=drop
|
||||
- Single zone configuration, "public-untrusted-net", with `action=drop`
|
||||
- Allowed services: SSH (port 22), DHCPv6-client, mySSH (custom, port 222)
|
||||
- All other ports (HTTP, HTTPS, Telnet, etc.) blocked
|
||||
- Check that unused interfaces are automatically assigned to default zone
|
||||
|
||||
@@ -5,7 +5,7 @@ Firewall configuration suitable for end devices on untrusted networks.
|
||||
|
||||
image::basic.svg[align=center, scaledwidth=50%]
|
||||
|
||||
- Single zone configuration, "public", with action=drop
|
||||
- Single zone configuration, "public-untrusted-net", with `action=drop`
|
||||
- Allowed services: SSH (port 22), DHCPv6-client, mySSH (custom, port 222)
|
||||
- All other ports (HTTP, HTTPS, Telnet, etc.) blocked
|
||||
- Check that unused interfaces are automatically assigned to default zone
|
||||
@@ -47,7 +47,7 @@ with infamy.Test() as test:
|
||||
|
||||
target.put_config_dict("infix-firewall", {
|
||||
"firewall": {
|
||||
"default": "public",
|
||||
"default": "public-untrusted-net",
|
||||
"logging": "all",
|
||||
"service": [{
|
||||
"name": "mySSH",
|
||||
@@ -69,7 +69,9 @@ with infamy.Test() as test:
|
||||
"interface": [mgmt_if],
|
||||
"service": ["ssh", "netconf", "restconf"]
|
||||
}, {
|
||||
"name": "public",
|
||||
# 20-char name, exceeds old iptables-derived 17-char limit
|
||||
# Verifies we allow long names with nftables, issue #1389
|
||||
"name": "public-untrusted-net",
|
||||
"description": "Public untrusted network",
|
||||
"action": "drop",
|
||||
"interface": [data_if],
|
||||
@@ -80,7 +82,7 @@ with infamy.Test() as test:
|
||||
|
||||
# Wait for configuration to be activated
|
||||
infamy.Firewall.wait_for_operational(target, {
|
||||
"public": {"action": "drop"},
|
||||
"public-untrusted-net": {"action": "drop"},
|
||||
"mgmt": {"action": "accept"}
|
||||
})
|
||||
|
||||
@@ -88,7 +90,7 @@ with infamy.Test() as test:
|
||||
data = target.get_data("/infix-firewall:firewall")
|
||||
fw = data["firewall"]
|
||||
|
||||
assert fw["default"] == "public"
|
||||
assert fw["default"] == "public-untrusted-net"
|
||||
|
||||
services = {svc["name"]: svc for svc in fw.get("service", [])}
|
||||
assert "mySSH" in services, "Custom service mySSH not found"
|
||||
@@ -106,8 +108,8 @@ with infamy.Test() as test:
|
||||
assert int(port_entry["lower"]) == 8080
|
||||
|
||||
zones = {zone["name"]: zone for zone in fw["zone"]}
|
||||
assert "public" in zones, "Public zone not found in configuration"
|
||||
public_zone = zones["public"]
|
||||
assert "public-untrusted-net" in zones, "public-untrusted-net zone not found in configuration"
|
||||
public_zone = zones["public-untrusted-net"]
|
||||
assert public_zone["action"] == "drop"
|
||||
assert data_if in public_zone["interface"]
|
||||
assert "ssh" in public_zone["service"]
|
||||
@@ -119,13 +121,13 @@ with infamy.Test() as test:
|
||||
data = target.get_data("/infix-firewall:firewall")
|
||||
fw = data["firewall"]
|
||||
|
||||
assert fw["default"] == "public", "Default zone should be 'public'"
|
||||
assert fw["default"] == "public-untrusted-net", "Default zone should be 'public-untrusted-net'"
|
||||
|
||||
zones = {zone["name"]: zone for zone in fw["zone"]}
|
||||
public_zone = zones["public"]
|
||||
public_zone = zones["public-untrusted-net"]
|
||||
|
||||
assert unused_if in public_zone["interface"], \
|
||||
f"Unused interface {unused_if} should be in default zone 'public', got interfaces: {public_zone['interface']}"
|
||||
f"Unused interface {unused_if} should be in default zone 'public-untrusted-net', got interfaces: {public_zone['interface']}"
|
||||
|
||||
with infamy.IsolatedMacVlan(host_data) as ns:
|
||||
ns.addip(HOST_IP)
|
||||
|
||||
Reference in New Issue
Block a user