Skip to content

Firewall the SSH port

Severity: highApplies to: Debian / Ubuntu (ufw)Applies to: RHEL / Rocky / Alma (firewalld)
The fix
Terminal window
# ufw — Debian, Ubuntu
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw deny 22/tcp
# firewalld — RHEL, Rocky, Alma
sudo firewall-cmd --permanent --new-zone=sshadmin
sudo firewall-cmd --permanent --zone=sshadmin --add-source=203.0.113.10
sudo firewall-cmd --permanent --zone=sshadmin --add-service=ssh
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
sudo firewall-cmd --reload

Replace 203.0.113.10 with the address you administer from.

Every other control on this list hardens a daemon that attackers can still reach. A firewall rule means they never get to it. Authentication bugs, a misconfigured directive, a bad upgrade — none of it is reachable from an address that can’t open the socket.

This is the highest-leverage SSH control available when you have a fixed administrative address or a bastion. It closes the exposure rather than narrowing it.

Moving SSH to port 2222 is commonly presented as hardening. It isn’t, and it’s worth being precise about why:

  • It does cut log noise substantially. Broad internet scans hammer port 22 and mostly don’t follow to high ports, so your auth log gets quieter.
  • It does not stop a targeted attacker. A full port scan finds the daemon in seconds, and the banner identifies it immediately.

Quieter logs have real operational value — it’s easier to see a genuine attack when the noise is gone. Just don’t count it as an access control. If you want the noise gone and the exposure closed, the firewall rule does both, and the port change does one.

If you do change the port, note the ordering trap: add the new rule and verify the new port works before removing the old one.

On Ubuntu 22.10 and newer, the port lives in two places

Section titled “On Ubuntu 22.10 and newer, the port lives in two places”

Ubuntu moved SSH to systemd socket activation in 22.10, which means ssh.socket owns the listener and Port in sshd_config is not the whole story. Check which model you’re on:

Terminal window
systemctl is-enabled ssh.socket

If that returns enabled, a port change needs the socket restarted, not the service reloaded:

Terminal window
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket

And to stop it listening on both the old and new port, override the socket explicitly — the empty ListenStream= clears the inherited value first:

/etc/systemd/system/ssh.socket.d/listen.conf
[Socket]
ListenStream=
ListenStream=2222