How to Secure an SSH Server (Complete Hardening Guide)

3 min read
Intermediate SSH Security Hardening Linux

Prerequisites

  • SSH access to a Linux server
  • SSH key already set up (see SSH Keys Setup guide)

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)"

See Also