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 verbose
A 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 port
These survive reboots. Re-running the reset script will wipe them — add any
permanent rules to the script itself, after the allow OpenSSH line.