Why SSH Hardening Matters
Every day, automated bots scan the internet for servers with default SSH configurations. They try common usernames likerootandadmin, cycling through password lists until they break in. Once inside, attackers can steal data, install malware, or use your server for further attacks.
The good news? Most SSH vulnerabilities stem from lazy defaults, not sophisticated exploits. A few configuration tweaks can dramatically improve your security posture.
Step 1: Disable Root Login
Why it matters: Therootaccount is every attacker's first target. If they guess your root password, they have complete control over your system.
The fix: Force attackers to know both a valid username AND its password, then escalate privileges separately.
Commands:
# Open the SSH configuration file
sudo nano /etc/ssh/sshd_config
# Find and modify this line (or add it if missing):
PermitRootLogin no
After making changes, restart SSH:
sudo systemctl restart sshd
Before applying this: Make sure you have another user account with sudo privileges, or you'll lock yourself out.
# Create a new user
sudo adduser yourusername
# Grant sudo privileges
sudo usermod -aG sudo yourusername
Step 2: Change the Default SSH Port
Why it matters: Port 22 is SSH's default, and bots scan it constantly. Changing ports won't stop determined attackers, but it dramatically reduces automated attacks.
The fix: Use a non-standard port above 1024.
Commands:
# Edit SSH config
sudo nano /etc/ssh/sshd_config
# Find and modify the Port line:
Port 2849
Update your firewall:
# For UFW (Ubuntu/Debian)
sudo ufw allow 2849/tcp
sudo ufw delete allow 22/tcp
# For firewalld (CentOS/RHEL)
sudo firewall-cmd --permanent --add-port=2849/tcp
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
Restart SSH:
sudo systemctl restart sshd
Connect using the new port:
ssh -p 2849 username@your-server-ip
Step 3: Use SSH Key Authentication (Disable Passwords)
Why it matters: Passwords can be guessed or brute-forced. SSH keys are cryptographic — essentially unbreakable with current technology.
The fix: Generate key pairs and disable password authentication entirely.
Commands:
On your local machine (not the server):
# Generate an SSH key pair (use Ed25519 for better security)
ssh-keygen -t ed25519 -C "[email protected]"
# Or use RSA if Ed25519 isn't supported
ssh-keygen -t rsa -b 4096 -C "[email protected]"
Press Enter to save to the default location (~/.ssh/id_ed25519). Set a strong passphrase when prompted.
Copy your public key to the server:
ssh-copy-id -p 2849 username@your-server-ip
# Or manually:
cat ~/.ssh/id_ed25519.pub | ssh -p 2849 username@your-server-ip "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Test key authentication:
ssh -p 2849 username@your-server-ip
You should log in without entering your server password (though you'll need your key passphrase).
Disable password authentication on the server:
sudo nano /etc/ssh/sshd_config
# Set these values:
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
Restart SSH:
sudo systemctl restart sshd
Critical: Test your key login in a separate terminal before closing your current session. If something goes wrong, you'll still have access to fix it.
Step 4: Limit User Access with AllowUsers
Why it matters: Even with strong authentication, limiting WHO can connect adds another layer of defense.
The fix: Explicitly whitelist users who need SSH access.
Commands:
sudo nano /etc/ssh/sshd_config
# Add this line at the end:
AllowUsers yourusername [email protected]
This allows:
yourusernamefrom any IPadminonly from IP192.168.1.100
Restart SSH:
sudo systemctl restart sshd
Step 5: Set Up Fail2Ban to Block Brute-Force Attacks
Why it matters: Even with hardened SSH, attackers will try. Fail2Ban monitors logs and temporarily bans IPs after repeated failed login attempts.
The fix: Install and configure Fail2Ban.
Commands:
Install Fail2Ban:
# Ubuntu/Debian
sudo apt update && sudo apt install fail2ban
# CentOS/RHEL
sudo yum install epel-release
sudo yum install fail2ban
Configure Fail2Ban:
# Create a local configuration file
sudo nano /etc/fail2ban/jail.local
Add this configuration:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = 2849
logpath = /var/log/auth.log
backend = systemd
What these settings mean:
bantime: Ban for 1 hour (3600 seconds)findtime: Track failures within 10 minutes (600 seconds)maxretry: Ban after 5 failed attemptsport: Your custom SSH port
Start and enable Fail2Ban:
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
Check status:
sudo fail2ban-client status sshd
Step 6: Disable Empty Passwords and Unused Authentication Methods
Why it matters: Old SSH configurations might allow empty passwords or authentication methods you don't use, creating unnecessary attack vectors.
The fix: Explicitly disable these options.
Commands:
sudo nano /etc/ssh/sshd_config
# Add or modify these lines:
PermitEmptyPasswords no
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 20
What these do:
PermitEmptyPasswords no: Blocks accounts with no passwordX11Forwarding no: Disables graphical forwarding (rarely needed)MaxAuthTries 3: Limits authentication attempts per connectionLoginGraceTime 20: Disconnect if login isn't completed within 20 seconds
Restart SSH:
sudo systemctl restart sshd
Step 7: Enable Two-Factor Authentication (2FA)
Why it matters: Even if someone steals your SSH key, 2FA requires a second verification code from your phone.
The fix: Use Google Authenticator for SSH.
Commands:
Install Google Authenticator:
# Ubuntu/Debian
sudo apt install libpam-google-authenticator
# CentOS/RHEL
sudo yum install google-authenticator
Set up 2FA for your user:
google-authenticator
Answer the prompts:
Time-based tokens? Yes
Update .google_authenticator file? Yes
Disallow multiple uses? Yes
Increase time skew? No
Rate limiting? Yes
Scan the QR code with Google Authenticator, Authy, or 1Password on your phone.
Configure SSH to use 2FA:
sudo nano /etc/pam.d/sshd
# Add this line at the top:
auth required pam_google_authenticator.so
Enable ChallengeResponseAuthentication:
sudo nano /etc/ssh/sshd_config
# Set:
ChallengeResponseAuthentication yes
Restart SSH:
sudo systemctl restart sshd
Test 2FA: When you log in, you'll be prompted for your 6-digit code after your SSH key.
Bonus: Review Your Complete SSH Configuration
After implementing all steps, your/etc/ssh/sshd_configshould include:
Port 2849
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication yes
UsePAM yes
PermitEmptyPasswords no
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 20
AllowUsers yourusername
Verify configuration syntax:
sudo sshd -t
If no errors appear, restart SSH:
sudo systemctl restart sshd
Testing Your Hardened SSH
From another terminal:
# Successful connection
ssh -p 2849 yourusername@your-server-ip
# Test that root is blocked
ssh -p 2849 root@your-server-ip
# Should fail
# Test that unauthorized users are blocked
ssh -p 2849 randomuser@your-server-ip
# Should fail
Monitoring and Maintenance
Check SSH logs regularly:
# View recent SSH attempts
sudo tail -f /var/log/auth.log
# Check Fail2Ban bans
sudo fail2ban-client status sshd
Update your server regularly:
sudo apt update && sudo apt upgrade -y
Final Thoughts
SSH hardening isn't paranoia — it's basic hygiene. These 7 steps transform your server from an easy target to a hardened system that repels automated attacks and makes manual intrusion significantly harder.
Remember:
Always test changes in a separate session before closing your current connection
Keep backups of your SSH keys
Store your 2FA backup codes somewhere safe
Review logs periodically for suspicious activity
Your server's security is only as strong as its weakest entry point. Don't let that be SSH.
Default shouldn't mean defenseless. Stay secure.