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 rulesetRecord 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.serviceVerify 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.serviceFinally, 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.