dotlinux guide

Step-by-Step Tutorial: Securing SSH on Linux

Secure Shell (SSH) is the de facto protocol for remote access to Linux servers, enabling secure command-line login, file transfer, and network administration. By default, SSH is relatively secure, but misconfigurations or outdated practices can expose systems to brute-force attacks, credential theft, or unauthorized access. This tutorial will guide you through fundamental concepts, practical steps, and best practices to harden SSH on Linux, ensuring your remote access remains protected against common threats.

Table of Contents

  1. Understanding SSH Basics
  2. Prerequisites
  3. Step 1: Update SSH and Dependencies
  4. Step 2: Configure the SSH Daemon (sshd_config)
  5. Step 3: Enable Key-Based Authentication
  6. Step 4: Disable Password Authentication
  7. Step 5: Secure SSH with a Firewall
  8. Step 6: Mitigate Brute-Force Attacks with Fail2ban
  9. Step 7: Enforce Two-Factor Authentication (2FA)
  10. Step 8: Limit User and Group Access
  11. Step 9: Enhance Logging and Monitoring
  12. Step 10: Regular Audits and Updates
  13. Common Practices
  14. Best Practices
  15. Conclusion
  16. References

1. Understanding SSH Basics

SSH (Secure Shell) is a cryptographic network protocol that replaces insecure tools like Telnet and FTP by encrypting data in transit. It uses a client-server model:

  • SSH Client: Runs on your local machine (e.g., ssh command-line tool, PuTTY).
  • SSH Server: Runs on the remote Linux server (typically sshd, the OpenSSH daemon).

Core Risks Without Hardening:

  • Brute-force attacks (automated tools guessing passwords).
  • Compromised credentials (e.g., weak passwords).
  • Unpatched vulnerabilities in SSH software.
  • Misconfigurations (e.g., allowing root login, password-only auth).

2. Prerequisites

Before starting, ensure:

  • A Linux server running OpenSSH (most distributions include it by default).
  • Sudo or root access to the server.
  • A backup of critical data (in case of misconfiguration).
  • A secondary terminal session open (to recover if SSH access is lost during setup).

3. Step 1: Update SSH and Dependencies

Outdated SSH software may contain vulnerabilities (e.g., CVE-2024-6387). Always start by updating openssh-server and dependencies:

# For Debian/Ubuntu
sudo apt update && sudo apt upgrade openssh-server -y

# For RHEL/CentOS/Rocky Linux
sudo dnf update openssh-server -y

# Verify the installed version
ssh -V  # Should return "OpenSSH_<version>"

4. Step 2: Configure the SSH Daemon (sshd_config)

The sshd_config file (usually at /etc/ssh/sshd_config) controls SSH server behavior. Always back up the file first:

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

Edit the file with a text editor (e.g., nano or vim):

sudo nano /etc/ssh/sshd_config

Key Configurations to Update:

DirectiveRecommended ValuePurpose
PortNon-default (e.g., 2222)Avoids automated scans targeting port 22.
PermitRootLoginnoPrevents direct root login (use sudo after user login instead).
MaxAuthTries3Limits failed login attempts per connection (blocks brute-force tools).
LoginGraceTime30sReduces time window for brute-force attempts (default: 2m).
StrictModesyesEnsures proper permissions for user home/SSH files (prevents misconfig).

Example sshd_config Snippet:

Port 2222                # Change from default 22
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 30s
StrictModes yes

After editing, validate the configuration to avoid syntax errors:

sudo sshd -t  # Returns "OK" if valid

4. Step 3: Enable Key-Based Authentication

Passwords are vulnerable to brute-force attacks. Key-based authentication uses cryptographic key pairs (public/private) for login, which is far more secure.

Step 3.1: Generate a Key Pair (Client-Side)

On your local machine, generate an SSH key pair. Use the modern ed25519 algorithm (more secure and faster than RSA):

ssh-keygen -t ed25519 -C "[email protected]"
  • -t ed25519: Specifies the Ed25519 algorithm.
  • -C: Adds a comment (e.g., email) to identify the key.

When prompted, set a strong passphrase (encrypts the private key, adding a second layer of security).

Step 3.2: Copy the Public Key to the Server

Transfer your public key to the remote server’s ~/.ssh/authorized_keys file. Use ssh-copy-id (replace user and server_ip with your details):

# If using default port 22:
ssh-copy-id user@server_ip

# If using a custom port (e.g., 2222):
ssh-copy-id -p 2222 user@server_ip

Alternatively, manually copy the public key (e.g., if ssh-copy-id is unavailable):

# On local machine:
cat ~/.ssh/id_ed25519.pub  # Copy the output

# On the server (via existing SSH session):
mkdir -p ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys  # Paste the public key here
chmod 600 ~/.ssh/authorized_keys  # Restrict permissions

Step 3.3: Test Key-Based Login

Verify key-based login works before disabling passwords:

# If using a custom port:
ssh -p 2222 user@server_ip

You should log in without a password (but you’ll need your key passphrase if set).

5. Step 4: Disable Password Authentication

Once key-based login is confirmed, disable password authentication on the server to block password-based attacks.

Edit sshd_config again:

sudo nano /etc/ssh/sshd_config

Set:

PasswordAuthentication no
ChallengeResponseAuthentication no  # Disables keyboard-interactive auth

Restart the SSH daemon to apply changes:

# Debian/Ubuntu:
sudo systemctl restart sshd

# RHEL/CentOS/Rocky Linux:
sudo systemctl restart sshd

6. Step 5: Secure SSH with a Firewall

A firewall limits SSH access to trusted IPs and blocks unauthorized traffic. Use ufw (Uncomplicated Firewall) or firewalld (default on RHEL-based systems).

Example with ufw (Debian/Ubuntu):

# Allow SSH on your custom port (e.g., 2222)
sudo ufw allow 2222/tcp

# Allow only specific IPs (e.g., your home network: 192.168.1.0/24)
sudo ufw allow from 192.168.1.0/24 to any port 2222/tcp

# Enable the firewall
sudo ufw enable

# Verify rules
sudo ufw status

Example with firewalld (RHEL/CentOS):

# Add SSH port and permanentize the rule
sudo firewall-cmd --add-port=2222/tcp --permanent
sudo firewall-cmd --reload

# Verify
sudo firewall-cmd --list-ports

7. Step 6: Mitigate Brute-Force Attacks with Fail2ban

Fail2ban monitors log files for repeated failed login attempts and temporarily bans malicious IPs.

Step 6.1: Install Fail2ban

# Debian/Ubuntu
sudo apt install fail2ban -y

# RHEL/CentOS/Rocky Linux
sudo dnf install fail2ban -y

Step 6.2: Configure Fail2ban

Create a custom configuration file (avoids overwriting defaults during updates):

sudo nano /etc/fail2ban/jail.local

Add SSH protection:

[sshd]
enabled = true
port = 2222  # Use your custom SSH port
filter = sshd
logpath = /var/log/auth.log  # Debian/Ubuntu; RHEL uses /var/log/secure
maxretry = 3  # Ban after 3 failed attempts
bantime = 3600  # Ban duration (1 hour in seconds)
ignoreip = 192.168.1.0/24  # Whitelist trusted IPs (e.g., home network)

Step 6.3: Restart Fail2ban

sudo systemctl restart fail2ban
sudo systemctl enable fail2ban  # Start on boot

Check banned IPs with:

sudo fail2ban-client status sshd

8. Step 7: Enforce Two-Factor Authentication (2FA)

Add an extra layer of security with 2FA, requiring both a private key and a one-time code (e.g., from Google Authenticator).

Step 7.1: Install Google Authenticator PAM Module

# Debian/Ubuntu
sudo apt install libpam-google-authenticator -y

# RHEL/CentOS/Rocky Linux
sudo dnf install google-authenticator -y

Step 7.2: Configure 2FA for a User

Run the authenticator tool as the user who will use 2FA:

google-authenticator

Answer the prompts:

  • Do you want authentication tokens to be time-based? (y/n): y (TOTP standard).
  • Scan the QR code with your Google Authenticator app (or enter the secret key manually).
  • Save the emergency scratch codes (for account recovery if you lose your device).
  • Do you want me to update your "/home/user/.google_authenticator" file? (y/n): y.
  • Do you want to disallow multiple uses of the same authentication token? (y/n): y.
  • Do you want to enable rate-limiting? (y/n): y (limits 3 logins per 30 seconds).

