Skip to content

SSH Hardening

SSH is the primary method for remote Linux server management and also the most commonly targeted entry point for attackers. This article systematically covers SSH security hardening measures, from key-based authentication to Fail2Ban brute-force protection, helping you build a secure remote access environment.

Use Key-Based Authentication Instead of Passwords

Section titled “Use Key-Based Authentication Instead of Passwords”

Key-based authentication is more secure and convenient than password authentication. Once configured, you should completely disable password login.

Terminal window
ssh-keygen -t ed25519 -C "[email protected]"

Follow the prompts. It is recommended to set a passphrase for an additional layer of security:

Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/user/.ssh/id_ed25519): Press Enter
Enter passphrase (empty for no passphrase): Enter passphrase
Enter same passphrase again: Re-enter passphrase
Terminal window
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server_ip

Or copy it manually:

Terminal window
cat ~/.ssh/id_ed25519.pub | ssh user@server_ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Terminal window
ssh user@server_ip

If you are no longer prompted for a password (or only need to enter the key passphrase), key-based authentication is working.

The main SSH configuration file is located at /etc/ssh/sshd_config. After each change, you need to restart the sshd service.

Terminal window
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
Terminal window
sudo vi /etc/ssh/sshd_config

Find or add:

PermitRootLogin no

After confirming that key-based login works properly, disable password authentication:

PasswordAuthentication no

Also disable other insecure authentication methods:

ChallengeResponseAuthentication no
KbdInteractiveAuthentication no

Changing the SSH port from the default 22 to another port can significantly reduce automated scanning attacks:

Port 2222

After changing the port, adjust SELinux and the firewall:

Terminal window
# Allow SELinux to use the new port
sudo semanage port -a -t ssh_port_t -p tcp 2222
# Allow the new port through the firewall
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload

Only allow specific users to log in via SSH:

AllowUsers admin deploy

You can also restrict by source IP:

Similarly, you can use AllowGroups for group-based authorization:

AllowGroups sshusers
Terminal window
# Create the group and add users
sudo groupadd sshusers
sudo usermod -aG sshusers admin
sudo usermod -aG sshusers deploy
Terminal window
# Disable empty password login
PermitEmptyPasswords no
# Set maximum authentication attempts
MaxAuthTries 3
# Set login timeout (seconds)
LoginGraceTime 30
# Disable X11 forwarding (if GUI is not needed)
X11Forwarding no
# Specify supported key exchange algorithms (remove weak ones)
KexAlgorithms curve25519-sha256,[email protected]
# Specify supported encryption algorithms
# Specify supported MAC algorithms
# Set client alive check interval
ClientAliveInterval 300
ClientAliveCountMax 2
# Display last login information
PrintLastLog yes
# Use SSH protocol 2 (default on modern systems)
Protocol 2

Check the configuration syntax:

Terminal window
sudo sshd -t

If there is no output, the syntax is correct. Restart the service:

Terminal window
sudo systemctl restart sshd

The following is a recommended complete security configuration:

Terminal window
# /etc/ssh/sshd_config security configuration
Port 2222
Protocol 2
# Authentication settings
PermitRootLogin no
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# User control
AllowUsers admin deploy
MaxAuthTries 3
LoginGraceTime 30
# Security enhancements
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
# Session management
ClientAliveInterval 300
ClientAliveCountMax 2
# Logging
LogLevel VERBOSE
# Algorithms
KexAlgorithms curve25519-sha256,[email protected]

Fail2Ban monitors log files and automatically bans IP addresses with multiple authentication failures, making it an effective tool against brute-force attacks.

Terminal window
sudo dnf install epel-release -y
sudo dnf install fail2ban -y
Terminal window
sudo systemctl enable --now fail2ban

Do not modify /etc/fail2ban/jail.conf directly. Instead, create a local override configuration:

Terminal window
sudo vi /etc/fail2ban/jail.local

Add the following configuration:

[DEFAULT]
# Ban duration (seconds), -1 for permanent ban
bantime = 3600
# Detection time window (seconds)
findtime = 600
# Maximum number of failures
maxretry = 3
# Ban action (using firewalld)
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rules
# Notification email (optional)
destemail = [email protected]
action = %(action_mwl)s
[sshd]
enabled = true
port = 2222
logpath = /var/log/secure
backend = systemd
maxretry = 3
bantime = 3600
Terminal window
sudo systemctl restart fail2ban
Terminal window
# Check overall status
sudo fail2ban-client status
# Check SSH jail details
sudo fail2ban-client status sshd

Example output:

Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 15
| `- File list: /var/log/secure
`- Actions
|- Currently banned: 1
|- Total banned: 3
`- Banned IP list: 203.0.113.50
Terminal window
# Manually unban an IP
sudo fail2ban-client set sshd unbanip 203.0.113.50
# Manually ban an IP
sudo fail2ban-client set sshd banip 203.0.113.100
# View all banned IPs
sudo fail2ban-client banned
  1. Generate and deploy SSH keys:

    Terminal window
    # Run on the client
    ssh-keygen -t ed25519 -C "admin key"
    ssh-copy-id -i ~/.ssh/id_ed25519.pub admin@server_ip
  2. After verifying key-based login works, harden the sshd configuration:

    Terminal window
    # Run on the server
    sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
    sudo tee /etc/ssh/sshd_config.d/hardening.conf <<'EOF'
    Port 2222
    PermitRootLogin no
    PasswordAuthentication no
    PermitEmptyPasswords no
    ChallengeResponseAuthentication no
    MaxAuthTries 3
    LoginGraceTime 30
    AllowUsers admin
    X11Forwarding no
    ClientAliveInterval 300
    ClientAliveCountMax 2
    LogLevel VERBOSE
    EOF
  3. Adjust SELinux and firewall:

    Terminal window
    sudo semanage port -a -t ssh_port_t -p tcp 2222
    sudo firewall-cmd --permanent --add-port=2222/tcp
    sudo firewall-cmd --reload
  4. Check the configuration and restart SSH:

    Terminal window
    sudo sshd -t
    sudo systemctl restart sshd
  5. Test the connection on the new port (keep the old session open):

    Terminal window
    # Run in a new terminal
    ssh -p 2222 admin@server_ip
  6. After confirming the new port works, close the old port:

    Terminal window
    sudo firewall-cmd --permanent --remove-service=ssh
    sudo firewall-cmd --reload
  7. Install and configure Fail2Ban:

    Terminal window
    sudo dnf install epel-release fail2ban -y
    sudo tee /etc/fail2ban/jail.local <<'EOF'
    [DEFAULT]
    bantime = 3600
    findtime = 600
    maxretry = 3
    banaction = firewallcmd-rich-rules
    [sshd]
    enabled = true
    port = 2222
    backend = systemd
    EOF
    sudo systemctl enable --now fail2ban
    sudo fail2ban-client status sshd

After completing these steps, your SSH service will have multiple layers of protection: key-based authentication, a non-standard port, a user whitelist, and automatic banning of brute-force IPs. Using these measures together significantly improves server security.