SSH Hardening on Linux: 7 Essential Settings to Secure Remote Access

A Linux server with SSH on port 22 exposed to the internet starts receiving login attempts within minutes of being provisioned. This is no exaggeration: automated tools scan entire IP blocks around the clock, testing username and password combinations. The good news is that securing SSH doesn't require weeks of study — you just need to adjust half a dozen parameters in the configuration file.
The File That Controls Everything
All SSH server settings live in /etc/ssh/sshd_config. Before making any changes, create a backup:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bakAfter editing the file, validate the syntax before restarting the service — this way you won't risk locking yourself out of remote access:
sudo sshd -tNo output means valid configuration. Once the test passes, apply the changes:
sudo systemctl restart sshdGolden rule: always keep a second SSH session open while making changes. If something goes wrong, you can still revert without needing physical access to the machine.
The 7 Essential Settings
How do you disable direct root login via SSH?
Simply set PermitRootLogin no in sshd_config. This prevents any attacker from forcing their way in using the system's highest-privilege account.
Most automated attacks try the root user first because a successful entry means full control of the machine. With this directive disabled, the attacker must guess both the username and the credential — significantly raising the cost of the attack.
PermitRootLogin noFor administrative tasks, log in with a regular user and escalate privileges with sudo.
2. Replace Passwords with SSH Keys
Key-based authentication is technically superior to passwords: an ed25519 key has complexity equivalent to hundreds of random characters. On the client, generate the key pair:
ssh-keygen -t ed25519 -C "user@machine"Copy the public key to the server:
ssh-copy-id user@server-ipOnly after confirming that key-based login works, disable password authentication:
PasswordAuthentication no3. Restrict Which Users Can Log In
Even if an account exists on the system, you can prevent it from using SSH. The AllowUsers directive creates an explicit whitelist:
AllowUsers john mary deployAny account not listed — including service accounts created by automatically installed packages — is blocked by default. For larger teams, use AllowGroups sshusers and manage access via group membership.
4. Limit Authentication Attempts per Connection
The MaxAuthTries directive sets how many attempts are allowed per connection before the server terminates the session:
MaxAuthTries 3
MaxStartups 3:50:10MaxStartups 3:50:10 means: accept at most 3 simultaneous unauthenticated connections; beyond that, reject 50% of new attempts until reaching 10, at which point all new attempts are rejected. This hampers attacks that open many parallel connections.
5. Automatically End Idle Sessions
Abandoned SSH sessions are a real risk: an unlocked terminal on a compromised machine can be used by anyone with local access. Configure automatic timeout:
ClientAliveInterval 300
ClientAliveCountMax 2The server sends a keepalive signal every 300 seconds. After 2 unanswered signals, the session is terminated — meaning any session idle for more than 10 minutes is automatically dropped.
6. Disable Features You Don't Use
OpenSSH ships with several features enabled by default that most servers don't need. Each active feature is a potential attack surface:
# Graphical interface via SSH — rarely needed on headless servers
X11Forwarding no
# Prevents SSH from being used as a generic TCP tunnel
AllowTcpForwarding no
# Prevents a compromised server from using your local SSH agent
AllowAgentForwarding no7. Block IPs with fail2ban
Even with key-based authentication and MaxAuthTries configured, connection attempts keep arriving and pollute your logs. fail2ban automates the banning of IPs that exceed a threshold of failed attempts within time windows — the essential complement to what you configured in sshd_config.
Checking Logs After the Adjustments
After applying the changes, monitor the logs to confirm attempts are being blocked:
# Systems with journald (Ubuntu 20+, Debian 11+, Fedora)
sudo journalctl -u sshd --since "1 hour ago" | grep -i "failed\|invalid"
# Systems with classic auth.log
sudo grep "Failed password\|Invalid user" /var/log/auth.log | tail -20If you still see hundreds of lines of attempts, configure fail2ban to automatically ban IPs.
What If the Server Is Already Compromised?
Hardening prevents future intrusions but doesn't erase traces of past compromises. If your server shows unusual behavior — unknown processes, unexpected traffic, files modified at suspicious times — check for signs of compromise. Compare the hashes of critical binaries like /usr/sbin/sshd against your distribution's original packages using tools like debsums (Debian/Ubuntu) or rpm -Va (RHEL/Fedora).
Full Configuration at a Glance
To ensure all parameters are present and correct in your sshd_config:
PermitRootLogin no
PasswordAuthentication no
AllowUsers your_user
MaxAuthTries 3
MaxStartups 3:50:10
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding noValidate with sudo sshd -t, restart with sudo systemctl restart sshd, and monitor the logs for the first few hours. These 10 parameters cover the vast majority of attack vectors against SSH — no additional external tools needed beyond fail2ban.




Comments