Changing rules safely
The classic firewall mistake is locking yourself out of a remote machine. The lab shows the sneaky version: a ruleset that seems fine.
The change narrows SSH to the admin PC, but with a typo: 10.10.0.2 instead of 10.10.0.20. It loads without complaint. The SSH session that loaded it keeps working, because its packets are established. The mistake only shows when you log in again, and then there's no way in. Recovery needed the console (in the lab, docker exec; on a real server, the provider's web console or a trip to the building).
Check first
nft -c -f FILE parses the file and checks every rule against the kernel without changing anything. It catches syntax errors and unknown names (here, acept), with the line and position. It can't catch a rule that's valid but wrong, like the typo'd address.
A timed rollback
Before loading a risky change on a remote machine, schedule its undo:
systemd-run --on-active=60s --timer-property=AccuracySec=1s --unit=nft-rollback nft -f /etc/nftables.conf
nft -f /root/new.conf
The timer reloads the known-good /etc/nftables.conf in a minute, whatever happens. Then, from a new SSH session, check you can still get in. If you can, make the change permanent (copy it over /etc/nftables.conf) and cancel the rollback with systemctl stop nft-rollback.timer. If you can't, wait a minute and you're back.
In the lab, the typo'd change locked SSH out again, and access came back by itself 30 seconds later. Note AccuracySec=1s: systemd timers fire within a minute of their time by default, to save power. In an earlier run without it, a 30-second rollback still hadn't happened 40 seconds later.
The same idea works with any change: keep the working configuration in place, test the new one with an automatic way back, and only then replace the file.
#!/usr/sbin/nft -f
# /root/new.conf: a change to test before it replaces /etc/nftables.conf.
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept comment "replies to traffic we started"
ct state invalid drop
iif lo accept comment "the machine talking to itself"
icmp type { echo-request, destination-unreachable, time-exceeded } accept
iifname "eth1" ip saddr 10.10.0.2 tcp dport 22 accept comment "SSH from the admin PC only"
counter comment "everything else hits the policy: drop"
}
chain output {
type filter hook output priority filter; policy accept;
}
}- Line 14: Meant to be 10.10.0.20, the admin PC. It's a valid address, so nothing complains.
root@pc:~# ssh fw "nft -f /root/new.conf && echo loaded; nft list chain inet filter input | grep admin"
loaded
iifname "eth1" ip saddr 10.10.0.2 tcp dport 22 accept comment "SSH from the admin PC only"
root@pc:~# ssh fw hostname
ssh: connect to host 10.10.0.1 port 22: Connection timed out
root@fw:~# systemctl reload nftables
root@pc:~# ssh fw hostname
fw- Lines 1–3: Loaded from an SSH session, which keeps working: its packets are established.
- Lines 5–6: A new login is a new connection, and nothing allows it.
- Line 8: Recovery from the console: load the file in /etc again.
- Lines 10–11: Back in.
root@fw:~# nft -c -f /root/typo.conf; echo "exit code $?"
/root/typo.conf:14:59-65: Error: syntax error, unexpected comment
iifname "eth1" ip saddr 10.10.0.20 tcp dport 22 acept comment "SSH from the admin PC only"
^^^^^^^
exit code 1- Lines 2–4: The line and position of the problem. Nothing was loaded.
- Line 5: A non-zero exit code, so scripts can stop here.
root@pc:~# ssh fw "systemd-run --on-active=30s --timer-property=AccuracySec=1s --unit=nft-rollback nft -f /etc/nftables.conf && nft -f /root/new.conf && echo loaded"
Running timer as unit: nft-rollback.timer
Will run service as unit: nft-rollback.service
loaded
root@pc:~# ssh fw hostname
ssh: connect to host 10.10.0.1 port 22: Connection timed out
root@pc:~# sleep 30; ssh fw hostname
fw
root@pc:~# ssh fw "journalctl -u nft-rollback --no-pager -o short-iso | tail -2"
2026-10-21T07:46:49+00:00 fw systemd[1]: Started nft-rollback.service - [systemd-run] /usr/sbin/nft -f /etc/nftables.conf.
2026-10-21T07:46:49+00:00 fw systemd[1]: nft-rollback.service: Deactivated successfully.- Lines 1–4: First schedule the undo, then load the change.
- Lines 6–7: Locked out again…
- Lines 9–10: …but 30 seconds later the timer has reloaded the old rules.
- Lines 13–14: What the timer did.
root@pc:~# ssh fw "systemd-run --on-active=60s --timer-property=AccuracySec=1s --unit=nft-rollback nft -f /etc/nftables.conf && nft -f /root/new.conf && echo loaded"
Running timer as unit: nft-rollback.timer
Will run service as unit: nft-rollback.service
loaded
root@pc:~# ssh fw hostname
fw
root@pc:~# ssh fw "cp /root/new.conf /etc/nftables.conf && systemctl stop nft-rollback.timer && systemctl list-timers --all --no-pager | grep -c nft-rollback"
0- Lines 1–4: The file with the typo fixed (10.10.0.20), loaded with a minute's safety net.
- Lines 6–7: A brand-new session gets in: the change works.
- Lines 9–10: Make it permanent and cancel the rollback. No timer left.
Check your understanding
You load new rules over SSH and your session still works. Is the change safe?
- Not yet: test with a new SSH session, because your current one is allowed as established.
- Yes: if SSH were blocked, the session would have dropped.
- Yes, if
nft -cpassed first. - Only if the policy is accept.