Quick Answer: 1) Set up SSH keys and disable password auth. 2) Disable root login. 3) Change SSH port from 22. 4) Install Fail2ban. These four steps block 99% of SSH attacks.
The Threat
A fresh server on the internet gets SSH brute-force attempts within minutes. Bots scan every IP on the internet trying common username/password combinations. Check your own server:
grep "Failed password" /var/log/auth.log | wc -l
If you see hundreds or thousands — that is normal. Every server gets this. The goal is to make these attempts useless.
Step 1: Use SSH Keys (Disable Passwords)
This is the single most important step. See our SSH Keys Setup Guide for the full walkthrough.
After keys are working, disable password authentication:
sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
sudo systemctl restart sshd
Test from a new terminal before closing your current session.
Step 2: Disable Root Login
PermitRootLogin no
If you need root access, SSH as a normal user and use sudo.
If you must allow root with key only:
PermitRootLogin prohibit-password
Step 3: Change the SSH Port
Port 22 is scanned by every bot. Changing it eliminates 99% of automated attacks:
Port 2222
Pick any port between 1024-65535. Common choices: 2222, 2200, 8022, 22222.
Before restarting SSH, open the new port in your firewall:
# UFW
sudo ufw allow 2222/tcp
# iptables
sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
Then restart:
sudo systemctl restart sshd
Connect with: ssh -p 2222 user@server
Step 4: Install Fail2ban
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban
See our Fail2ban Setup Guide for custom jails and configuration.
Step 5: Restrict Users
Only allow specific users to SSH:
AllowUsers sam admin
Or allow a group:
AllowGroups ssh-users
# Create the group and add users
sudo groupadd ssh-users
sudo usermod -aG ssh-users sam
Step 6: Connection Limits
# Disconnect idle sessions after 5 minutes
ClientAliveInterval 300
ClientAliveCountMax 2
# Max authentication attempts per connection
MaxAuthTries 3
# Max concurrent unauthenticated connections
MaxStartups 10:30:60
# Limit sessions per user
MaxSessions 3
Step 7: Disable Unnecessary Features
# Disable X11 forwarding (GUI over SSH)
X11Forwarding no
# Disable TCP forwarding (if not needed for tunnels)
AllowTcpForwarding no
# Disable agent forwarding
AllowAgentForwarding no
# Disable empty passwords
PermitEmptyPasswords no
# Protocol 2 is the only option since OpenSSH 7.6+ (Protocol 1 removed)
Complete sshd_config Template
# /etc/ssh/sshd_config — Hardened configuration
Port 2222
# Authentication
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
# Restrict users
AllowUsers sam
# Session limits
ClientAliveInterval 300
ClientAliveCountMax 2
MaxSessions 3
MaxStartups 10:30:60
# Disable unnecessary features
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PrintMotd no
# Logging
LogLevel VERBOSE
Apply:
# Test config first
sudo sshd -t
# If no errors, restart
sudo systemctl restart sshd
Step 8: Monitor SSH Access
# See who's logged in now
who
w
# Recent successful logins
last | head -20
# Recent failed logins
lastb | head -20
# Failed attempts by IP
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10
# Successful key logins
grep "Accepted publickey" /var/log/auth.log | tail -10
Quick Security Checklist
# Run this to check your SSH security
echo "=== SSH Security Check ==="
echo "Port: $(grep "^Port" /etc/ssh/sshd_config || echo '22 (default)')"
echo "Root login: $(grep "^PermitRootLogin" /etc/ssh/sshd_config || echo 'not set')"
echo "Password auth: $(grep "^PasswordAuthentication" /etc/ssh/sshd_config || echo 'not set')"
echo "Fail2ban: $(systemctl is-active fail2ban 2>/dev/null || echo 'not installed')"
echo "Failed logins today: $(grep "Failed password" /var/log/auth.log | grep "$(date +%b\ %d)" | wc -l)"