Linux networking

A Minimal nftables Host Firewall You Can Still Recover From

A host firewall is only useful if its live policy is explicit, persistent, and recoverable from a remote mistake.

Inventory before writing policy

Start with listening sockets, routes, interfaces, and the active ruleset. A firewall file written from memory will eventually omit a transport, an address family, or the management path keeping the session alive.

sudo ss -H -lntup
ip -br address
ip route show
sudo nft -a list ruleset

Record SSH's source network and port separately. Keep a second authenticated session open. If the provider has a console, confirm that it works before treating it as a recovery route.

Build the baseline around state

A small host usually needs loopback, established traffic, control traffic such as ICMP, the management path, and a short list of public services. State TCP and UDP rules independently.

table inet host_firewall {
  chain input {
    type filter hook input priority 0; policy drop;
    ct state invalid drop
    ct state established,related accept
    iifname "lo" accept
    ip protocol icmp accept
    meta l4proto ipv6-icmp accept
    ip saddr 198.51.100.0/24 tcp dport 22 accept
    tcp dport 443 accept
    udp dport 443 accept
  }
}

The documentation prefix 198.51.100.0/24 is not a real allowlist. Replace it deliberately. Keep provider firewall rules aligned, but do not assume they replace host policy.

Arm a timed rollback

Save a restorable snapshot before loading a full ruleset. Prefixing the snapshot with flush ruleset makes it a replacement rather than a second copy layered over the mistake.

snapshot="/root/nft-before-$(date +%s).nft"
{ printf '%s\n' 'flush ruleset'; sudo nft list ruleset; } > "$snapshot"
sudo nft -c -f "$snapshot"
sudo systemd-run --unit=nft-rollback --on-active=5m \
  /usr/sbin/nft -f "$snapshot"

Then validate the candidate with nft -c -f before applying it. After external verification, stop the timer rather than merely stopping a service that has not run yet.

sudo systemctl stop nft-rollback.timer
sudo systemctl reset-failed nft-rollback.service

Verify the live and saved copies

A successful load proves syntax and kernel acceptance. It does not prove the intended services work. Open a fresh SSH connection, test each public protocol externally, and inspect counters on the rules expected to match.

sudo nft -a list ruleset
sudo nft -c -f /etc/nftables.conf
systemctl is-enabled nftables.service
systemctl is-active nftables.service

Finally, reboot during a controlled window and repeat the listener, ruleset, SSH, and application checks. Persistence is a separate property from a healthy runtime rule.

Firewall checklist

  • Inventory live sockets and routes first.
  • Keep loopback, established traffic, and both ICMP families.
  • Name every public port with its transport.
  • Restrict SSH to a known path where practical.
  • Validate the candidate and arm a timed rollback.
  • Test a fresh SSH session and real applications externally.
  • Verify persistence after reboot.