Tools: SSH Hardening — The Ultimate Guide for 2026
SSH Hardening — The Ultimate Guide for 2026
Prerequisites
1. Switch to Key-Based SSH Authentication
Generate a Key Pair (on your local machine)
Copy the Public Key to Your Server
Test Key-Based Login
2. Harden the SSH Configuration
Disable Password Authentication
Disable Root Login
Change the Default Port
Disable Empty Passwords
Limit Authentication Attempts
Set a Login Grace Time
Disable X11 Forwarding
Disable TCP Forwarding (if not needed)
Restrict SSH to Specific Users
Use Strong Ciphers and Key Exchange Algorithms
3. Complete Hardened sshd_config
4. Set Up Fail2Ban for SSH
5. Add Two-Factor Authentication (Optional)
6. Monitor SSH Access
Check Active Sessions
Review Recent Logins
Watch Failed Attempts in Real Time
Set Up Login Notifications
Security Considerations
Troubleshooting
Conclusion If your server has a public IP, it's getting SSH brute-force attempts right now. Not maybe. Not eventually. Right now. Check your auth log: You'll see hundreds — sometimes thousands — of failed login attempts from IPs you've never seen. Botnets scan the entire IPv4 space and hammer port 22 with common username/password combinations 24/7. My basic VPS hardening guide covers the essentials. This SSH hardening guide goes deeper — every sshd_config setting that matters, key-based authentication, two-factor auth, and monitoring. By the end, your SSH setup will be hardened against everything from automated bots to targeted attacks. Warning: Always keep a second SSH session open when modifying SSH config. If you make a mistake, the existing session stays connected. Test your new config in a new connection before closing the old one. Password authentication is the weakest link. Even a strong password can be brute-forced given enough time. SSH keys are cryptographically stronger and immune to dictionary attacks. Ed25519 is the modern default — faster and more secure than RSA. If you need RSA compatibility (some older systems), use: You'll be prompted for a passphrase. Use one. The passphrase encrypts the private key on disk. If someone steals your key file, the passphrase is the last line of defense. Open a new terminal (keep the old one open) and test: If it logs in without asking for a password (or asks for your key passphrase instead), key auth is working. The main SSH server config lives at /etc/ssh/sshd_config. Every change below goes in this file. After editing, restart SSH to apply: Once key-based auth works, disable passwords entirely: This single change eliminates brute-force password attacks completely. Bots can hammer port 22 all day — without the private key, they're not getting in. Even with key auth, there's no reason to allow direct root login. Use a regular user and sudo for privilege escalation. This adds a layer — an attacker needs both the SSH key and knowledge of which user has sudo access. Changing from port 22 won't stop a determined attacker, but it eliminates 99% of automated bot traffic. Most botnets only scan port 22. This is security through obscurity — not a defense by itself, but it reduces noise in your logs dramatically. After changing the port, update your firewall: This should already be the default, but explicitly set it. After 3 failed attempts, the connection is dropped. Combined with Fail2Ban, this makes brute-force attacks impractical. The server waits 30 seconds for authentication before disconnecting. The default is 120 seconds — that's too generous. Reduce it to limit resource consumption from idle connections. Unless you're running graphical applications over SSH (unlikely on a server), disable this. It's an unnecessary attack surface. If you don't use SSH tunnels, disable forwarding. If you use SSH tunnels for things like database access, leave it enabled or restrict to specific users. This is a whitelist. Only the listed users can log in via SSH. Everyone else is rejected before authentication even starts. This disables weak algorithms (3DES, SHA1, diffie-hellman-group1). Modern clients support all of these. If you have very old clients that can't connect after this change, they need upgrading — not your server weakening its crypto. Here's the full set of changes in one block. Add or modify these lines in /etc/ssh/sshd_config: Validate the config before restarting: If there are no errors, restart: Test in a new terminal before closing your current session. Fail2Ban monitors your auth logs and temporarily bans IPs that fail authentication too many times. Even with key-based auth, it reduces log noise and blocks scanners. Create a local config (don't edit the main config — it gets overwritten on updates): This bans an IP for 1 hour after 3 failed attempts within 10 minutes. Start and enable Fail2Ban: You'll see the number of currently banned IPs and total bans since startup. For maximum security, add TOTP (Time-based One-Time Password) as a second factor. You'll need both your SSH key and a code from your authenticator app. Install the Google Authenticator PAM module: Run the setup as your regular user (not root): Scan the QR code with your authenticator app (Google Authenticator, Authy, or any TOTP app). Save the emergency codes in a secure location. Configure PAM. Edit /etc/pam.d/sshd: Update sshd_config to require both key and TOTP: Now login requires: SSH key → TOTP code. Two factors, both under your control. Warning: If you enable 2FA, make sure you have your emergency codes saved somewhere physically secure. Losing access to your authenticator app AND your emergency codes means you're locked out. Hardening is only half the job. You also need to know who's connecting and when. Add this to /etc/profile.d/ssh-notify.sh to get notified on every successful login: This sends you an email every time someone logs in via SSH. If you see a login you don't recognize, investigate immediately. Then connect with ssh byteguard. Problem: Locked out after disabling password auth.
Cause: Key-based auth wasn't properly configured before disabling passwords.Fix: Access your server through your hosting provider's console (Hetzner has a web console). Re-enable PasswordAuthentication yes, restart SSH, and fix your key setup. Problem: Connection refused after changing the port.Cause: Firewall doesn't allow the new port, or you forgot to update the SSH config.Fix: Connect via console, check ufw status, and ensure the new port is allowed. Verify sshd_config has the right port. Problem: "Too many authentication failures" error.Cause: SSH agent is offering multiple keys before the right one.Fix: Specify the key explicitly: ssh -i ~/.ssh/id_ed25519 -p 2222 user@server. Or set IdentitiesOnly yes in your SSH client config. Problem: 2FA prompt doesn't appear.Cause: PAM module not loaded or sshd_config not updated.Fix: Verify auth required pam_google_authenticator.so is in /etc/pam.d/sshd. Verify ChallengeResponseAuthentication yes and AuthenticationMethods publickey,keyboard-interactive in sshd_config. Restart SSH. Problem: Fail2Ban not banning IPs.Cause: Wrong log path or port in jail config.
Fix: Check sudo fail2ban-client status sshd for errors. Verify logpath matches your system (/var/log/auth.log on Ubuntu/Debian). Verify the port matches your custom SSH port. SSH is the front door to your server. Every hardening step here stacks — key-based auth eliminates password attacks, Fail2Ban blocks scanners, a custom port reduces noise, and 2FA adds a second factor even if your key is compromised. If you haven't done the basics yet, start with my VPS hardening guide — it covers UFW, unattended upgrades, and user setup alongside basic SSH config. For protecting other services, check out the Fail2Ban setup guide and Docker security best practices. Your server is only as secure as its weakest entry point. Make SSH a strong one. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse