The Problem: Your SSH Server Is a Target Right Now
If your server has port 22 open to the internet, it’s being scanned. Not maybe — it’s happening right now. Run this on any public-facing Linux server:
sudo journalctl -u ssh --since "1 hour ago" | grep "Failed password" | wc -l
You’ll likely see dozens or hundreds of failed login attempts. Bots continuously scan the internet for open SSH ports and hammer them with credential attacks. The two biggest vulnerabilities feeding these attacks are: allowing root login directly over SSH, and relying on passwords instead of cryptographic keys.
Passwords can be brute-forced. The root account, if compromised, gives an attacker full control of your machine with no extra steps needed. Put them together and you’ve got a server that’s trivially easy to compromise.
Root Cause: Why Default SSH Configuration Is Risky
Root Login Exposes Your Most Privileged Account
Most Linux distributions allow root SSH login by default (or at minimum don’t block it explicitly). When root login is permitted, an attacker only needs one thing: the password. There’s no privilege escalation step — one successful guess and they own everything.
Switch to a regular user with sudo instead. That adds a second layer: even if an attacker gets your user’s credentials, they still need the sudo password to do real damage.
Password Authentication Is Guessable
Even strong passwords can be cracked with enough time and compute. SSH key-based authentication uses asymmetric cryptography — a private key on your machine, a public key on the server. Without the private key file, login is mathematically impossible regardless of computing power.
On my production Ubuntu 22.04 server, the difference was measurable. Before switching to key-based auth, the server fielded 200–400 failed password attempts per hour. Each attempt triggers a full SSH handshake and key derivation — real CPU work. After disabling password auth entirely, that background noise disappeared.
Hands-On: Hardening Your SSH Server Step by Step
Step 1: Create a Non-Root User with Sudo Access
Before disabling root login, make sure you have another way in. Create a regular user if you don’t have one:
# On the server
sudo adduser deployuser
sudo usermod -aG sudo deployuser
Verify the user can use sudo before proceeding:
su - deployuser
sudo whoami
# Should output: root
Step 2: Generate an SSH Key Pair on Your Local Machine
Do this on your local workstation, not the server:
ssh-keygen -t ed25519 -C "deployuser@myserver" -f ~/.ssh/myserver_ed25519
Use ed25519 over RSA — it’s faster, the keys are shorter (68 characters vs 700+ for RSA-4096), and it’s just as secure. The -C flag adds a comment to identify the key. You’ll be asked for a passphrase — set one. It encrypts your private key file locally, so even if someone gets your laptop, the key is useless without the passphrase.
This creates two files:
~/.ssh/myserver_ed25519— your private key (never share this)~/.ssh/myserver_ed25519.pub— your public key (this goes on the server)
Step 3: Copy the Public Key to the Server
The easiest method:
ssh-copy-id -i ~/.ssh/myserver_ed25519.pub deployuser@your-server-ip
If ssh-copy-id isn’t available (common on macOS or Windows), do it manually:
# On the server, logged in as deployuser
mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
# Paste your public key content into this file
nano ~/.ssh/authorized_keys
Test that key-based login works before you disable password auth:
ssh -i ~/.ssh/myserver_ed25519 deployuser@your-server-ip
If this succeeds without prompting for a password (just the passphrase for your key if you set one), you’re good to proceed.
Step 4: Configure SSH to Disable Root Login and Password Authentication
Edit the SSH daemon config:
sudo nano /etc/ssh/sshd_config
Find and update these directives (add them if missing):
# Disable root login entirely
PermitRootLogin no
# Disable password-based authentication
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Only allow specific users (optional but recommended)
AllowUsers deployuser
# Disable challenge-response (includes PAM keyboard-interactive)
# OpenSSH 8.7+ renamed this to KbdInteractiveAuthentication — set both to be safe
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
# Keep alive to detect dead connections
ClientAliveInterval 300
ClientAliveCountMax 2
On Ubuntu 22.04+, there’s a catch. Check if there are override files in the sshd_config.d/ directory:
ls /etc/ssh/sshd_config.d/
cat /etc/ssh/sshd_config.d/*.conf 2>/dev/null
Ubuntu 22.04 ships with /etc/ssh/sshd_config.d/50-cloud-init.conf that re-enables PasswordAuthentication yes. Override it or delete it:
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config.d/50-cloud-init.conf
# or just remove the file if it only contains that line
# sudo rm /etc/ssh/sshd_config.d/50-cloud-init.conf
Step 5: Validate Config and Reload SSH
Always test your config before reloading — a syntax error can lock you out:
sudo sshd -t
# No output = no errors
sudo systemctl reload ssh
# On some distros: sudo systemctl reload sshd
Keep your current SSH session open. Open a new terminal window and test login:
ssh -i ~/.ssh/myserver_ed25519 deployuser@your-server-ip
Also verify that password login is now rejected:
ssh deployuser@your-server-ip
# Should immediately get: Permission denied (publickey)
Step 6: Optional — Change the Default SSH Port
Changing from port 22 doesn’t add real security, but it does dramatically reduce noise in your logs. Bots mostly scan port 22; moving to a non-standard port cuts automated attacks by over 90% in my experience:
# In /etc/ssh/sshd_config
Port 2222
If you’re using UFW:
sudo ufw allow 2222/tcp
sudo ufw delete allow ssh # removes port 22 rule
sudo ufw status
Connect with the custom port:
ssh -p 2222 -i ~/.ssh/myserver_ed25519 deployuser@your-server-ip
To avoid typing the port every time, add an entry to your local ~/.ssh/config:
Host myserver
HostName your-server-ip
User deployuser
Port 2222
IdentityFile ~/.ssh/myserver_ed25519
Now you can just run ssh myserver.
Step 7: Verify the Changes Took Effect
Check the running SSH configuration:
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|port'
Expected output:
port 2222
permitRootLogin no
passwordauthentication no
Check your auth log to confirm brute-force attempts are being rejected at the key stage rather than password stage:
sudo tail -f /var/log/auth.log | grep sshd
Bonus: Manage Multiple SSH Keys Cleanly
If you manage several servers, keeping keys organized prevents confusion:
# ~/.ssh/config
Host production
HostName 203.0.113.10
User deployuser
Port 2222
IdentityFile ~/.ssh/prod_ed25519
Host staging
HostName 203.0.113.20
User deployuser
Port 22
IdentityFile ~/.ssh/staging_ed25519
Set correct permissions on the config file:
chmod 600 ~/.ssh/config
What You’ve Achieved
Done. Your SSH server now requires a cryptographic key to get in. Brute-force password attacks simply don’t work anymore. Root login is blocked, which limits the damage if any user account does get compromised. Change the port too, and you’ll cut automated scanning noise by 90%+.
These three steps — dedicated non-root user, key-based auth, no password login — cover the core SSH attack surface. Add a firewall rule restricting SSH to known IP ranges when possible, and your server is in dramatically better shape than stock defaults.
One last thing: store your private key backup securely. Losing it without a backup means you’re locked out. A password manager with secure notes or an encrypted USB drive works well for this.

