WIUNIX ACADEMY
Atomic nftables changes: rehearse timeout rollback before risking remote access
Page 2 of 4. Review the earlier steps and prerequisites before continuing.
YOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
USE YOUR OWN VALUES
Configure this tutorial
Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.
Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.
Set values for my environment8 editable values
LET’S BUILD
Reserve the baseline and intentionally bad proposal files
Create two new root-only files in the exclusive evidence directory and open them with sudoedit. Paste each following panel into its matching file. Existing files cause refusal; if the second reservation fails, the first empty file can remain. Resolve that exact collision before continuing.
These files are lab inputs, not host startup configuration. Do not place either into a distribution firewall include directory or attach it to a production boot service.
Terminal on the disposable lab VM
sudo sh -c 'set -euC; : > "$1/baseline.nft"; : > "$1/candidate.nft"' sh '/var/lib/academy-nft-rollback-lab' || exit 1
sudo chmod 0600 '/var/lib/academy-nft-rollback-lab/baseline.nft' '/var/lib/academy-nft-rollback-lab/candidate.nft' || exit 1
sudoedit '/var/lib/academy-nft-rollback-lab/baseline.nft' '/var/lib/academy-nft-rollback-lab/candidate.nft' || exit 1
Two newly reserved files are ready for the following baseline and candidate content.
Permit only the isolated client’s echo request
The baseline creates one inet input chain with a drop policy and permits loopback and the chosen client’s ICMP echo requests. Replies leave through the namespace’s otherwise unfiltered output path. This intentionally tiny IPv4 experiment is not a complete IPv4/IPv6, DNS or SSH policy.
The table name is quoted so an accepted public identifier is not interpreted as nft syntax. On an actual host, hook priority, other chains and manager-owned policy would also need review; our fresh namespace keeps that scope explicit.
File contents — not a shell command
table inet "academy_guard" {
chain input {
type filter hook input priority 0; policy drop;
iifname "lo" accept
ip saddr 192.0.2.2 icmp type echo-request counter accept
}
}
Make a syntactically valid proposal that blocks the probe
The proposal removes and recreates only the lab table in one native nft batch. Its explicit drop rule replaces the baseline echo-request allow. Both table operations commit together when this file is applied; a shell loop of separate deletion and addition commands would create a different, inconsistent transition.
A check of this file can succeed even though the intended reachability is broken. We use that deliberate mistake to test recovery rather than pretending parser acceptance is a connectivity test.
File contents — not a shell command
delete table inet "academy_guard"
table inet "academy_guard" {
chain input {
type filter hook input priority 0; policy drop;
iifname "lo" accept
ip saddr 192.0.2.2 icmp type echo-request counter drop
}
}
LET’S BUILD
Check and load the baseline inside the server namespace
The first invocation asks the kernel/userspace stack to validate the batch without applying it. The second applies it to the explicitly named namespace. Confirm that the same isolated client can still reach the server and inspect this table’s counter.
Do not omit the namespace wrapper or replace the target with the host ruleset. If the baseline probe fails, stop here: there is no known-good connectivity state to restore yet.
Terminal on the disposable lab VM
sudo ip netns exec 'academy-nft-server' nft -c -f '/var/lib/academy-nft-rollback-lab/baseline.nft' || exit 1
sudo ip netns exec 'academy-nft-server' nft -f '/var/lib/academy-nft-rollback-lab/baseline.nft' || exit 1
sudo ip netns exec 'academy-nft-client' ping -c 2 -W 2 '192.0.2.1' || exit 1
sudo ip netns exec 'academy-nft-server' nft -n list table inet '"academy_guard"'
The baseline table is present and the isolated probe still succeeds.
LET’S BUILD
Capture a table-scoped restore and check both batches
Build a root-only rollback file from this table’s actual baseline. It first deletes the table, then recreates the captured policy in the same nft transaction. A plain listing is not an exclusive replacement: loading it into existing objects can merge or duplicate configuration instead of undoing a change.
The stateless listing deliberately omits live counter state. It is a policy snapshot, not a backup of connection tracking, dynamic external manager state or application sessions. This fixture has no named timeout sets or dependent cross-table objects. A stale snapshot on a managed host could undo someone else’s later changes; that is why this restore owns one isolated table and no one edits it concurrently.
Terminal on the disposable lab VM
sudo sh -c 'set -euC
out="$1/rollback.nft"
printf "delete table inet \"%s\"\n" "$3" > "$out"
ip netns exec "$2" nft -s -n list table inet "\"$3\"" >> "$out"
chmod 0600 "$out"
' sh '/var/lib/academy-nft-rollback-lab' 'academy-nft-server' 'academy_guard' || exit 1
sudo ip netns exec 'academy-nft-server' nft -c -f '/var/lib/academy-nft-rollback-lab/rollback.nft' || exit 1
sudo ip netns exec 'academy-nft-server' nft -c -f '/var/lib/academy-nft-rollback-lab/candidate.nft' || exit 1
A complete table-scoped rollback file and candidate both pass the namespace check. A failed capture/check stops the experiment.
LET’S BUILD
Prove the detached timer can execute the known-good restore
Schedule a separate short rehearsal timer through the system service manager. Its command takes the same local file lock and restores the unchanged baseline in the namespace. It is not a background sleep tied to this terminal. Wait for the explicit success result and repeat the probe before arming the actual change.
If the VM is heavily delayed, the four-second observation may arrive before completion; inspect the named timer/service and wait for its real result rather than calling it a pass. RemainAfterExit keeps the successful oneshot active in exited state for inspection until cleanup; this one-shot timer is not a recurring job.
Terminal on the disposable lab VM
ip_bin=$(sudo sh -c 'command -v ip') || exit 1
nft_bin=$(sudo sh -c 'command -v nft') || exit 1
flock_bin=$(sudo sh -c 'command -v flock') || exit 1
sudo systemd-run --unit='academy-nft-rollback-rehearsal' --on-active=2s --timer-property=AccuracySec=1s --property=Type=oneshot --property=RemainAfterExit=yes "$flock_bin" -x '/var/lib/academy-nft-rollback-lab/change.lock' "$ip_bin" netns exec 'academy-nft-server' "$nft_bin" -f '/var/lib/academy-nft-rollback-lab/rollback.nft' || exit 1
sleep 4
test "$(systemctl show -p ActiveState --value 'academy-nft-rollback-rehearsal.service')" = active || exit 1
test "$(systemctl show -p SubState --value 'academy-nft-rollback-rehearsal.service')" = exited || exit 1
test "$(systemctl show -p Result --value 'academy-nft-rollback-rehearsal.service')" = success || exit 1
test "$(systemctl show -p ExecMainStatus --value 'academy-nft-rollback-rehearsal.service')" = 0 || exit 1
sudo journalctl -u 'academy-nft-rollback-rehearsal.service' --no-pager -n 30
sudo ip netns exec 'academy-nft-client' ping -c 2 -W 2 '192.0.2.1' || exit 1
The rehearsal service has Result=success and exit status zero, and the isolated client still reaches the server.