SSH is how most administrators reach a remote server, and it is also one of the most aggressively attacked services on the internet — any server with port 22 open receives thousands of login attempts a day, almost none of them from a human. The good news is that SSH can be made close to impenetrable with a handful of configuration changes. This guide covers what the real risk looks like and exactly how to close it.
For the server owner (plain English)
Picture your server's front door getting rattled by strangers all day, every day — not because anyone singled you out, but because a robot walks down every street on the internet trying every door handle. That is SSH traffic on a public server: automated bots trying common usernames and passwords, nonstop, from the moment it goes online.
The fix is not a better password — it's changing what kind of key opens the door at all. Swap the password lock for a key that cannot be guessed, remove the one username every server on earth shares (root), and the knocking continues but nothing the bots try ever works.
How bad are SSH attacks, really?
From the moment a server goes online, bots begin probing port 22. These are not targeted attacks against you specifically — they are automated scripts scanning every IP address on the internet for weak credentials. The scale is real: a typical public server sees hundreds to thousands of login attempts a day, accumulating into tens of thousands of attempts from hundreds of IPs across dozens of countries within weeks.
The method is simple — a brute-force attack: cycle through lists of common usernames and passwords, trying every combination. It requires no skill or knowledge of your specific server, just time and automation. (The same technique targets web login forms; see how to defend those in the login rate-limit guide.)
Why are passwords a poor choice for SSH?
A password has a fundamental weakness: it can be guessed. No matter how strong it is, enough time and computation eventually finds it. In practice, most successful password intrusions don't even need a long brute-force campaign — they succeed because the password is common or predictable, reused from another breached service, or technically compliant but still guessable. Passwords also carry risks brute force doesn't: they can be captured by malware on a compromised device, leaked from a breached password manager, and — unlike a key tied to one device — used from anywhere by whoever has them.
SSH keys: a different approach
Key authentication replaces the password with a cryptographic key pair: a private key that never leaves your computer, and a public key placed on the server. When you connect, the server challenges your client to prove it holds the private key — the client signs the challenge, the server verifies the signature with the public key. The private key itself never crosses the network, so even a fully intercepted exchange gives an attacker nothing usable. Without the key file (or the device holding it), brute force against a modern key is mathematically infeasible.
Why is the root account such a target?
Every Linux system has a superuser named root, with unrestricted access to everything — every file, every configuration, the ability to remove the operating system itself. Its defining weakness: the name is identical on every Linux server in the world. An attacker doesn't need to guess the username, only the password, which turns a two-variable problem (username + password) into a one-variable problem. Look at SSH logs on any public server and the pattern is consistent — the overwhelming majority of attempts target root by name.
Disabling root login
The fix is to disable direct SSH login as root entirely, so even a correctly guessed root password cannot be used to log in over SSH. This is controlled by the SSH daemon's config file, /etc/ssh/sshd_config, which lives on the server itself (not your local machine) and can only be edited with root or sudo privileges — it is not something a regular user account can change, by design. Both settings below are documented in the OpenSSH sshd_config manual, the authoritative reference for the daemon's options.
Open it with a terminal editor over your existing SSH session:
sudo nano /etc/ssh/sshd_config
# or: sudo vim /etc/ssh/sshd_configFind the PermitRootLogin line (it may be commented out with a #, or missing entirely) and set it to:
PermitRootLogin noSave the file — in nano, Ctrl+O then Enter to write, Ctrl+X to exit — then restart the SSH service for the change to take effect:
sudo systemctl restart sshAdministrative tasks that need root access still work after this — log in as a regular user and elevate with sudo when needed. Some distributions split this into drop-in files under /etc/ssh/sshd_config.d/ instead of the main file; if a value there overrides yours, edit or add it there — the daemon reads the main file and this directory together, and later entries win.
Which usernames should you avoid?
Because bots cycle through lists of common usernames, the account name itself matters. Names on standard attack wordlists get targeted far more heavily than an uncommon one:
root, admin, administrator, ubuntu, debian, ec2-user, centos, pi,
vagrant, deploy, git, ansible, jenkins, test, guest, user,
postgres, mysql, oracle, ftpuser, www-data, www, apache, nginxA server with a user named ubuntu (the default on Ubuntu cloud images) receives far more targeted attempts against that specific name than one with an unpredictable name. A good username is not on that list, not obviously tied to your name/company/domain, not a common word — and still memorable enough for you to type reliably.
How do you create a secure user account?
On a new server, the setup sequence is:
1. Create a user with an unpredictable name
sudo adduser yourusername2. Grant administrative privileges via the sudo group
sudo usermod -aG sudo yourusernameThis lets the user run commands as root via sudo without being root themselves.
3. Generate a key pair — on your local machine, not the server
Run this on the computer you connect from (your laptop or desktop), never on the server itself:
ssh-keygen -t ed25519 -C "your_comment"Accept the default file location and set a passphrase when prompted — the passphrase encrypts the private key file at rest, so a stolen laptop doesn't hand over the key on its own. This creates two files in ~/.ssh/ on your local machine:
id_ed25519— the private key. This never leaves your machine and is never copied to the server, uploaded, or shared anywhere. Anyone who obtains this file can authenticate as you, so treat it like the master credential it is.id_ed25519.pub— the public key. This is what gets copied to the server. It cannot be used to derive the private key, so there is no risk in it being visible — its only job is letting the server verify signatures made by your private key.
(ed25519 is the modern default — shorter keys, faster, and at least as strong as older RSA keys. If you ever need rsa for a legacy system, use at least 4096 bits: ssh-keygen -t rsa -b 4096.)
4. Install the public key on the server
The easiest path is ssh-copy-id, run from your local machine while you can still log in with a password (this is the last time you'll need to):
ssh-copy-id yourusername@yourserverOr do it manually — logged in to the server as yourusername:
mkdir -p /home/yourusername/.ssh
chmod 700 /home/yourusername/.ssh
echo "contents-of-your-id_ed25519.pub" > /home/yourusername/.ssh/authorized_keys
chmod 600 /home/yourusername/.ssh/authorized_keys
chown -R yourusername:yourusername /home/yourusername/.sshEither way, only the .pub file's contents go into authorized_keys on the server — never paste the private key anywhere.
5. Verify the new account works before changing anything else
6. Disable password authentication and root login
In /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
MaxAuthTries 3MaxAuthTries 3 caps authentication attempts per connection, slowing any automated attempt further.
7. Restart SSH
sudo systemctl restart sshWhat changes after you harden SSH?
Before these changes, an attacker has two clear paths in: the root account with a known username, or any other account where they might guess both username and password. After: root login is disabled, so even the correct root password does nothing over SSH; password authentication is off entirely, so no password works, correct or not; and an attacker now needs both the exact username and the private key file on your device. The attack surface shrinks dramatically — bots still probe port 22, and always will, but every attempt now fails immediately at the authentication stage. The connection is closed, the attempt is logged, and nothing is gained.
Do you still need Fail2ban?
Even with keys-only auth and no root login, bots keep trying — they don't know your configuration, so they keep knocking. Those attempts are harmless from a security standpoint at this point, but they add log noise and a small amount of connection overhead. Fail2ban watches the authentication log and automatically firewalls off any IP that exceeds a defined number of failed attempts within a time window. It doesn't meaningfully improve on keys-only security (password attacks already fail outright), but it cuts the noise.
How do you check your own exposure?
You can confirm a couple of these facts without touching the server's configuration files. Locally, sudo ss -tlnp shows what is actually listening and on which interface — cross-check it against what you intended to expose. The other half of the picture is the internet's view: whether port 22 is reachable from outside at all, alongside the other high-risk ports (databases, caches, admin services) that should almost never be.
A full external scan confirms whether port 22 — and the databases, caches and admin services that should never face the internet — are reachable from outside, the way an attacker's tooling sees them. It can't test your password or key config from outside (that would mean actively logging in, which this passive scanner deliberately never does); confirming exposure is the first half, the hardening steps above are the second.
Scan your server's external attack surface →SSH hardening is one piece of securing the server itself; pair it with the UFW firewall guide so port 22 (and everything else) is only reachable the way you actually intend.
What's the takeaway?
SSH security comes down to removing what attackers rely on: a known root username, a guessable or stealable password, a predictable account name, and unlimited attempts. Disable root login, switch to SSH keys only, pick an unpredictable username, and layer Fail2ban to cut the noise. None of these steps are complex, and together they push the realistic risk of unauthorized SSH access close to zero — the bots keep knocking, but there is nothing left for them to find.