42.1 SSH Hardening: Disabling Root Login, Password Auth, and Limiting Ciphers
Right, let’s talk about SSH. It’s the front door to your server, and right now, yours is probably unlocked, with a welcome mat and a neon “Hack Me” sign flashing above it. We’re going to change that. The goal isn’t just to lock the door; it’s to make the door itself nearly invisible to anyone but you. We’ll do this by kicking out the most privileged user, getting rid of the flimsiest lock, and only using keys that can’t be copied.
First, a word of caution that I cannot stress enough: DO NOT LOG OUT OF YOUR CURRENT SSH SESSION UNTIL YOU ARE ABSOLUTELY POSITIVE YOUR NEW ONE WORKS. Open a second terminal window and test your new configuration there. If you screw this up and lock yourself out, your only option will be to use a virtual console (if you’re lucky) or drive to the data center (if you’re not). I’m not kidding. I’ve done the latter. It’s a profoundly humbling experience.
Disabling Root Login
Let’s start by evicting the king. The root user is the ultimate prize for an attacker. By allowing SSH logins directly as root, you’re giving them a chance to brute-force the keys to the kingdom without even needing a username. It’s a terrible idea, and disabling it is non-negotiable.
The directive in your sshd_config is PermitRootLogin. You’ll want to set it to no. Some older guides might suggest without-password, which means “yes, but only with a key.” This is slightly better than allowing passwords, but it’s still a bad practice. Why? Because it tells everyone on the internet that a valid root user account exists on this machine. It’s an unnecessary data leak. We practice principle of least privilege: you log in as a regular user and then escalate your privileges using sudo. This creates an audit trail and adds a tiny, yet valuable, extra step.
Here’s how you do it. Find the line (or add it if it’s not there) in /etc/ssh/sshd_config:
# Edit the config file. I like nano for simplicity, but use vim if you enjoy a text-based puzzle.
sudo nano /etc/ssh/sshd_config
# Find the PermitRootLogin line and change it to:
PermitRootLogin no
Disabling Password Authentication
This is the single most effective change you can make. Password authentication is fundamentally vulnerable to brute-force attacks. Servers are constantly bombarded by bots trying common username/password combinations. Disabling it forces everyone to use cryptographic key pairs, which are, for all practical purposes, unbreakable.
The directive is PasswordAuthentication. Set it to no. This renders all password guesses useless because the server won’t even entertain the idea.
# In the same /etc/ssh/sshd_config file, find and change:
PasswordAuthentication no
Now, the critical pitfall: this will lock you out if you don’t have an SSH key configured for your user. Before you reload the SSH service, you must have your public key in ~/.ssh/authorized_keys on the server. If you don’t know how to do that, google “ssh-copy-id” and come right back. I’ll wait.
Limiting Key Exchange, Ciphers, and MACs
This is where we get into the weeds, and honestly, where the SSH config gets a bit… crusty. Over the years, algorithms have been found to be weak. We need to explicitly tell our SSH server to only use the modern, strong ones and to ignore the old, broken ones. If we don’t, a client could negotiate a weak connection.
The names and syntax for these directives have changed between versions of OpenSSH, which is a fantastic source of confusion. For a modern server (OpenSSH 8.4+), we use KexAlgorithms (Key Exchange), Ciphers (for the encryption), and MACs (Message Authentication Codes - for integrity).
Here’s a robust, modern configuration that tells bots from the early 2000s to get off your lawn:
# Add these lines to the bottom of your sshd_config, or uncomment and change existing ones.
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
Why these? curve25519 is a modern, fast, and secure key exchange algorithm. We prioritize chacha20-poly1305 and GCM modes because they are authenticated encryption modes, which are more efficient and just better designed than the older CBC modes. The -etm (Encrypt-then-MAC) versions of the MAC algorithms are crucial because they fix a potential timing vulnerability in the older MAC-then-encrypt scheme. This is the kind of detail that separates a secure setup from a textbook one.
Putting It All Together and Testing
After making these changes, you need to restart the SSH service. But first, let’s do a dry run to check for syntax errors. This is your last chance to avoid locking yourself out.
# Check the config file for syntax mistakes
sudo sshd -t
# If that returns nothing, your config is syntactically valid. Now restart ssh.
sudo systemctl restart sshd
Now, in your second terminal window (you opened a second one, right?), test logging in. It should only work with your key and should refuse all password attempts. To be absolutely sure, try to connect in a way that should fail:
# This should fail spectacularly with "Permission denied (publickey)."
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no your_username@your_server
If that fails as expected, and your normal key-based login works, congratulations. You’ve just replaced that neon “Hack Me” sign with a solid slab of reinforced concrete. It’s not absolutely impenetrable, but you’ve moved from being low-hanging fruit to a genuinely hard target. And that’s 99% of the battle.