Idempotent UFW Configuration Over SSH (Without Losing Your Connection)
A fresh cloud server or VPS comes with every port wide open. Your provider doesn’t
know what you’re running, so they let everything through. This is fine for about
five minutes — until the first bot finds your IP and starts probing every exposed
service. If you’re running something with a web UI (VNC, admin panels, databases),
that’s an open door to anyone with a port scanner. The fix is simple: firewall
everything except SSH, and tunnel in for the rest. One port exposed, one
authentication method, one key pair. Everything else is invisible from the outside.
The problem
You want a script that guarantees a clean firewall state: SSH allowed, everything
else denied. Something you can run as part of provisioning, or re-run later if you
suspect the rules have been tampered with.
One approach is to selectively delete rules: allow SSH, then loop through ufw status numbered and delete everything that isn’t SSH. This sounds clever but is
fragile in practice — you’re parsing human-readable output with grep, and one
mismatched line can delete your SSH rule. We learned this the hard way when a
provisioning script kept locking us out of fresh servers.
The solution
ufw –force reset and start clean.
#!/usr/bin/env bash
set -euo pipefail
sudo ufw --force reset
sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw --force enable
sudo ufw status verboseA few things to note:
- Reset is safe.
ufw resetdisables the firewall and removes all rules. Disabling
means no filtering — all traffic passes through. It does not mean “block
everything.” Your SSH session survives because there’s nothing to drop it. The brief
window of no firewall is seconds, not a risk. - Then build from zero. Allow SSH, deny everything else incoming, allow outgoing,
enable. No parsing, no loops, no fragile grep chains. –force suppressesconfirmation prompts. Both ufw reset and ufw enable want
interactive confirmation by default, which doesn’t work over scripted SSH. Avoidyes | ufw enable— the pipe can causeSIGPIPE(exit 141) when UFW closes stdin beforeyesstops writing.- Idempotent. Run it once or ten times, you get the same result: SSH in, everything
else out.
Opening additional ports
The firewall starts locked down, but you can add rules for services you want to
expose publicly:
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 8080/tcp # custom app portThese survive reboots. Re-running the reset script will wipe them — add any
permanent rules to the script itself, after the allow OpenSSH line.