Support nftables and iptables, preferring nftables #92

Open
claude wants to merge 1 commit from issue15_nftables_support into main
Collaborator

Closes #15.

Abstracts the packet-interception/NAT mechanism behind a firewallBackend interface (internal/nfq_forwarder/firewall.go) with two implementations:

  • iptablesBackend (firewall_iptables.go) - a near-mechanical move of the existing, already battle-hardened iptables logic (unchanged in substance).
  • nftablesBackend (firewall_nftables.go) - shells out to nft(8) and parses its structured -j JSON output, owning a dedicated inet nfq_forwarder table with two chains pinned to nftables' named priorities mangle/dstnat (confirmed empirically to resolve to -150/-100, preserving the same interception-before-DNAT ordering guarantee GitLab issue #4 depends on).

Configuration.FirewallBackend (firewall_backend: auto|iptables|nftables, default auto) drives selectFirewallBackend, called once at startup: auto-detection prefers nftables when actually usable (not just present on PATH - confirmed empirically that an unprivileged process can find nft but still fail a bare nft list tables), falling back to iptables otherwise. An explicit override skips detection and fails loudly if that backend doesn't work.

Packaging now bundles both iptables and nft private, capability-enabled copies (.goreleaser.yaml, scripts/nfpm-postinstall.sh, Makefile's install target, CI's defensive package-install block) - verified via a real make package run that both binaries build, get packaged into the .deb, and the postinstall script's setcap step covers both.

Real-host verification (this sandbox has genuine root): the nftables backend was verified end-to-end - rule creation, correct priorities/comments, counter reads, and full cleanup - against a real nft instance. The iptables backend's regression check hit an unrelated, pre-existing environment defect in this sandbox (the local iptables binary crashes with a glibc free(): invalid size on any invocation at all, confirmed via a raw shell call outside Go) - not caused by this refactor, since the exact rule-construction code is unchanged. See CLAUDE.md's Known Issues for the full verification status and what's still open (a clean-host iptables regression check, and real SSH traffic through the nftables backend).

Test plan

  • make lint clean
  • make test clean (all packages)
  • go build ./...
  • Real end-to-end verification of the nftables backend against a real nft instance (root)
  • Real make package build confirming both iptables and nft are bundled and setcap'd
  • Real-host iptables regression check on a host with a working iptables binary
  • Real SSH traffic end-to-end through an nftables-selected connection

🤖 Generated with Claude Code

Closes #15. Abstracts the packet-interception/NAT mechanism behind a `firewallBackend` interface (`internal/nfq_forwarder/firewall.go`) with two implementations: - `iptablesBackend` (`firewall_iptables.go`) - a near-mechanical move of the existing, already battle-hardened iptables logic (unchanged in substance). - `nftablesBackend` (`firewall_nftables.go`) - shells out to `nft(8)` and parses its structured `-j` JSON output, owning a dedicated `inet nfq_forwarder` table with two chains pinned to nftables' named priorities `mangle`/`dstnat` (confirmed empirically to resolve to -150/-100, preserving the same interception-before-DNAT ordering guarantee GitLab issue #4 depends on). `Configuration.FirewallBackend` (`firewall_backend: auto|iptables|nftables`, default `auto`) drives `selectFirewallBackend`, called once at startup: auto-detection prefers nftables when actually usable (not just present on PATH - confirmed empirically that an unprivileged process can find `nft` but still fail a bare `nft list tables`), falling back to iptables otherwise. An explicit override skips detection and fails loudly if that backend doesn't work. Packaging now bundles both `iptables` and `nft` private, capability-enabled copies (`.goreleaser.yaml`, `scripts/nfpm-postinstall.sh`, `Makefile`'s `install` target, CI's defensive package-install block) - verified via a real `make package` run that both binaries build, get packaged into the `.deb`, and the postinstall script's `setcap` step covers both. **Real-host verification** (this sandbox has genuine root): the nftables backend was verified end-to-end - rule creation, correct priorities/comments, counter reads, and full cleanup - against a real `nft` instance. The iptables backend's regression check hit an unrelated, pre-existing environment defect in this sandbox (the local `iptables` binary crashes with a glibc `free(): invalid size` on any invocation at all, confirmed via a raw shell call outside Go) - not caused by this refactor, since the exact rule-construction code is unchanged. See `CLAUDE.md`'s Known Issues for the full verification status and what's still open (a clean-host iptables regression check, and real SSH traffic through the nftables backend). ## Test plan - [x] `make lint` clean - [x] `make test` clean (all packages) - [x] `go build ./...` - [x] Real end-to-end verification of the nftables backend against a real `nft` instance (root) - [x] Real `make package` build confirming both `iptables` and `nft` are bundled and setcap'd - [ ] Real-host iptables regression check on a host with a working iptables binary - [ ] Real SSH traffic end-to-end through an nftables-selected connection 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Support nftables and iptables, preferring nftables
All checks were successful
CI / lint (push) Successful in 36s
CI / build (push) Successful in 32s
CI / lint (pull_request) Successful in 29s
CI / build (pull_request) Successful in 31s
CI / release (push) Has been skipped
CI / release (pull_request) Has been skipped
f87fb9082c
Abstracts the packet-interception/NAT mechanism behind a firewallBackend
interface with two implementations - iptables (the existing, unchanged logic)
and a new nftables backend shelling out to nft - so hosts without legacy
iptables can still run nfq_forwarder. Runtime auto-detection prefers nftables
when usable, falling back to iptables; firewall_backend in --config forces a
specific backend. Both binaries are now bundled and capability-enabled in
every packaged build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
claude requested review from danny 2026-07-10 11:15:35 +01:00
All checks were successful
CI / lint (push) Successful in 36s
CI / build (push) Successful in 32s
CI / lint (pull_request) Successful in 29s
Required
Details
CI / build (pull_request) Successful in 31s
Required
Details
CI / release (push) Has been skipped
CI / release (pull_request) Has been skipped
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin issue15_nftables_support:issue15_nftables_support
git switch issue15_nftables_support
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
keyop-go/nfq_forwarder!92
No description provided.