Table of Contents
- Understanding Your Current SSH Configuration
- Key Hardening Steps
- 2.1 Disable Password Authentication & Use SSH Keys
- 2.2 Limit User and Group Access
- 2.3 Change the Default SSH Port
- 2.4 Configure Idle Timeout and Auto-Disconnect
- 2.5 Enforce Strong Ciphers, Key Exchanges, and MACs
- 2.6 Enhance Logging and Monitoring
- 2.7 Implement Firewall Rules
- 2.8 Enable Two-Factor Authentication (2FA)
- 2.9 Disable Unused Features
- 2.10 Keep OpenSSH Updated
- Testing Your Hardened Configuration
- Common Pitfalls & Troubleshooting
- Conclusion
- References
1. Understanding Your Current SSH Configuration
Before making changes, audit your existing SSH setup to identify vulnerabilities. Start with these checks:
Check OpenSSH Version
Older versions may have known vulnerabilities (e.g., CVE-2024-6387). Verify your version with:
ssh -V # Client version
sshd -V # Server version (run as root)
Aim for OpenSSH 8.0+ for modern features like Ed25519 keys and ChaCha20 ciphers.
Audit sshd_config
The main configuration file for the SSH server (sshd) is /etc/ssh/sshd_config. Review it for insecure defaults:
cat /etc/ssh/sshd_config | grep -v '^#' | grep -v '^$' # Filter comments/empty lines
Use Automated Tools
Tools like ssh-audit (a lightweight scanner) can identify weak ciphers, protocols, or misconfigurations:
# Install ssh-audit (e.g., on Ubuntu)
sudo apt install ssh-audit
# Scan your SSH server
ssh-audit localhost
2. Key Hardening Steps
2.1 Disable Password Authentication & Use SSH Keys
Passwords are vulnerable to brute-force attacks and human error (e.g., weak passwords). SSH keys offer stronger, passwordless authentication.
Step 1: Generate an SSH Key Pair
Use Ed25519 (Elliptic Curve Digital Signature Algorithm) for better security and performance than RSA. For compatibility with older systems, use RSA (4096-bit minimum).
Generate Ed25519 key (recommended):
ssh-keygen -t ed25519 -C "[email protected]"
Generate RSA key (for legacy systems):
ssh-keygen -t rsa -b 4096 -C "[email protected]"
- The key pair will be saved to
~/.ssh/id_ed25519(private key) and~/.ssh/id_ed25519.pub(public key). - Never share the private key (
id_ed25519).
Step 2: Copy the Public Key to the Server
Use ssh-copy-id to transfer your public key to the server’s ~/.ssh/authorized_keys file:
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@server_ip
If ssh-copy-id isn’t available, manually copy the public key:
# On your local machine, display the public key
cat ~/.ssh/id_ed25519.pub
# On the server, create ~/.ssh (if missing) and append the public key
mkdir -p ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys # Paste the public key here
chmod 600 ~/.ssh/authorized_keys # Restrict permissions
Step 3: Verify Key Authentication
Test login with your key (no password prompt should appear):
ssh -i ~/.ssh/id_ed25519 username@server_ip
Step 4: Disable Password Authentication
Edit /etc/ssh/sshd_config to disable password-based login:
sudo nano /etc/ssh/sshd_config
Set these directives:
PasswordAuthentication no # Disable password logins
ChallengeResponseAuthentication no # Disable keyboard-interactive auth
Critical Note: Ensure key authentication works before disabling passwords—otherwise, you may lock yourself out!
2.2 Limit User and Group Access
Restrict SSH access to specific users/groups to minimize attack surface.
Disable Root Login
Never allow direct root login—use sudo from a regular user account instead:
PermitRootLogin no # In sshd_config
Allow/Deny Specific Users/Groups
Use AllowUsers, AllowGroups, DenyUsers, or DenyGroups to restrict access. For example:
AllowUsers alice [email protected]/24 # Allow alice (any IP) and bob (only local subnet)
AllowGroups ssh-users # Allow only users in the "ssh-users" group
DenyUsers charlie # Block user "charlie"
Create a dedicated ssh-users group (optional but recommended):
sudo groupadd ssh-users
sudo usermod -aG ssh-users alice # Add user "alice" to the group
2.3 Change the Default SSH Port
The default SSH port (22) is a target for automated scans. Changing it to a non-standard port (e.g., 2222) reduces noise from brute-force attempts.
Step 1: Update sshd_config
Port 2222 # Replace 22 with your chosen port (1024-65535)
Step 2: Update Firewall Rules
Allow the new port in your firewall (e.g., ufw or iptables):
Using UFW (Uncomplicated Firewall):
sudo ufw allow 2222/tcp
sudo ufw reload
Using iptables:
sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
sudo iptables-save | sudo tee /etc/iptables/rules.v4 # Persist rules (Debian/Ubuntu)
Step 3: Test Access on the New Port
ssh -p 2222 username@server_ip
2.4 Configure Idle Timeout and Auto-Disconnect
Idle SSH sessions are targets for hijacking. Automatically terminate inactive sessions with:
ClientAliveInterval 300 # Send a keepalive every 300 seconds (5 minutes)
ClientAliveCountMax 3 # Terminate after 3 failed keepalives (15 minutes total)
2.5 Enforce Strong Ciphers, Key Exchanges, and MACs
Older ciphers (e.g., 3DES) or MACs (e.g., HMAC-MD5) are insecure. Enforce modern, cryptographically strong algorithms.
Update sshd_config
Add/modify these directives to restrict to strong options:
# Ciphers: Prioritize ChaCha20 (for low-power devices) and AES-GCM
Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr
# Key Exchanges: Avoid SHA1-based KEX (e.g., diffie-hellman-group1-sha1)
KexAlgorithms [email protected],diffie-hellman-group-exchange-sha256
# MACs: Use SHA256/512 and avoid HMAC-MD5
MACs [email protected],[email protected],[email protected]
2.6 Enhance Logging and Monitoring
Log SSH activity to detect suspicious behavior (e.g., repeated failed logins).
Configure Logging in sshd_config
LogLevel VERBOSE # Log detailed info (e.g., user, IP, key used)
SyslogFacility AUTH # Send logs to the "AUTH" facility
View Logs
Logs are stored in:
- Debian/Ubuntu:
/var/log/auth.log - RHEL/CentOS:
/var/log/secure
Example:
grep "sshd" /var/log/auth.log # Filter SSH logs
Block Brute-Force Attacks with fail2ban
fail2ban automatically blocks IPs with repeated failed login attempts:
Install fail2ban:
# Ubuntu/Debian
sudo apt install fail2ban
# RHEL/CentOS
sudo dnf install fail2ban
Configure fail2ban:
Edit /etc/fail2ban/jail.local (create if missing):
[sshd]
enabled = true
port = 2222 # Use your custom SSH port
filter = sshd
logpath = /var/log/auth.log # Path to your SSH logs
maxretry = 3 # Block after 3 failed attempts
bantime = 3600 # Block for 1 hour (3600 seconds)
Restart fail2ban:
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban # Start on boot
2.7 Implement Firewall Rules
Restrict SSH access to trusted IPs/subnets (e.g., your home/office network) using a firewall.
Example with UFW
# Allow SSH only from 192.168.1.0/24 (local subnet) and 203.0.113.45 (remote IP)
sudo ufw allow from 192.168.1.0/24 to any port 2222/tcp
sudo ufw allow from 203.0.113.45 to any port 2222/tcp
# Deny all other SSH traffic
sudo ufw deny 2222/tcp
# Enable UFW (if not already enabled)
sudo ufw enable
2.8 Enable Two-Factor Authentication (2FA)
Add an extra layer of security with 2FA (e.g., TOTP codes via Google Authenticator).
Step 1: Install Google Authenticator PAM Module
# Ubuntu/Debian
sudo apt install libpam-google-authenticator
# RHEL/CentOS
sudo dnf install google-authenticator
Step 2: Configure 2FA for a User
Run google-authenticator as the user who needs 2FA:
google-authenticator
- Answer “y” to all prompts (enable time-based tokens, save key, etc.).
- Scan the QR code with Google Authenticator (or similar app) to generate codes.
Step 3: Update sshd_config
Enable PAM-based 2FA:
ChallengeResponseAuthentication yes
UsePAM yes
Update PAM SSH configuration (/etc/pam.d/sshd):
sudo nano /etc/pam.d/sshd
Add this line (comment out other @include lines if needed to avoid conflicts):
auth required pam_google_authenticator.so
2.9 Disable Unused Features
Disable rarely used SSH features to reduce attack surface:
X11Forwarding no # Disable GUI forwarding (if not needed)
AllowTcpForwarding no # Block port forwarding (enable selectively with "yes" or "local")
AgentForwarding no # Disable SSH agent forwarding (risk of key theft)
PermitTunnel no # Disable VPN-like tunnels
Compression no # Disable compression (potential side-channel risks)
2.10 Keep OpenSSH Updated
Regularly update OpenSSH to patch vulnerabilities (e.g., CVE-2024-6387):
# Ubuntu/Debian
sudo apt update && sudo apt upgrade openssh-server
# RHEL/CentOS
sudo dnf update openssh-server
3. Testing Your Hardened Configuration
After making changes, validate the configuration to avoid locking yourself out:
Step 1: Check for Syntax Errors
sudo sshd -t # Test sshd_config for errors
Step 2: Restart the SSH Service
# Systemd-based systems (Ubuntu 16.04+, RHEL 7+)
sudo systemctl restart sshd
# SysVinit (older systems)
sudo service ssh restart
Step 3: Test Login from a New Terminal
Always test with a second terminal session to ensure you can still connect. If you’re locked out, use a local console (e.g., physical access or IPMI) to revert changes.
4. Common Pitfalls & Troubleshooting
- Locked Out After Disabling Passwords? Re-enable password auth temporarily via the console:
sudo nano /etc/ssh/sshd_config PasswordAuthentication yes sudo systemctl restart sshd - 2FA Not Working? Ensure
ChallengeResponseAuthentication yesand PAM is configured correctly. - Firewall Blocking Access? Verify rules with
sudo ufw statusorsudo iptables -L.
5. Conclusion
Hardening OpenSSH is critical for securing remote access to Linux systems. By following these steps—using SSH keys, limiting access, enforcing strong cryptography, and monitoring for threats—you significantly reduce the risk of breaches. Remember, security is ongoing: regularly audit your configuration, update OpenSSH, and stay informed about new vulnerabilities.