analyst @ nohacky :~/briefings $
cat / briefings / ssh-security-best-practices
analyst@nohacky:~/briefings/ssh-security-best-practices.html
reading mode 9 min read
category linux
published March 2025
read_time 9 min

SSH Security Best Practices

SSH is the backbone of remote server administration — and one of the most persistently targeted entry points in the threat landscape. Hardening your SSH configuration is not optional. It is baseline hygiene, and it starts with understanding where the defaults fail you.

Secure Shell (SSH) was designed to replace insecure protocols like Telnet and rsh by encrypting communications between a client and a remote host. But a protocol being encrypted does not make it secure by default. The way SSH is configured, who is permitted to use it, and what authentication methods are allowed can turn a well-intentioned remote access tool into an open door for attackers.

Credential stuffing, brute-force attacks against password-authenticated SSH, and exploitation of weak key management are routine. Threat actors — including nation-state groups — routinely scan for SSH services exposed on default port 22, probe for known usernames, and attempt to reuse credentials harvested from prior breaches. The good news is that many of the most effective SSH hardening steps cost nothing and take minutes to implement.

Use Key-Based Authentication — and Disable Passwords

Password authentication over SSH is a liability. Even strong passwords are vulnerable to brute-force attempts at scale, and credential reuse from other breaches means that a password you consider unique may already be in an attacker's wordlist. Public key authentication eliminates this attack surface entirely.

The mechanism is straightforward: you generate a key pair on the client, copy the public key to the server's ~/.ssh/authorized_keys file, and the server validates the cryptographic challenge without a password ever leaving your machine. Generate an Ed25519 key, which is the current recommended algorithm:

ssh-keygen -t ed25519 -C "your_identifier"
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@remote-host

Once keys are in place, disable password authentication entirely in /etc/ssh/sshd_config:

PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
warning

Confirm that your public key authentication is working before disabling passwords. Testing in a second terminal window before closing your current session prevents you from locking yourself out of the server.

Also consider adding a passphrase to your private key. If the key file is ever compromised, the passphrase provides a second layer of protection. Use ssh-agent to avoid having to enter the passphrase repeatedly during a session.

Disable Root Login Over SSH

Root is the highest-privilege account on a Linux system, and it exists on every server by default. Allowing root to authenticate directly over SSH means that a successful attack grants immediate total control with no further privilege escalation required. There is no defensive reason to allow it.

Set the following in /etc/ssh/sshd_config:

PermitRootLogin no

Administrators who need root access should log in as a standard user and escalate with sudo. This approach creates an audit trail — sudo logs who ran what and when — and limits the blast radius if a user account is compromised.

note

If automated processes (backups, deployment scripts) currently rely on direct root SSH access, refactor them to use a dedicated non-root service account with only the minimum permissions needed.

Change the Default Port

Port 22 is the default SSH port, and it is scanned millions of times a day by automated scanners. Moving SSH to a non-standard port is not a security control on its own — an attacker performing a full port scan will find it — but it does eliminate a significant volume of opportunistic, automated noise from your logs and reduces exposure to commodity attacks that only target port 22.

Port 2222

After changing the port, update any firewall rules and inform users and scripts that connect to the host. On systems using firewalld or ufw, remember to allow the new port and remove the old one. When connecting, specify the port with ssh -p 2222 user@host or configure it in ~/.ssh/config on the client side.

Restrict Access with AllowUsers and AllowGroups

By default, any valid user account on the server can attempt to authenticate over SSH. If your system has service accounts, legacy users, or accounts provisioned for other purposes, that is unnecessary exposure. Use explicit allow lists to control who can authenticate.

# Allow specific users only
AllowUsers alice bob deploybot

# Or control by group membership
AllowGroups sshusers admins

Create a dedicated group for SSH access, add only the accounts that require it, and reference that group in sshd_config. This makes access management auditable and ensures that new accounts added to the system do not automatically inherit SSH access.

Limit Authentication Attempts and Idle Sessions

Default SSH configuration is permissive about how many authentication attempts a client can make before being disconnected. Tightening this reduces the window for brute-force attacks and limits what automated tools can accomplish before being cut off.

# Maximum authentication attempts per connection
MaxAuthTries 3

# Disconnect idle sessions
ClientAliveInterval 300
ClientAliveCountMax 2

# Limit concurrent unauthenticated connections
MaxStartups 10:30:60

