Stackvora.tech

Linux: harden OpenSSH server in 5 minutes

Securing remote access to Linux systems is paramount for maintaining infrastructure integrity. OpenSSH, the de facto standard for secure shell access,…

Securing remote access to Linux systems is paramount for maintaining infrastructure integrity. OpenSSH, the de facto standard for secure shell access, offers a robust set of configuration options to mitigate common attack vectors. This guide focuses on quickly hardening an OpenSSH server configuration, balancing strong security practices with operational usability.

Initial Configuration Review and Backup

Before making any changes, understand the current state of your sshd_config and create a backup. The primary configuration file is typically located at /etc/ssh/sshd_config. Modern distributions (e.g., RHEL 8+, Debian 10+, Ubuntu 20.04+) also support including configuration fragments from a directory, commonly /etc/ssh/sshd_config.d/. Using a dedicated file for hardening (e.g., 99-hardening.conf) in this directory simplifies management and updates.

# Backup the main configuration file
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

# Create or edit a hardening configuration file
# Using .d/ directory ensures your changes are less likely to be overwritten by package updates
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

If your system does not support the sshd_config.d/ directory, append or modify settings directly in /etc/ssh/sshd_config. Always ensure only one instance of any given directive is present; later directives override earlier ones.

Restrict Access and Authentication Methods

Change Default Port (Optional, but Recommended)

Moving SSH from its default port 22 reduces noise from automated scanning bots. While not a security measure in itself (a determined attacker will find it), it significantly cuts down on log entries and resource consumption from failed authentication attempts.

Port 2222  # Choose an unprivileged port > 1024 that isn't in use

Remember to update your firewall rules (e.g., ufw allow 2222/tcp or firewall-cmd --add-port=2222/tcp --permanent, then firewall-cmd --reload) if you change the port.

Disable Root Login

Logging in directly as root is a significant security risk. It prevents accountability and increases the impact of a compromised password. Users should log in with a regular account and use sudo for administrative tasks.

PermitRootLogin no

For scenarios requiring root access, consider PermitRootLogin prohibit-password (allows key-based only) or PermitRootLogin forced-commands-only with strict authorized_keys restrictions for specific automated tasks.

Enforce Public Key Authentication

Password-based authentication is susceptible to brute-force attacks and dictionary attacks. Public key authentication is far more secure, relying on cryptographic key pairs.

PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no # Older, less common, but good to disable
PubkeyAuthentication yes

Before disabling password authentication, ensure all necessary users have their public keys (~/.ssh/id_rsa.pub or similar) added to their ~/.ssh/authorized_keys file on the server. If you lock yourself out, you may need console access to restore password authentication.

Limit Login Attempts

Reduce the window for brute-force attacks by limiting the maximum number of authentication attempts per connection.

MaxAuthTries 3

This setting means a client will be disconnected after 3 failed authentication attempts.

Restrict User/Group Access

Explicitly define which users or groups are allowed to connect via SSH. This acts as an additional layer of defense.

AllowGroups sshusers

Ensure that all users who need SSH access are members of the sshusers group (or whatever group you specify). You can create this group with sudo groupadd sshusers and add users with sudo usermod -aG sshusers username. Conversely, you can use DenyUsers or DenyGroups.

For more granular control, AllowUsers can specify individual users (e.g., AllowUsers john mary). Note that AllowUsers/DenyUsers and AllowGroups/DenyGroups are mutually exclusive in practice; if AllowUsers is set, other users are implicitly denied, and group directives are ignored for allowed users.

Session Management and Keepalives

Configure Client Alive Intervals

Prevent orphaned SSH sessions and ensure that idle connections are terminated. This is particularly useful for environments where network connectivity might be unstable, or to reclaim resources from forgotten sessions.

ClientAliveInterval 300   # Server sends null packets if no data received for 300 seconds (5 minutes)
ClientAliveCountMax 2     # Disconnect after 2 such probes without response (total 15 minutes idle)

With these settings, an idle session will be disconnected after approximately ClientAliveInterval * (ClientAliveCountMax + 1) seconds, which is 300 * (2 + 1) = 900 seconds or 15 minutes in this example.

