Your SSH Server is Being Attacked#SSH Server Hardening
— ny_wk
SSH server hardening means locking down OpenSSH so automated brute-force bots and opportunistic attackers cannot guess their way into your Linux box. The fastest wins are key-based authentication, disabling root login and passwords entirely, and rate-limiting connections — and you can apply every one of them in under an hour.
If you run any internet-facing Linux server, this is not optional. The moment a fresh host gets a public IP, automated scanners find port 22 and start hammering it. Tail your authentication log and you will watch failed root logins scroll past every minute, around the clock. The good news: OpenSSH is genuinely secure when configured correctly, and a handful of sshd_config directives shut down the vast majority of these attacks.
Why Your SSH Server Is Under Attack
SSH (Secure Shell) is the standard for remote login, remote command execution, and file transfer over scp and sftp. OpenSSH — the most widely deployed implementation — protects the confidentiality and integrity of everything that travels between two machines, and authenticates the server to the client using public-key cryptography. That strength is exactly why attackers target it: a single compromised SSH login often means full root control of the machine.
Run this on almost any public server and you will see the reality for yourself:
sudo journalctl -u ssh -xe | grep "Failed password"
Typical output looks like this — the same bot trying the root account again and again:
sshd[27389]: Failed password for invalid user root from 116.31.116.30 port 42803 ssh2
sshd[27389]: PAM 2 more authentication failures; rhost=116.31.116.30 user=root
Two clues matter here. "invalid user" means that account either does not exist or is not permitted to log in over SSH — a good sign your restrictions are working. A failure without "invalid user" means a real, allowed account got the password wrong, which deserves a closer look. The takeaway is simple: assume your SSH port is being probed constantly, and harden accordingly.
The Hardening Strategy in Plain Terms
Effective OpenSSH server hardening layers several independent defenses so that defeating one does not hand over the box:
- Authentication: use SSH keys, kill password and root login, forbid empty passwords.
- Access control: allow only named users or groups, and restrict source IPs at the firewall.
- Protocol & crypto: SSHv2 only, with modern ciphers, key-exchange (KEX), and MAC algorithms.
- Attack mitigation: rate-limit connections and run
fail2banto auto-ban repeat offenders. - Hygiene: idle timeouts, logging, banners, and keeping OpenSSH patched.
All server-side changes below live in one file: /etc/ssh/sshd_config (or a drop-in under /etc/ssh/sshd_config.d/ on modern distros). Edit it with sudo nano /etc/ssh/sshd_config, and after every change validate and reload — covered in the verification section so you do not lock yourself out.
Step-by-Step: Hardening sshd_config
Work through these in order. Crucially, set up key-based login and confirm it works before you disable passwords — otherwise you will be locked out the moment the session drops.
-
Set up key-based authentication. On your local machine, generate a modern key pair. Ed25519 is fast and secure and preferred over RSA today:
ssh-keygen -t ed25519 -C "you@workstation"Always give the private key a strong passphrase — never create a passphrase-free key for interactive use. Copy the public key to the server:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@serverThis appends your public key to
~/.ssh/authorized_keyson the server. Confirm you can log in with the key, then continue. -
Create an unprivileged login user. Never log in directly as root. Make a normal account that can escalate with
sudoinstead — this also gives you a clean audit trail of who ran what:sudo adduser deploy
sudo usermod -aG sudo deployInstall your key for this user and verify SSH login plus
sudo -iwork before locking root out. -
Disable root login. Find
#PermitRootLoginand set:PermitRootLogin noEvery Linux box has a
rootaccount, so it is the first target of every brute-force bot. Removing it as a login option forces an attacker to guess both a valid username and its credentials. -
Disable password authentication entirely. Once keys work, turn passwords off so brute-forcing becomes impossible:
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords noThis single step neutralizes the dictionary attacks filling your logs — there is no password to guess.
-
Limit who may connect. Restrict SSH to specific users or groups so service accounts (FTP, mail, web) created for other purposes can never log in interactively:
AllowUsers deploy aliceOr manage by group, which scales better:
AllowGroups sshusersUse the inverse —
DenyUsers/DenyGroups— only for blocking a few accounts. If none of these directives are set, the default is to allow everyone, so an explicit allow-list is the safer posture. -
Enforce SSH protocol 2 and modern crypto. SSHv1 had man-in-the-middle weaknesses and is long obsolete; current OpenSSH drops it by default, but pin strong algorithms explicitly:
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.comThese drop legacy CBC ciphers and weak MACs while keeping fast, authenticated-encryption options. Test against your client versions before rolling out widely.
-
Add idle-session timeouts. Automatically disconnect abandoned sessions so an unlocked terminal is not a free door:
ClientAliveInterval 300
ClientAliveCountMax 0This logs out a user after 5 minutes (300 seconds) of inactivity.
-
Disable legacy and unused features. Shrink the attack surface by turning off what you do not use:
IgnoreRhosts yes
HostbasedAuthentication no
X11Forwarding no
AllowTcpForwarding no
StrictModes yesLeave
AllowTcpForwardingon only if you genuinely tunnel through this host. -
Turn on useful logging and a banner. Verbose logs power your monitoring and intrusion tools; the last-login line helps you spot anything you do not recognize:
LogLevel VERBOSE
PrintLastLog yes
Banner /etc/issue.netA legal warning banner does not stop attackers, but it can matter for prosecution. Put your authorized-use notice in
/etc/issue.net; consult your legal team for the exact wording.
Firewall, Rate-Limiting, and fail2ban
Configuration locks down authentication; the firewall and active defenses cut off the noise before it ever reaches sshd.
Restrict SSH to known source IPs
If you always connect from a fixed IP (an office, a VPN, or a jump host with a static address), allow only that source. On a modern distro using firewalld or ufw this is one rule; the classic iptables form is:
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -s 203.0.113.10 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j DROP
The first rule keeps your current session alive; the second permits your IP; the third drops everyone else. A common pattern is a small, cheap VPS used purely as a bastion (jump host) — you SSH into it, then hop to the real server, which only accepts SSH from the bastion's fixed IP. Keep at least two bastions on different providers so you are not locked out if one disappears. Remember that plain iptables rules are not persistent across reboots, which is a useful escape hatch if you ever lock yourself out: rebooting from your provider's console resets them.
Rate-limit new connections
Throttle brute-force bursts directly in the firewall. This drops a source that opens more than four new connections to port 22 within 60 seconds:
sudo iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
sudo iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 -j DROP
Install fail2ban
fail2ban watches your auth log and temporarily bans any IP that racks up failed logins — the single highest-value addition after key auth. Install and enable it:
sudo apt install fail2ban (Debian/Ubuntu) or sudo dnf install fail2ban (RHEL/Fedora)
Create /etc/fail2ban/jail.local with an SSH jail:
[sshd]
enabled = true
maxretry = 4
bantime = 1h
findtime = 10m
Then sudo systemctl enable --now fail2ban and check status with sudo fail2ban-client status sshd. The actively maintained fail2ban has largely replaced older tools like DenyHosts.
Changing the SSH port: obscurity, not security
Moving SSH off port 22 (for example Port 2222) dramatically reduces log noise because most bots only scan the default port. Be honest about what it is, though: this is security through obscurity, not a real defense — a targeted attacker runs nmap and finds the new port in seconds. Treat a port change as noise reduction layered on top of key auth and fail2ban, never as a substitute. When you do change it, connect with ssh -p 2222 user@server and update the matching firewall rule.
Two-factor authentication
For high-value servers, add a second factor on top of keys with libpam-google-authenticator (TOTP) or a hardware security key (FIDO2 via ssh-keygen -t ed25519-sk). Even a stolen private key is then useless without the second factor.
Common Pitfalls That Lock You Out
Most SSH disasters are self-inflicted. Avoid these:
- Disabling passwords before keys work. Always confirm a successful key login first — in a separate session you keep open.
- Setting
AllowUserswithout including yourself. A typo in your username and the door shuts behind you. - A firewall rule that drops your own IP. Add the ESTABLISHED/RELATED accept rule first so the active session survives.
- Restarting
sshdwithout validating config. A syntax error can stop the daemon from starting. Always runsshd -tfirst. - No out-of-band access. Know where your provider's web console / KVM is before you start — it is the recovery path when SSH fails.
Verification: Test Before You Trust
Never assume a change took effect — verify it. First, check the syntax of your edited file before reloading:
sudo sshd -t
No output means the file is valid. To see the fully resolved, effective configuration the daemon will actually use (defaults plus your overrides plus any drop-ins), run:
sudo sshd -T
Grep it to confirm specific directives stuck:
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauthentication|allowusers'
Now apply the changes by reloading (not restarting) the service so existing sessions are not killed:
sudo systemctl reload ssh (Debian/Ubuntu) or sudo systemctl reload sshd (RHEL/Fedora)
The golden rule: keep your current SSH session open, then open a brand-new terminal and try to connect. If the new login succeeds, your changes are safe. If it fails, you still have the original session to fix things. Only close the first session once the new one is proven.
| Setting | Hardened value | Why |
PermitRootLogin | no | Removes the #1 brute-force target |
PasswordAuthentication | no | Defeats dictionary attacks outright |
PubkeyAuthentication | yes | Strong cryptographic login |
PermitEmptyPasswords | no | Blocks blank-password accounts |
AllowUsers / AllowGroups | your accounts | Explicit access allow-list |
X11Forwarding | no | Shrinks attack surface |
ClientAliveInterval | 300 | Drops idle sessions |
Keep It Patched
Hardening is not a one-time job. OpenSSH and its dependencies receive security fixes regularly, and old versions carry real, documented vulnerabilities — the kind of "there's an exploit in the wild" rumors that periodically sweep the sysadmin world almost always turn out to affect outdated builds, not current ones. Keep the package current with apt, dnf, or your distro's unattended-upgrades, and if a workstation does not need to accept incoming SSH at all, disable the server entirely with sudo systemctl disable --now ssh. The most secure service is the one that is not running.
Key Takeaways
- Keys over passwords: set up Ed25519 key auth, verify it, then set
PasswordAuthentication noto make brute-forcing impossible. - No root, named users only:
PermitRootLogin noplusAllowUsers/AllowGroupsforces attackers to guess both username and credential. - Layer active defenses:
fail2banplus firewall rate-limiting and source-IP restrictions stop attacks before authentication. - Port-change is cosmetic: it cuts log noise but is obscurity, not security — never rely on it alone.
- Always verify: run
sshd -tandsshd -T, reload, and test in a second session before closing your first.
Frequently Asked Questions
Is changing the SSH port from 22 actually worth it?
Yes for noise reduction, no as a real defense. Moving off port 22 hides you from the mass scanners that only probe the default port, so your logs get much quieter. But any targeted attacker finds the new port instantly with a scan, so treat it as a convenience layered on top of key authentication and fail2ban — never as your primary protection.
Should I disable password authentication completely?
Yes — once key-based login is set up and confirmed working. With PasswordAuthentication no there is no password for a bot to guess, which eliminates the dictionary and brute-force attacks that dominate SSH logs. Just be certain your key login succeeds in a fresh session before you flip this, or you will lock yourself out.
How do I check my hardening actually applied?
Run sudo sshd -t to validate syntax, then sudo sshd -T to print the fully effective configuration including defaults and drop-in files. Grep that output for the directives you changed. After reloading, open a new terminal and log in while keeping your original session open as a safety net.
Do I still need fail2ban if I only allow SSH keys?
It is strongly recommended. Key-only auth means brute-force attempts cannot succeed, but bots still flood your daemon with connection attempts, wasting resources and bloating logs. fail2ban bans those repeat offenders automatically, keeping the server quiet and reducing the chance an unrelated future weakness gets exploited.
Want more practical Linux and DevOps walkthroughs? Subscribe to @explorenystream on YouTube for hands-on server, security, and sysadmin tutorials.