ClientAliveInterval sends a keepalive message every 300 seconds (5 minutes). ClientAliveCountMax 2 means the server will disconnect after two unanswered keepalives — so an unresponsive session will be terminated after approximately 10 minutes. The MaxStartups directive controls the rate at which unauthenticated connections are accepted, helping protect against connection-flooding attacks.

Use a Strong SSH Protocol Version and Ciphers

Modern OpenSSH installations default to SSHv2, but it is worth verifying explicitly. SSHv1 had fundamental cryptographic flaws and should never be in use on any system. Beyond the protocol version, the cipher suites and key exchange algorithms available to connecting clients can also be locked down.

Protocol 2

# Restrict to strong ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com

# Restrict key exchange algorithms
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512

# Restrict MACs
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
note

Cipher hardening can break connectivity for older clients. Audit the SSH clients in your environment before enforcing strict cipher lists in production. The Mozilla OpenSSH guidelines and NIST SP 800-52 are useful references for up-to-date recommendations.

Implement Two-Factor Authentication

Even with key-based authentication enabled, adding a second factor creates defense in depth. If a private key is stolen — from a compromised workstation, a leaked backup, or an improperly secured secrets manager — a second factor prevents the key alone from granting access.

On Linux systems, libpam-google-authenticator provides TOTP-based two-factor authentication that integrates with PAM and SSH. After installing and configuring it, update /etc/pam.d/sshd to require the authenticator, then set the following in sshd_config:

ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

This configuration requires both a valid public key and a TOTP code. Hardware security keys (FIDO2/U2F) are another strong option and are supported natively in OpenSSH 8.2 and later via the sk key types (ecdsa-sk and ed25519-sk).

Monitor and Rate-Limit with Fail2ban

Even with strong authentication controls in place, automated scanning and brute-force attempts will hit your SSH service. fail2ban monitors authentication logs and dynamically bans IP addresses that trigger a configurable threshold of failures.

# /etc/fail2ban/jail.local
[sshd]
enabled = true
port    = 2222
filter  = sshd
logpath = /var/log/auth.log
maxretry = 4
bantime  = 3600
findtime = 600

This configuration bans any IP that fails authentication four times within a 10-minute window for one hour. Adjust bantime to a longer duration for repeat offenders using the recidive jail. Pair fail2ban with firewall rules (ufw, firewalld, or iptables) to ensure bans are applied at the network level.

critical

Whitelist your own management IP addresses in fail2ban's ignoreip setting before enabling aggressive ban rules. Locking yourself out of a cloud server with no out-of-band access can be a serious operational incident.

Keep OpenSSH Updated

OpenSSH has a strong security record, but vulnerabilities do surface. In early 2024, a regression introduced a signal handler race condition in the OpenSSH server (CVE-2024-6387, branded "regreSSHion") that enabled unauthenticated remote code execution as root on glibc-based Linux systems. The flaw affected a wide range of OpenSSH versions and required patching — not configuration changes alone.

Running an unpatched SSH daemon on an internet-facing system means that configuration hardening, however thorough, cannot protect against software vulnerabilities. Treat OpenSSH updates as critical patches. Subscribe to your distribution's security advisories and apply updates promptly.

An estimated 14 million potentially vulnerable OpenSSH server instances were exposed to the internet at the time CVE-2024-6387 was disclosed. — Qualys Threat Research Unit, July 2024

Key Takeaways

  1. Replace passwords with public key authentication. Ed25519 keys are the current recommended choice. Disable password authentication in sshd_config once keys are confirmed working.
  2. Block root login entirely. Require escalation via sudo from a named user account instead. This preserves an audit trail and limits blast radius.
  3. Restrict who can authenticate. Use AllowUsers or AllowGroups to create an explicit allow list rather than permitting any valid system account to use SSH.
  4. Tighten session and connection limits. Set MaxAuthTries, ClientAliveInterval, and MaxStartups to reduce the window for brute-force attempts and clean up idle sessions.
  5. Add a second factor. TOTP or hardware security keys protect against compromised private keys.
  6. Deploy fail2ban and keep OpenSSH current. Automated rate-limiting and timely patching address the threats that configuration alone cannot fully mitigate.

SSH hardening is not a one-time task. Configurations drift, new users get added without review, and software vulnerabilities require ongoing attention. Treat your SSH configuration as a living document: audit it periodically, test it against your current threat model, and verify that the controls you put in place are still in effect after system updates and changes.

— end of briefing