Step-by-Step Guide: Harden SSH with Security Best Practices

SSH (Secure Shell) is vital for remote Linux server administration, but default settings often lack sufficient security. Hardening SSH is essential to protect your system from unauthorised access, brute-force attacks, and other threats.

This guide outlines best practices for securing SSH, using a Linode server as an example. These steps apply universally across all server environments.

Overview of SSH Hardening Techniques #

Authentication and Access Control

  • Disable root login
    Prevent direct root access over SSH to reduce the impact of brute-force attacks.
  • Use key-based authentication
    SSH keys are more secure than passwords and eliminate the risk of password-guessing attacks.
  • Disable password authentication
    Forces all users to authenticate with SSH keys.
  • Enforce strong SSH key algorithms
    Use secure key types like ed25519 for strong, efficient authentication.
  • Restrict user access
    Explicitly define which users or groups are allowed to connect via SSH.

Network and Port Security

  • Change the default SSH port
    Using a non-standard SSH port reduces automated scanning and noise.

Protocol

  • Set a short login grace time
    Limit how long an unauthenticated user can log in to reduce brute-force risk.

Step 1: Create Key Pair #

Create SSH key pair with Ed25519 algorithm (on local machine):

bash
ssh-keygen -t ed25519 -C "<[email protected]>" -f ~/.ssh/<key_name>
  • Key naming: Create a separate SSH key for each Linode. Use clear, descriptive names such as linode-blog or linode-test.

  • Key algorithm: Use Ed25519. It offers strong security, high performance, and better long-term support than RSA or ECDSA.

  • Key rotation: Regular rotation is good practice but not essential. If your private key is secured with a strong passphrase and stored safely, and there’s no sign of compromise, frequent changes are unnecessary.

Notes:

  • -t: Specify key type (ecdsa, ed25519, rsa).
  • -C: Add a comment (typically your email address).
  • -f: File name to save the key (default is id_ed25519 or id_rsa).
  • Example key_name: linode-blog

Verify key creation:

bash
ls -l ~/.ssh

Set private key permissions to 600:

bash
chmod 600 ~/.ssh/<key_name>

Step 2: Copy Public Key to Instance #

Copy public key:

bash
ssh-copy-id -i ~/.ssh/<key_name>.pub <user>@<public_ipv4>

Log in:

bash
ssh <user>@<public_ipv4>

Verify if public key is copied:

bash
vim ~/.ssh/authorized_keys

Set permissions:

bash
sudo chmod -R 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys

Step 3: Set Up SSH Config #

Backup current configuration:

bash
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

Edit SSH configuration file:

bash
sudo vim /etc/ssh/sshd_config

Modify:

Custom SSH port: select any TCP port number between 1024 and 49151. This range includes unprivileged, user-defined ports not reserved by the system.

/etc/ssh/sshd_config
Port <custom_port>           # Change default SSH port (22) to improve security through obscurity
LoginGraceTime 30s           # Allow 30 seconds for users to authenticate before disconnecting
PermitRootLogin no           # Disable root login to enhance security
AllowUsers <user1> <user2>   # Restrict SSH access to specified user(s) only
PubkeyAuthentication yes     # Enable public key authentication
PasswordAuthentication no    # Disable password authentication to enforce key-based login

Restart SSH service to apply changes:

bash
sudo systemctl restart ssh

Step 4: Update Firewall Settings #

  1. In the Linode dashboard, go to Network > Firewall.
  2. Under SSH port, select Custom.
  3. Enter your custom SSH port range in Custom port range (e.g. <custom_port>).
  4. Click Save Changes.
  5. Navigate to Compute > Linodes.
  6. Select your Linode, right-click it, and choose Reboot.
  7. Confirm by clicking Reboot Linode.

Step 5: Create Custom Host #

Edit SSH config (on local machine):

bash
vim ~/.ssh/config

Add:

~/.ssh/config
Host <host>
  HostName <public_ipv4_or_domain>
  Port: <custom_port>
  User <user>
  IdentityFile ~/.ssh/<key_name>

Examples:

~/.ssh/config
Host linode-blog
  HostName xxx.xxx.xx.xxx
  Port xxxxx
  User jane
  IdentityFile ~/.ssh/linode-blog

Host linode-test
  HostName domain.com
  Port xxxxx
  User john
  IdentityFile ~/.ssh/linode-test

Result

You can nog connect using:

bash
ssh <host>

Instead of:

bash
ssh -i ~/.ssh/<key_name> -p <custom_port> <user>@<public_ipv4>