Ciphers, MACs, and KexAlgorithms

OpenSSH relies on cryptographic algorithms for key exchange (KexAlgorithms), encryption (Ciphers), and integrity checking (MACs). Regularly review and update these to use strong, modern, and efficient algorithms while deprecating older, weaker ones. The following recommendations are suitable for OpenSSH 7.x and newer.

Key Exchange Algorithms (KexAlgorithms)

Prefer elliptic curve Diffie-Hellman (ECDH) key exchange algorithms for their efficiency and strong security properties.

KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256

curve25519-sha256 is a modern, strong choice. Include diffie-hellman-group-exchange-sha256 for broader compatibility with some older clients, but prioritize ECDH.

Ciphers

Use modern authenticated encryption modes, such as ChaCha20-Poly1305 and AES-GCM. These provide both confidentiality and integrity.

Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

Prioritize chacha20-poly1305 and aes*-gcm. Fallback to aes*-ctr for compatibility. Avoid CBC modes entirely if possible.

Message Authentication Codes (MACs)

MACs ensure the integrity of transmitted data. Use HMAC-SHA2 variants with "etm" (Encrypt-then-MAC) for better security.

MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

The -etm suffix indicates Encrypt-then-MAC, which is considered more secure than MAC-then-Encrypt. umac-128-etm is also a good, modern choice. Avoid all SHA1 and MD5 MACs.

You can check the supported algorithms on your SSH server with ssh -Q kex, ssh -Q cipher, ssh -Q mac, etc.

Final Configuration Snippet Example

Your /etc/ssh/sshd_config.d/99-hardening.conf (or modified /etc/ssh/sshd_config) would look something like this:

# Basic Security
Port 2222
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowGroups sshusers

# Session Management
ClientAliveInterval 300
ClientAliveCountMax 2

# Cryptographic Hardening (OpenSSH 7.x+)
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

# Other Recommended Settings
# Disable X11 forwarding if not explicitly needed
X11Forwarding no

# Disable agent forwarding if not explicitly needed (can be a security risk)
AllowAgentForwarding no

# Disable TCP forwarding if not explicitly needed
AllowTcpForwarding no

# Prevent host key changes from being ignored
StrictModes yes

# LogLevel VERBOSE provides more detail for troubleshooting
# LogLevel INFO is default, quiet but sufficient for daily use
LogLevel INFO

Apply and Test Configuration

After modifying the configuration, always test its syntax before reloading the SSH service. A syntax error can prevent SSH from starting, potentially locking you out.

# Test configuration syntax
sudo sshd -t

# If no output, syntax is OK. If errors, fix them.
# Reload the SSH daemon to apply changes
sudo systemctl reload sshd

It is highly recommended to keep a separate, active SSH session open while testing. If the reload fails or locks you out, you can use the active session to revert changes. Once you've confirmed you can log in with the new configuration, close the old session.

Common Pitfalls and Troubleshooting

  • Locked out after reload: Most common issue. This usually happens if PasswordAuthentication no is set before your public key is properly installed, or if AllowGroups/AllowUsers is too restrictive. Always test with a separate session open. If locked out, physical console or out-of-band management (e.g., IPMI, EC2 console) is your only recourse.
  • Incorrect file permissions: ~/.ssh/ should be 700, ~/.ssh/authorized_keys should be 600. Incorrect permissions will prevent SSH from using the keys.
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys
  • SELinux/AppArmor issues: On systems with mandatory access control, SELinux or AppArmor might prevent sshd from reading configuration files or user home directories. Check respective logs (/var/log/audit/audit.log for SELinux, /var/log/kern.log or /var/log/syslog for AppArmor).
  • Firewall blocking new port: If you change the SSH port, remember to update firewall rules. Use ss -tulnp | grep sshd to confirm sshd is listening on the new port, and sudo ufw status or sudo firewall-cmd --list-all to check firewall rules.
  • Using deprecated algorithms: Older clients or specific tools might fail to connect if you aggressively remove too many older algorithms without providing a compatible fallback. Check your client's SSH version and supported algorithms if connections fail.

Back to the knowledge base · Ask the AI assistant