Every publicly accessible Linux server on the internet is subject to an unrelenting barrage of automated brute-force attacks. Within minutes of spinning up a fresh VPS, port 22 is probed by botnets attempting default credentials, dictionary wordlists, and known exploit payloads.
Leaving default SSH configurations in place is an invitation for server compromise. Securing your shell access is the single most critical defensive measure in server administration. In this guide, we walk through an enterprise-grade SSH hardening blueprint: adopting high-security Ed25519 cryptographic keys, locking down sshd_config, deploying Fail2ban for automated IP ban policies, and enforcing Two-Factor Authentication (2FA/MFA).
1. Transitioning to Modern Ed25519 Cryptographic Keys
For years, RSA 2048 or 4096-bit keys were standard. Today, Ed25519 (Edwards-curve Digital Signature Algorithm) is the gold standard for SSH authentication. Ed25519 keys are faster, mathematically superior, resistant to side-channel attacks, and produce much shorter signatures (68 characters vs. over 500 characters for RSA).
Generate a new Ed25519 keypair on your local machine with key derivation function (KDF) rounds for enhanced passphrase protection:
# Generate a hardened Ed25519 key with 100 KDF rounds
ssh-keygen -t ed25519 -a 100 -C "[email protected]" -f ~/.ssh/id_ed25519_production
Copy the public key to your target server:
ssh-copy-id -i ~/.ssh/id_ed25519_production.pub username@your-server-ip
Verify that you can authenticate using the new key before touching the server configuration:
ssh -i ~/.ssh/id_ed25519_production username@your-server-ip
2. Production-Grade sshd_config Hardening
Open the SSH daemon configuration file on your server (/etc/ssh/sshd_config or modular drop-in file under /etc/ssh/sshd_config.d/99-hardened.conf):
sudo nano /etc/ssh/sshd_config.d/99-hardened.conf
Add or verify the following critical security directives:
# Enforce modern protocol version
Protocol 2
# Disable root login over SSH
PermitRootLogin no
# Strictly require public key authentication
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
# Disable challenge-response password authentication
KbdInteractiveAuthentication no
# Reduce maximum authentication attempts before disconnect
MaxAuthTries 3
# Terminate stale or idle connections quickly
ClientAliveInterval 300
ClientAliveCountMax 2
# Disable insecure X11 and Agent forwarding unless explicitly needed
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
# Restrict allowed key exchange, cipher, and MAC algorithms
KexAlgorithms curve25519-sha256,[email protected],diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected]
sudo sshd -t before restarting the daemon. Keep your existing SSH terminal session open while testing a new connection in a separate window.
Validate the syntax and reload SSH daemon:
sudo sshd -t && sudo systemctl reload sshd
3. Automated IP Banning with Fail2Ban
Fail2Ban continuously inspects authentication logs (/var/log/auth.log) and automatically inserts temporary firewall rules (via nftables or iptables) to drop traffic from IPs that exhibit repeated failed login attempts.
Installing and Configuring Fail2Ban
sudo apt update && sudo apt install -y fail2ban
Create a localized jail override at /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
backend = systemd
[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 3
bantime = 24h
findtime = 15m
Start and enable Fail2Ban:
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
You can inspect active banned IPs at any time:
sudo fail2ban-client get sshd banned
4. Enforcing Two-Factor Authentication (MFA / 2FA)
For critical bastion hosts and production servers, combining public key authentication with Time-based One-Time Passwords (TOTP) ensures that even a stolen private key is insufficient without the physical authenticator token.
Step 1: Install the PAM Google Authenticator Module
sudo apt install -y libpam-google-authenticator
Step 2: Generate TOTP Secrets for your User
Run the initialization wizard under your standard user account:
google-authenticator -t -d -f -r 3 -R 30 -W
Scan the generated QR code into your authenticator app (e.g., Aegis, Bitwarden Authenticator, or Google Authenticator) and store the emergency scratch codes safely in an encrypted password vault.
Step 3: Configure PAM and SSHD
Edit /etc/pam.d/sshd and append:
auth required pam_google_authenticator.so nullok
In /etc/ssh/sshd_config.d/99-hardened.conf, enable interactive authentication specifically for PAM while keeping passwords disabled:
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
UsePAM yes
Reload the service:
sudo sshd -t && sudo systemctl reload sshd
5. Firewall Rate Limiting & Monitoring Summary
Complement your SSH hardening with local firewall rate-limiting using UFW:
# Deny connections from IPs making more than 6 attempts in 30 seconds
sudo ufw limit ssh
sudo ufw enable
Security Checklist
- Ed25519 Only: Deprecate short RSA or DSA keys.
- No Passwords:
PasswordAuthentication noprevents brute force. - No Direct Root: Always authenticate via an unprivileged
sudouser. - Active Jail: Fail2ban automatically suppresses automated network scanners.
- MFA Defense: Dual-layer authentication protects infrastructure even in worst-case workstation compromise.