⚠️ Test your new SSH config in a second terminal before closing your current session. If you lock yourself out, you need console access to fix it.
The Hardened Config
Drop this into /etc/ssh/sshd_config (back up the original first with cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak):
Port 47892
PermitRootLogin no
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
UsePAM yes
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
MaxAuthTries 3
LoginGraceTime 20
ClientAliveInterval 300
ClientAliveCountMax 2
Then: sudo systemctl restart sshd. That's it. Your server is now rejecting passwords, root logins, and most automated attacks.
The rest of this page explains what each directive does and walks through key setup if you haven't done that yet.
Failed password for root from 218.92.0.107 port 53210 ssh2
Failed password for invalid user admin from 185.224.128.43 port 42180 ssh2
Failed password for root from 103.145.13.41 port 55764 ssh2
Failed password for invalid user test from 45.136.14.72 port 38920 ssh2
...repeated 11,433 more times
What Each Line Does
One line at a time:
Port 47892— Moves SSH off port 22. Kills 99% of automated scanners. Not real security, but your logs get a lot quieter.PermitRootLogin no— Nobody logs in as root over SSH. Use a normal user and sudo.PasswordAuthentication no— Keys only. The single most important line in this file.PermitEmptyPasswords no— Belt and suspenders. Should already be the default.ChallengeResponseAuthentication no— Closes a backdoor that lets password auth sneak back in through PAM.PubkeyAuthentication yes— Enables key-based login. Usually on by default, but be explicit.AuthenticationMethods publickey— Only allow public key. No keyboard-interactive, no GSSAPI, nothing else.UsePAM yes— Keep PAM on for account/session management, just not for authentication.X11Forwarding no— Unless you're forwarding GUI apps (you're probably not), turn it off.AllowTcpForwarding no— Prevents your server from being used as a tunnel. Enable selectively if you need it.AllowAgentForwarding no— Same idea. Don't let agent forwarding be abused by a compromised server.MaxAuthTries 3— Three failed attempts, then disconnect. Slows down brute-force scripts.LoginGraceTime 20— 20 seconds to authenticate or get dropped. Default is 120, which is generous to attackers.ClientAliveInterval 300— Ping idle clients every 5 minutes.ClientAliveCountMax 2— Two missed pings and you're disconnected. Cleans up ghost sessions.
Now, if you don't already have SSH keys set up, here's how.
Generating an SSH Key
Run this on your local machine, not the server:
# Modern systems - use Ed25519 (faster and more secure)
ssh-keygen -t ed25519 -C "anurag@mymachine"
# Older systems that don't support Ed25519
ssh-keygen -t rsa -b 4096 -C "anurag@mymachine"
Set a passphrase when prompted. If your laptop gets stolen, the passphrase is the only thing between the thief and your servers. I keep mine in a password manager.
You get two files:
~/.ssh/id_ed25519— Your private key. Never share this. Never copy it to servers.~/.ssh/id_ed25519.pub— Your public key. This goes on servers you want to access.
Copying Your Key to the Server
Easiest method (while password auth is still enabled):
ssh-copy-id [email protected]
Appends your public key to ~/.ssh/authorized_keys on the server.
Manual method if ssh-copy-id isn't available:
# Display your public key
cat ~/.ssh/id_ed25519.pub
# Copy the output, then on the server:
mkdir -p ~/.ssh
echo "paste-your-public-key-here" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
💡 Keys not working? If key auth isn't working, check permissions first: ~/.ssh should be 700, authorized_keys should be 600. SSH is picky about this.
SSH refuses to use keys if permissions are too open. The chmod commands aren't optional.
Test Before You Disable Passwords
Do not skip this. Open a new terminal and confirm key-based login works:
ssh [email protected]
If it asks for your key passphrase (not your server password), you're good. If it asks for the server password, something is broken — fix it before touching sshd_config.
Apply the Config
If you already copied the full config from the top of this page, you're done with this step. If you're making changes incrementally instead, here are the critical lines:
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
Restart SSH to apply:
sudo systemctl restart sshd
Open a second terminal immediately and test login. Keep the first session alive until you've confirmed the new config works.
Disable Root Login
"root" is the first username every bot tries. Kill it. Make sure you have a sudo-capable user first:
adduser anurag
usermod -aG sudo anurag
Already in the config from above: PermitRootLogin no. Restart sshd, confirm you can still log in as your regular user.
Change the Port
People will tell you this is "security through obscurity" and therefore useless. They're half right — a port scan finds it in seconds. But the bots doing bulk password spraying across the entire IPv4 range only check port 22. Moving off it drops your log noise by 99%. That's worth the 30 seconds it takes.
The config above uses 47892. Pick any high port. Avoid 2222, 8022, 22222 — some scanners check those.
Open the port in your firewall before restarting sshd or you'll lock yourself out:
# For ufw
sudo ufw allow 47892/tcp
# For firewalld
sudo firewall-cmd --permanent --add-port=47892/tcp
sudo firewall-cmd --reload
Restart sshd, then connect with the new port:
ssh -p 47892 [email protected]
Save yourself from typing -p 47892 every time — add it to your local SSH config:
Host myserver
HostName yourserver.com
User anurag
Port 47892
Now ssh myserver does the right thing.
Fail2Ban
Honest opinion: fail2ban is a band-aid. The real fix is key-only auth + non-standard port + firewall rules. fail2ban just makes the logs quieter. That said, I still install it everywhere because quieter logs are easier to read when something actually goes wrong.
sudo apt update && sudo apt install fail2ban
sudo yum install epel-release
sudo yum install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Works out of the box for SSH. Customize it by creating a local config (the main config gets overwritten on updates):
[DEFAULT]
# Ban for 1 hour
bantime = 3600
# An IP gets banned if it fails 5 times...
maxretry = 5
# ...within a 10-minute window
findtime = 600
# Ignore my home IP so I don't lock myself out
ignoreip = 127.0.0.1/8 ::1 YOUR.HOME.IP.HERE
[sshd]
enabled = true
port = ssh,47892
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600— Block for 1 hour. Some people go permanent. I don't — rotating IPs make that pointless.maxretry = 3— Three strikes for SSH. Legit users don't fat-finger three times in a row.findtime = 600— Counting window. 3 failures in 10 minutes = banned.ignoreip— Your home/office IP goes here so you don't ban yourself when you mistype your passphrase.port— Include your custom SSH port if you changed it.
Restart Fail2Ban:
sudo systemctl restart fail2ban
Checking Fail2Ban Status
# Check status of SSH jail
sudo fail2ban-client status sshd
# See currently banned IPs
sudo fail2ban-client get sshd banned
# Unban an IP (if you accidentally banned yourself)
sudo fail2ban-client unban YOUR.IP.HERE
Give it a few days, then check. You'll see hundreds of banned IPs. All automated junk that never had a chance anyway, since you disabled password auth. Like I said — band-aid. But a tidy one.
Extra Hardening
Stuff I do on servers that matter more than average:
Limit Who Can SSH
Whitelist who can SSH in:
AllowUsers anurag jane
Everyone else gets rejected before authentication even starts.
Use Two-Factor Authentication
Google Authenticator or Duo can require a key AND a TOTP code. That's a separate tutorial — search "SSH Google Authenticator PAM" if you want it.
Set Up Login Notifications
Simple script that emails me on every login:
#!/bin/bash
IP=$(echo $SSH_CONNECTION | awk '{print $1}')
HOSTNAME=$(hostname)
USER=$USER
DATE=$(date)
echo "SSH login: $USER from $IP on $HOSTNAME at $DATE" | mail -s "SSH Alert: $HOSTNAME" [email protected]
Requires a working mail setup on the server. Worth it for the peace of mind — you'll know instantly if someone else gets in.
Verify It Worked
Run this and confirm the output matches what you expect:
sshd -T | grep -E 'permitrootlogin|passwordauthentication|port'
You should see:
port 47892
passwordauthentication no
permitrootlogin no
If the output doesn't match, your config has a syntax error or there's an override in a Match block somewhere below your settings. sshd -T shows the effective config after all Match blocks are resolved — it's the only way to know what sshd is actually running with. Don't trust the file alone.
💬 Comments