7 Proven Ways to Harden SSH and Block Brute‑Force Attacks
A senior operations engineer shares seven battle‑tested techniques—including port masking, key‑based authentication, Fail2ban, IP whitelisting, connection limits, two‑factor authentication, and a honeypot—to dramatically reduce SSH brute‑force attacks and protect critical servers.
As a senior operations engineer, I have witnessed countless servers compromised due to weak SSH configurations; this guide presents seven proven hardening methods that can eliminate brute‑force threats.
How serious is the problem?
Each publicly exposed server receives 2000+ SSH login attempts per day.
90% of attacks target the default port 22.
Servers using weak passwords are typically breached within 30 minutes .
Real‑world case: An e‑commerce company suffered a breach of over 100,000 user records after attackers cracked SSH credentials, incurring losses exceeding $5 million.
Method 1: Change the default SSH port
Risk rating: ⭐⭐⭐⭐⭐ Difficulty: ⭐
# Edit SSH configuration
sudo vim /etc/ssh/sshd_config
# Change port (choose a value between 1024‑65535)
Port 2022
# Restart SSH service
sudo systemctl restart sshdWhy it works:
99% of automated attack tools only scan the default port 22.
This single change blocks more than 95% of brute‑force attempts.
Tip: Avoid common service ports (e.g., 80, 443, 3306) and prefer a four‑digit port.
Method 2: Disable password login and enable key authentication
Risk rating: ⭐⭐⭐⭐⭐ Difficulty: ⭐⭐
Generate an SSH key pair
# Generate a 4096‑bit RSA key pair
ssh-keygen -t rsa -b 4096 -C "[email protected]"
# Copy the public key to the server (replace 2022 with your custom port)
ssh-copy-id -p 2022 user@server_ipConfigure the server to reject password authentication
# Edit SSH configuration
sudo vim /etc/ssh/sshd_config
# Disable password authentication
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Restart service
sudo systemctl restart sshdSwitching from a 10⁸‑size password space to a 2048‑bit RSA key (2²⁰⁴⁸ possibilities) makes brute‑force practically impossible.
Method 3: Deploy Fail2ban for dynamic protection
Risk rating: ⭐⭐⭐⭐ Difficulty: ⭐⭐⭐
# Ubuntu/Debian
sudo apt install fail2ban
# CentOS/RHEL
sudo yum install fail2ban # Create custom jail configuration
sudo vim /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2022
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600After configuration, any IP that fails to log in three times within ten minutes is automatically banned for one hour.
Method 4: Set up an SSH whitelist (IP allowlist)
Risk rating: ⭐⭐⭐ Difficulty: ⭐⭐
# Flush existing rules (use with caution)
sudo iptables -F
# Allow loopback traffic
sudo iptables -A INPUT -i lo -j ACCEPT
# Allow established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Permit only a specific subnet to access SSH on the custom port
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 2022 -j ACCEPT
# Drop all other SSH traffic
sudo iptables -A INPUT -p tcp --dport 2022 -j DROP
# Save rules
sudo iptables-save > /etc/iptables/rules.v4Ideal for fixed office networks or VPN‑based access.
Method 5: Enable two‑factor authentication (2FA) for SSH
Risk rating: ⭐⭐⭐⭐⭐ Difficulty: ⭐⭐⭐⭐
# Ubuntu/Debian
sudo apt install libpam-google-authenticator
# CentOS/RHEL
sudo yum install google-authenticator # Run the configuration wizard
google-authenticator
# Edit PAM configuration for SSH
sudo vim /etc/pam.d/sshd
# Add the following line
auth required pam_google_authenticator.so # Enable challenge‑response authentication in sshd_config
sudo vim /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
# Restart service
sudo systemctl restart sshdUsers must now provide a time‑based six‑digit code from a mobile app, adding a strong extra layer of protection.
Method 6: Restrict SSH connection parameters
Risk rating: ⭐⭐⭐ Difficulty: ⭐⭐
# Edit sshd_config
sudo vim /etc/ssh/sshd_config
# Limit simultaneous sessions and unauthenticated connections
MaxSessions 3
MaxStartups 3:30:10
# Set idle timeout and login grace period
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
# Restrict allowed users
AllowUsers admin operator
DenyUsers root guest
# Restart service
sudo systemctl restart sshd MaxSessions: maximum sessions per connection. MaxStartups: max unauthenticated concurrent connections. ClientAliveInterval: server heartbeat interval. LoginGraceTime: maximum time allowed for user login.
Method 7: Deploy an SSH honeypot (Cowrie) for monitoring
Risk rating: ⭐⭐ Difficulty: ⭐⭐⭐⭐
# Clone Cowrie repository
git clone https://github.com/cowrie/cowrie.git
cd cowrie
# Install dependencies
pip install -r requirements.txt
# Copy default config and edit
cp etc/cowrie.cfg.dist etc/cowrie.cfg
vim etc/cowrie.cfg
# Example SSH listener configuration
[ssh]
listen_endpoints = tcp:2222:interface=0.0.0.0 #!/bin/bash
# Real‑time monitoring of SSH login attempts
tail -f /var/log/auth.log | while read line; do
if echo "$line" | grep -q "Failed password"; then
ip=$(echo "$line" | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
echo "$(date): Detected brute‑force attempt from $ip" >> /var/log/ssh_attacks.log
# Optional: send alert to webhook
# curl -X POST "https://your-webhook-url" -d "{'text':'SSH attack from $ip'}"
fi
doneUltimate SSH Security Configuration Template
# /etc/ssh/sshd_config – Best‑practice settings
Port 2022
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
# Authentication
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no
AuthenticationMethods publickey
# Connection limits
MaxAuthTries 3
MaxSessions 3
MaxStartups 3:30:10
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
# User restrictions
AllowUsers admin operator
DenyUsers root guest
# Additional hardening
X11Forwarding no
AllowTcpForwarding no
GatewayPorts no
PermitTunnel no
Banner /etc/ssh/banner.txtBest‑Practice Recommendations
Immediately change the default SSH port – takes ~5 minutes.
Disable root login – eliminates ~99% of risk.
Enable key‑based authentication – fully mitigates password‑based brute force.
Deploy Fail2ban – automated blocking of repeated failures.
Configure an IP whitelist – restricts access to trusted sources.
Set connection limits – prevents resource exhaustion.
Implement 2FA – provides financial‑grade security.
Common Pitfalls and Solutions
Forgot to open the new port in the firewall before changing it – always add firewall rules first.
Lost private keys without backups – keep copies in multiple secure locations or retain an emergency password‑login account.
Fail2ban mistakenly bans your own IP – configure a whitelist for administrator addresses.
Final Advice
SSH hardening is an ongoing process; regularly review configurations, monitor logs, and update defenses. Newcomers should start with the first three methods, while experienced engineers can adopt the full suite for enterprise‑grade protection.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
