I have been writing iptables rules from memory for the better part of fifteen years, and I have been getting them subtly wrong for about the same length of time. This weekend I finally moved the gateway box over to nftables and I am cross that I waited so long.
The thing that sold me was not performance, though the single in-kernel ruleset is tidier. It was that I could read the result. One file, /etc/nftables.conf, with the whole thing in it, and named sets so the IP lists live in one place instead of being smeared across forty -A lines.
table inet filter {
set admin_hosts {
type ipv4_addr
elements = { 10.0.0.5, 10.0.0.6 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
ip saddr @admin_hosts tcp dport 22 accept
tcp dport { 80, 443 } accept
}
}
That inet family doing both IPv4 and IPv6 in one table is the other quiet win. No more maintaining a parallel ip6tables ruleset that drifts out of sync the moment I forget it exists, which was always.
Migration was less dramatic than I feared. iptables-translate got me most of the way, I rewrote the rest by hand because the machine output is ugly, and nft -c -f checks the file before you commit to it. Reload is atomic, so a bad ruleset doesn't lock you out mid-apply. I still kept a console session open while I did it, because I am not an animal.