10 Essential Best Practices for Hardening Your SSH Server

Harden your SSH server with safer authentication, least-privilege access, firewall rules, rate limiting, updates, and log checks.

10 Essential Best Practices for Hardening Your SSH Server

Secure Shell (SSH) is usually the front door to your server. If that door accepts weak passwords, direct root logins, or traffic from the whole internet, attackers will find it quickly.

This guide gives you 10 practical SSH server hardening steps you can apply to OpenSSH on common Linux distributions. Keep one working session open while you test each change so you can recover if a firewall rule or config edit blocks new logins.

1. Change the Default SSH Port

The default SSH port is 22. This is universally known and constantly scanned by automated bots looking for vulnerable servers. Changing the default port provides a simple, yet effective, layer of obscurity. While not a security measure on its own, it significantly reduces the noise from automated scans and helps avoid many opportunistic attacks.

To change the port, edit the sshd_config file, typically located at /etc/ssh/sshd_config.

sudo nano /etc/ssh/sshd_config

Find the line Port 22 (or add it if it doesn't exist) and change 22 to a non-standard, unassigned port number (e.g., 2222, 49152-65535 are user/dynamic ports).

#Port 22
Port 2222

Before restarting SSH, allow the new port in your firewall. Then test a new login on the new port before you remove access to port 22.

# For UFW (Uncomplicated Firewall):
sudo ufw allow 2222/tcp
sudo ufw reload

# For Firewalld:
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload

sudo sshd -t
sudo systemctl restart sshd

Tip: Always keep at least one SSH session open while testing configuration changes. If you get locked out, you can revert changes in the active session.

2. Disable Root Login

Direct root login via SSH is highly discouraged. The root user has unrestricted privileges, making it a prime target for attackers. If an attacker gains root access, they have full control over your system. Instead, log in as a regular, unprivileged user and then use sudo to perform administrative tasks.

In sshd_config, find the PermitRootLogin directive and set it to no.

PermitRootLogin no

Save and restart the SSH service:

sudo systemctl restart sshd

3. Use Key-Based Authentication

Password-based authentication is susceptible to brute-force attacks and dictionary attacks, especially if users choose weak passwords. SSH key-based authentication is a far more secure alternative. It uses a pair of cryptographic keys: a public key stored on the server and a private key kept on your local machine. Only clients with the matching private key can authenticate.

Steps to implement key-based authentication (briefly):

  1. Generate a key pair on your local machine:

    ssh-keygen -t ed25519 -C "[email protected]"
    
    • ed25519 is a modern, secure algorithm with a fixed key size. rsa is also common; if you use RSA, generate a large key such as ssh-keygen -t rsa -b 4096.
  2. Copy the public key to your server:

    ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your_server_ip
    

    Alternatively, manually append the contents of ~/.ssh/id_ed25519.pub to ~/.ssh/authorized_keys on the server for the user you wish to log in as.

  3. Ensure correct permissions on the server:

    • ~/.ssh directory: 700 (rwx for owner only)
    • ~/.ssh/authorized_keys file: 600 (rw for owner only)
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys
    

4. Disable Password Authentication

Once you have successfully set up and tested key-based authentication for all necessary users, you should disable password authentication entirely. This removes the weakest link in SSH security by making brute-force password attacks impossible.

In sshd_config, set PasswordAuthentication to no.

PasswordAuthentication no
KbdInteractiveAuthentication no

On some distributions, PAM or keyboard-interactive authentication can still prompt for passwords unless you disable it as well. Test with a fresh terminal before closing your existing session.

Save and restart the SSH service:

sudo systemctl restart sshd

Warning: Never disable password authentication before verifying that key-based authentication works correctly for all users who need access. Otherwise, you risk locking yourself out of the server.

5. Limit User Access

By default, any user account on the server can attempt to log in via SSH. You can restrict SSH access to only specific users or groups, minimizing the attack surface.

Use AllowUsers or AllowGroups directives in sshd_config.

To allow only specific users (e.g., adminuser, devuser):

AllowUsers adminuser devuser

To allow only members of a specific group (e.g., sshusers):

AllowGroups sshusers

Tip: It's generally better to use AllowGroups if you have multiple users. Create a dedicated group for SSH access and add authorized users to it.

sudo groupadd sshusers
sudo usermod -aG sshusers adminuser
sudo usermod -aG sshusers devuser

After making changes, save and restart SSH.

6. Use Strong Passphrases for SSH Keys

While key-based authentication is robust, your private key is still a critical asset. If an attacker gains access to your local machine, they could steal your private key. A strong passphrase encrypts your private key, requiring the passphrase to unlock it before use. This adds another layer of security, protecting your key even if it falls into the wrong hands.

When generating your SSH key (as in step 3), you will be prompted for a passphrase. Choose a long, complex, and memorable passphrase that is different from any other password you use.

7. Implement Connection Rate Limiting (Fail2Ban)

Even with key-based authentication, an SSH server is still subject to connection attempts. Tools like Fail2Ban can actively monitor SSH logs for repeated failed login attempts from the same IP address and automatically block that IP address using firewall rules for a set period.

Installation (example for Debian/Ubuntu):

sudo apt update
sudo apt install fail2ban

Fail2Ban works out of the box with default SSH rules, but you can customize its configuration by copying jail.conf to jail.local and editing it.

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Within jail.local, you can adjust bantime, findtime, and maxretry for the [sshd] section.

[sshd]
enabled = true
port = 2222 # Your new SSH port
logpath = %(sshd_log)s
maxretry = 3
bantime = 1h
findtime = 10m

Restart Fail2Ban after configuration changes:

sudo systemctl restart fail2ban

8. Keep Your SSH Server Software Updated

Software vulnerabilities are constantly discovered and patched. Running outdated SSH daemon software (OpenSSH) means you might be exposed to known exploits. Regularly updating your server's software, including the OpenSSH server package, is crucial for patching security vulnerabilities.

# For Debian/Ubuntu:
sudo apt update && sudo apt upgrade

# For CentOS/RHEL:
sudo yum update
# or
sudo dnf update

Configure your system to apply security updates automatically where appropriate, or establish a routine for manual updates.

9. Monitor SSH Logs for Suspicious Activity

Even with robust preventative measures, vigilance is key. Regularly review SSH authentication logs to detect unusual patterns, failed login attempts, or unauthorized access attempts. This helps you identify potential breaches or ongoing attacks.

SSH logs are typically found in:

  • /var/log/auth.log (Debian/Ubuntu)
  • /var/log/secure (CentOS/RHEL)
  • Using journalctl (systemd systems):
    sudo journalctl -u sshd -f
    

Look for repeated failed authentication attempts, logins from unusual IP addresses, or successful logins by unfamiliar users. Tools like Logwatch or Elastic Stack (ELK) can automate log analysis and alerting for larger environments.

10. Configure Firewall Rules to Restrict Access

A firewall is your first line of defense. By default, it should block all incoming traffic except for the services you explicitly need to expose. For SSH, this means allowing connections only on your chosen port (e.g., 2222) and, ideally, only from specific trusted IP addresses or networks.

Example using UFW (Uncomplicated Firewall):

Allow SSH from a specific IP address 192.168.1.100 on port 2222:

sudo ufw allow from 192.168.1.100 to any port 2222

Allow SSH from a specific subnet 192.168.1.0/24:

sudo ufw allow from 192.168.1.0/24 to any port 2222

Example using Firewalld (CentOS/RHEL):

Allow SSH from a specific IP address 192.168.1.100 on port 2222:

sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.100" port port=2222 protocol="tcp" accept'
sudo firewall-cmd --reload

Warning: If you're managing a server from a dynamic IP address, be cautious with strict firewall rules. You might need to allow access from a broader range or use a VPN.

Additional Hardening Tips

Beyond the essential 10, consider these directives for even greater security:

  • MaxAuthTries: Limits the number of authentication attempts per connection. Default is 6. Lowering it (e.g., 3) reduces brute-force chances. Set in sshd_config.
    MaxAuthTries 3
    
  • LoginGraceTime: Limits the time allowed for a user to authenticate. Default is 2 minutes. Lowering it (e.g., 30s) reduces the window for slow attacks.
    LoginGraceTime 30s
    
  • ClientAliveInterval and ClientAliveCountMax: Prevent idle SSH sessions from remaining open indefinitely. ClientAliveInterval sends a keepalive message every X seconds. If ClientAliveCountMax responses are missed, the connection is terminated.
    ClientAliveInterval 300
    ClientAliveCountMax 2
    
  • Banner: Display a warning message before authentication. This serves legal notice to potential unauthorized users.
    Banner /etc/issue.net
    
    Create the /etc/issue.net file with your desired warning message.

Practical Takeaway

Start with the changes that remove the biggest risk: disable root login, require SSH keys, restrict who can connect, and limit the source networks when you can. Test every change with sshd -t and a new login session before closing your current one.