Step 7.3: Update SSH to Use PAM for 2FA

Edit sshd_config to enable PAM (Pluggable Authentication Module) and challenge-response auth:

sudo nano /etc/ssh/sshd_config

Set:

ChallengeResponseAuthentication yes  # Re-enable for 2FA
UsePAM yes

Restart sshd:

sudo systemctl restart sshd

Now, SSH login will require:

  1. Your private key (key-based auth).
  2. A 6-digit code from Google Authenticator (2FA).

9. Step 8: Limit User and Group Access

Restrict SSH access to specific users or groups to minimize exposure.

Edit sshd_config:

sudo nano /etc/ssh/sshd_config

Allow Only Specific Users

Use AllowUsers to whitelist users (separate with spaces):

AllowUsers [email protected]/24 bob  # Alice can connect from 192.168.1.0/24; Bob from anywhere

Allow Only Specific Groups

Create a group (e.g., ssh-users) and allow only its members:

sudo groupadd ssh-users
sudo usermod -aG ssh-users alice  # Add user "alice" to the group

In sshd_config:

AllowGroups ssh-users

Restart sshd to apply:

sudo systemctl restart sshd

10. Step 9: Enhance Logging and Monitoring

SSH logs are critical for detecting attacks. Configure sshd to log detailed activity and monitor logs regularly.

Step 9.1: Enable Verbose Logging

In sshd_config, set LogLevel to VERBOSE (logs key exchanges, auth attempts, etc.):

LogLevel VERBOSE

Step 9.2: View SSH Logs

Logs are stored in:

  • Debian/Ubuntu: /var/log/auth.log
  • RHEL/CentOS: /var/log/secure

Use tail or grep to monitor live activity:

sudo tail -f /var/log/auth.log | grep sshd  # "tail -f" follows real-time logs

Step 9.3: Alert on Suspicious Activity

Use tools like logwatch or fail2ban to send email alerts for failed logins:

# Install logwatch (Debian/Ubuntu)
sudo apt install logwatch -y

# Configure daily email reports (edit /etc/logwatch/conf/logwatch.conf)
sudo nano /etc/logwatch/conf/logwatch.conf

Set MailTo = [email protected] to receive daily summaries.

11. Step 10: Regular Audits and Updates

SSH security is not “set and forget.” Regularly audit configurations and update software to patch vulnerabilities.

Audit Tools:

  • ssh-audit: Scans SSH server configuration for weaknesses (GitHub).
    # Run from local machine (replace port and IP)
    ssh-audit server_ip:2222
  • sshd -T: Lists all active sshd configuration parameters (compare with hardening guidelines).

Update SSH Software:

Run regular system updates to patch openssh-server:

# Debian/Ubuntu
sudo apt update && sudo apt upgrade -y

# RHEL/CentOS/Rocky Linux
sudo dnf update -y

12. Common Practices

  • Avoid default port 22: Use a non-standard port (e.g., 2222) to reduce scan exposure.
  • Never hardcode keys: Store private keys securely (e.g., ~/.ssh/ with chmod 600 permissions).
  • Revoke compromised keys: Remove unauthorized public keys from ~/.ssh/authorized_keys.
  • Use SSH agents: Tools like ssh-agent or keychain manage passphrases, avoiding repeated entry.

13. Best Practices

  • Use Ed25519 keys: Prefer over RSA for stronger security and better performance.
  • Enforce MFA for all users: Even key-based auth benefits from 2FA as a backup.
  • Limit SSH to trusted IPs: Use firewalls to block access from untrusted networks.
  • Rotate keys annually: Expire old keys and generate new ones to minimize exposure.
  • Monitor logs with SIEM tools: Use solutions like ELK Stack or Splunk to detect anomalies at scale.

14. Conclusion

Securing SSH is critical for protecting remote server access. By following this tutorial, you’ve implemented key hardening steps: non-default ports, key-based auth, 2FA, firewall rules, and logging. Remember, security is iterative—regularly audit configurations, update software, and stay informed about new threats (e.g., via CVE databases). With these measures, you’ll significantly reduce the risk of SSH-related breaches.

15. References