As soon as a server gets a public IP address, bots try to log in over SSH with lists of common passwords. This is not theoretical: run journalctl -u ssh | grep -c Failed on a VPS that has been online for a few days, and the count is often in the thousands. This guide makes those attempts useless.
1. Why it is the first thing to do
A password can be guessed, reused or leaked. An ED25519 SSH key, in practice, cannot be guessed. Three measures together remove most of the risk:
- Key-only authentication: passwords are no longer accepted at all.
- Root login forbidden: an attacker must also guess a username.
- Fail2ban: addresses that keep trying are banned automatically.
2. Create and install an SSH key
On your computer:
ssh-keygen -t ed25519 -C "alex@laptop"
Choose a passphrase: if your computer is stolen, the key stays unusable. Then copy the public key to the user account on the server (created in the first steps):
ssh-copy-id alex@SERVER_IP
On Windows, where ssh-copy-id does not exist, the equivalent command is given in first steps on your VPS. Check that ssh alex@SERVER_IP works without asking for a password before going further.
3. Harden the configuration
Rather than editing the main /etc/ssh/sshd_config file, create a dedicated file in the configuration directory: it is not overwritten by updates and keeps your settings in one place.
cat > /etc/ssh/sshd_config.d/00-hardening.conf <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
AllowUsers alex
EOF
AllowUsers only allows the listed accounts: separate them with spaces if you have several. MaxAuthTries drops the connection after three attempts.
4. The cloud-init trap
The file name starts with 00- for a reason. For each setting, OpenSSH keeps the first value it reads, and files in the directory are read in alphabetical order. Many Ubuntu images contain a 50-cloud-init.conf file with PasswordAuthentication yes. Named 99-..., your file would be read afterwards and silently ignored: passwords would still be accepted.
ls /etc/ssh/sshd_config.d/
To see which value really applies, ask the SSH server directly:
sshd -T | grep -E "permitrootlogin|passwordauthentication|kbdinteractive|allowusers"
5. Change the port (optional)
Moving away from port 22 does not stop an attacker targeting you, who will find the new port in seconds. It does, however, remove most automated attempts from your logs. If you do it, add the line Port 2222 to the file above, and open the port in the firewall first:
ufw allow 2222/tcp
systemctl daemon-reload then systemctl restart ssh.socket. On Debian 12, systemctl restart ssh is enough.6. Apply without locking yourself out
Check the syntax, then reload SSH. Do not close your current session.
sshd -t && systemctl reload ssh
Open a second terminal and test:
ssh alex@SERVER_IP # must work
ssh root@SERVER_IP # must be refused
ssh -o PubkeyAuthentication=no alex@SERVER_IP # must be refused
If the first command fails, fix things from the session you kept open. If you changed the port, add -p 2222 to each command; once the new port works, remove the old firewall rule with ufw delete allow OpenSSH.
7. Install Fail2ban
Fail2ban reads the logs and bans, through the firewall, addresses that pile up failures. Even with passwords disabled, it reduces load and log noise.
apt install -y fail2ban
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = ufw
[sshd]
enabled = true
backend = systemd
port = ssh
maxretry = 3
bantime = 24h
EOF
systemctl enable --now fail2ban
systemctl restart fail2ban
If you changed the port, replace port = ssh with port = 2222. backend = systemd is required on Debian 12, which no longer writes /var/log/auth.log by default; it sits in the [sshd] section rather than [DEFAULT] so it does not interfere with jails that read log files later, such as Nginx ones.
8. Verify
fail2ban-client status sshd
journalctl -u ssh --since "1 hour ago" | tail
The first command shows how many addresses are banned. Then add an allow-list firewall: configuring UFW.
Frequently asked questions
What if I locked myself out of my VPS?
Why do SSH passwords still work after setting PasswordAuthentication no?
RSA or ED25519 keys?
Is Fail2ban useful if passwords are disabled?
Related
First steps on a Linux VPS
The 15-minute checklist for a new server.
Configuring the UFW firewall
An allow-list firewall and the ports to open for each use.