firewall: switch to nftables - #3817
Open
mkg20001 wants to merge 17 commits into
Open
Conversation
…firewall-* Rename all firewall packages to implementation-neutral names before switching the implementation from ebtables/iptables to nftables: gluon-ebtables -> gluon-firewall gluon-ebtables-filter-multicast -> gluon-firewall-filter-multicast gluon-ebtables-filter-ra-dhcp -> gluon-firewall-filter-ra-dhcp gluon-ebtables-limit-arp -> gluon-firewall-limit-arp gluon-ebtables-source-filter -> gluon-firewall-source-filter gluon-iptables-clamp-mss-to-pmtu -> gluon-firewall-clamp-mss-to-pmtu The corresponding image-customization feature names change from ebtables-* to firewall-*. No functional changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
…bles/firewall4
Replace the ebtables init script with an upgrade script that assembles
a bridge-family nftables table ('bridge gluon') from Lua rule snippets
in /lib/gluon/nftables and registers it as a firewall4 nftables
include (ruleset-prepend). Rule files can also register additional
fw4 includes via include().
The base chains mirror the previously used ebtables chains: input,
forward and output run at filter priority, prerouting at dstnat
priority (equivalent to the ebtables nat table).
The multicast direction chains (IN_ONLY, OUT_ONLY, MULTICAST_*) move
into the new gluon-firewall-multicast package, keeping gluon-firewall
a mesh-agnostic framework. Unlike the ebtables version, the IN_ONLY/
OUT_ONLY drop rules only apply to packets on br-client, matching the
previous RETURN policy for other bridges.
Rules are (re)generated on gluon-reconfigure; the loader runs as
800-firewall so it sees configuration staged by earlier upgrade
scripts (e.g. gluon_bat0.gw_mode from 310-gluon-mesh-batman-adv-mesh).
firewall3 and the legacy iptables packages are dropped from the
default package selection in favor of firewall4.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
The iptables OUTPUT DNAT rule redirecting DNS queries of the gluon-mesh-vpn group to gluon-wan-dnsmasq becomes an nftables ruleset-append include with its own ip-family table, as firewall4 provides no output-hook NAT chain. The stale iptables include is removed from existing configurations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
The ip6tables mangle FORWARD TCPMSS rule becomes a firewall4 chain-append include on mangle_forward, still restricted to IPv6 like the previous family=ipv6 include. The stale iptables include is removed from existing configurations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
Straight translation of the ebtables rules. The DROP policy of the LOCAL_FORWARD chain becomes an explicit final drop rule, added after all allow rules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
Translation of the ebtables rules, including the meshtastic allow rules and the Gluon VXLAN multicast group drop. Notable differences: - nftables cannot compare two packet fields, so the bridge loop avoidance rules match only on the BLA group MAC destination and drop the --arp-gratuitous check. - masked MAC matches need the value pre-masked, as nftables compares against the literal constant. The package now depends on gluon-firewall-multicast, which provides the MULTICAST_OUT chains. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
Straight translation of the ebtables rules. The package now depends on gluon-firewall-multicast, which provides the IN_ONLY/OUT_ONLY chains. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
Straight translation of the next-node isolation, RA input/output and brmldproxy MLD report allow rules. The next-node PREROUTING rules run in the prerouting base chain, which uses dstnat priority like the previous ebtables nat table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
The daemon now manages a regular chain in the bridge gluon table: instead of flushing the ebtables RADV_FILTER chain (policy DROP) and adding an ACCEPT rule for the chosen router, it flushes the radv_filter chain and adds a single 'ether saddr != <router> drop' rule. As before, the chain accepts everything until a router has been chosen, and is reset to accept-all on daemon shutdown. The respondd provider parses the gateway MAC from the nft chain listing instead of the ebtables listing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
The daemon now maintains two plain nftables sets (limitmac with the client MACs from the translation table, datips with the DAT cache IPs) instead of per-address ebtables rules. The rate limiting itself happens in a static chain: a dynamic set keyed by source MAC provides the per-client 6/minute limit for members of limitmac (matching the previous per-client ebtables limit rules), followed by the node-wide 1/second limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
Update the developer and user documentation for the nftables-based firewall and drop the ebtables luacheck configuration, as no ebtables rule files remain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQSpgPimcLD7qJnT2sQLA
olsr-site enables the firewall filter features, but all three depend on gluon-mesh-batman-adv, so configuring an olsrd image fails. It also sets next_node.ip4 without a prefix4, which gluon-client-bridge's check_site requires in that case - image assembly then fails in that package's post-install script. Neither showed up so far because no CI job builds this site config. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RypBvVYcqtWU1E88btmLA
firewall4 depends on nftables-json, while the alias added with the nftables migration pointed at nftables-nojson - the two variants conflict, so configuring an image with the firewall stack failed with "unable to enable package 'nftables-nojson'". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RypBvVYcqtWU1E88btmLA (cherry picked from commit d48ac7d)
800-firewall and 800-clamp-mss-to-pmtu were installed mode 644, so gluon's upgrade runner skipped them without a word: no firewall4 includes were registered, the bridge gluon table was never created and the whole gluon firewall layer - multicast egress filtering, RA/DHCP direction filters, ARP rate limiting - was missing at runtime on every nftables image. bridge.nft held nothing but its generated header. Also give the drop rules a counter, so what the firewall does is visible on the device the same way the ebtables backend has always been. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RypBvVYcqtWU1E88btmLA
The gluon-ebtables-* -> gluon-firewall-* rename left three :doc: refs in historical release notes pointing at documents that no longer exist, which Sphinx reports as ref.doc warnings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MdNkzDnm8hEvgmPFS4b6Qs
Member
Author
|
@neocturne I've created a new PR. Regarding the uci-less way: I would almost prefer wiping /etc/config/firewall on reconfigure and only keeping user rules with a special prefix user_* or similar, as we already have packages adding firewall rules and removing the package keeps the firewall rule arround. We can't really avoid uci as we need to inject into chains of firewall4 occasionally and those could be injected via extra rules in /etc/nftables.d but since firewall4 manages cleanup of it's own table I'm not sure how stable that would be |
Renaming the packages to gluon-firewall-* renamed the feature flags with them, and an unhandled flag falls through to 'gluon-' .. flag, so every site that still asked for ebtables-filter-multicast and friends stopped resolving to any package at all - a build failure with nothing pointing at the rename. Every existing site config had to change in lockstep with the Gluon update. Feature definition files gain deprecated(old, new), which substitutes the new flag for the old one and warns. Sites keep building unchanged and can migrate on their own schedule. The substitution happens as the definition files are evaluated, so it covers both the default gluon-$flag fallback and any _'flag' test that comes after it - hence the deprecations at the top of package/features. Verified against the real thing: Freifunk Lübeck's site config, which still names all four ebtables features, resolves to exactly the package set the firewall-* names produce. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RypBvVYcqtWU1E88btmLA
fw4 rebuilds 'table bridge gluon' on every reload, which empties the
limitmac and datips sets. gluon-arp-limiter fills those from batman-adv's
translation table and DAT cache and only calls nft when its own view of
them changes, so it never refills a set emptied behind its back.
The visible symptom is a failed delete for an element that is long gone:
gluon-arp-limiter: Error: Could not process rule: No such file or directory
gluon-arp-limiter: delete element bridge gluon datips { 10.130.21.212 }
The quiet half is worse. With limitmac empty, 'arp saddr ether @limitmac'
cannot match, so the per-client ARP rate limit stops applying and only
the node-wide 1/second limit is left. On a node two reloads into its
uptime, limitmac held nothing while batman-adv listed eight clients.
fw4 runs script includes after the new ruleset is in place, which is
exactly where the daemon has to be told to start over. Verified on a
running node: straight after a reload both sets read 0, and the hook
brings them back to 82 DAT entries and 2 client MACs, matching
batman-adv.
The existing reload.d hooks do not cover this - they run on a gluon
reload, while this is triggered by anything that reloads the firewall,
an interface coming up included.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RypBvVYcqtWU1E88btmLA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
New PR superseeding #2783