Linux SSH Security and Hardening
# Linux SSH Security and Hardening ## Introduction SSH (Secure Shell) is the primary method for remote Linux administration. By default, SSH installations often use insecure configurations that are
# Linux SSH Security and Hardening
Introduction
SSH (Secure Shell) is the primary method for remote Linux administration. By default, SSH installations often use insecure configurations that are vulnerable to brute-force attacks, credential theft, and unauthorized access. Hardening SSH is one of the most important security measures for any Linux server.
This tutorial covers SSH security best practices including key-based authentication, disabling root login, changing the default port, implementing fail2ban, configuring SSH banners, two-factor authentication, and ongoing SSH monitoring.
By the end of this guide, you will be able to secure SSH access on Linux servers and reduce the risk of remote compromise.
Technical Background
SSH Protocol
SSH provides encrypted remote access using:
- **Public-key cryptography**: Host authentication and user authentication
- **Symmetric encryption**: Encrypting the session data
- **Message authentication codes**: Ensuring data integrity
- **Key exchange**: Negotiating session keys securely
SSH Authentication Methods
| Method | Security | Description | |--------|-----------|-------------| | Password | Low | Vulnerable to brute-force and credential stuffing | | Public Key | High | Cryptographic key pairs; resistant to brute-force | | Host-based | Medium | Based on trusted hosts (less common) | | Certificate | Very High | SSH certificates with CA verification | | MFA/2FA | Very High | Multiple authentication factors |
SSH Configuration Files
| File | Purpose | |------|---------| | /etc/ssh/sshd_config | SSH daemon (server) configuration | | /etc/ssh/ssh_config | SSH client configuration | | ~/.ssh/authorized_keys | Authorized public keys for user | | ~/.ssh/id_rsa / id_ed25519 | User private key | | ~/.ssh/id_rsa.pub / id_ed25519.pub | User public key | | /etc/ssh/ssh_host_* | Host key pairs | | /etc/ssh/ssh_known_hosts | Known host keys |
Prerequisites
- Linux system with root or sudo access
- SSH server installed and running
- Client machine with SSH client
- Understanding of public-key cryptography concepts
Architecture
SSH Connection Flow
Client Server
───────── ─────────
1. TCP connection to port 22
2. Server presents host key
3. Client verifies host key
4. Key exchange (session keys)
5. Client authentication (password or public key)
6. Encrypted session establishedsequenceDiagram
participant C as Client
participant S as SSH Server
C->>S: TCP SYN (port 22)
S->>C: TCP SYN-ACK
C->>S: TCP ACK
S->>C: SSH version + host key
C->>S: Verify host key
C->>S: Key exchange (Diffie-Hellman)
S->>C: Session keys established
C->>S: Authenticate (public key/password)
S->>C: Authentication success
C->>S: Encrypted shell sessionStep-by-Step Implementation
Step 1: Install and Verify SSH Server
**Purpose**: Ensure OpenSSH server is installed and running.
**Commands**:
# Install OpenSSH server (Ubuntu/Debian)
sudo apt update
sudo apt install openssh-server
# Install OpenSSH server (RHEL/CentOS/Fedora)
sudo dnf install openssh-server
# Check SSH service status
sudo systemctl status ssh
# or
sudo systemctl status sshd
# Enable SSH to start on boot
sudo systemctl enable ssh
# Start SSH service
sudo systemctl start ssh**Expected output**: SSH service active and listening on port 22.
**Verification**:
sudo systemctl status ssh
# Shows: Active: active (running)
sudo ss -tlnp | grep 22
# Shows: LISTEN 0 128 *:22 *:... users:(("sshd",...))Step 2: Generate SSH Key Pair
**Purpose**: Create public/private key pair for key-based authentication.
**Commands** (on client machine):
# Generate Ed25519 key pair (recommended)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "user@ityantra.lab"
# Or RSA with 4096 bits
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -C "user@ityantra.lab"
# Set restrictive permissions on private key
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub**Expected output**: Key pair created in ~/.ssh/ directory.
**Verification**:
ls -la ~/.ssh/
# Shows id_ed25519 (private) and id_ed25519.pub (public)Step 3: Copy Public Key to Server
**Purpose**: Install client's public key on the server for authentication.
**Commands**:
# Copy public key to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip
# Or manually append to authorized_keys
cat ~/.ssh/id_ed25519.pub | ssh user@server-ip "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# Set permissions on server
ssh user@server-ip "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"**Expected output**: Public key added to server's authorized_keys file.
**Verification**:
# Test key-based authentication (should not require password)
ssh -i ~/.ssh/id_ed25519 user@server-ipStep 4: Configure SSH Daemon for Security
**Purpose**: Harden SSH daemon configuration.
**Commands**:
# Backup original configuration
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
# Edit SSH daemon configuration
sudo nano /etc/ssh/sshd_config**Hardened /etc/ssh/sshd_config**:
# Port and listen
Port 2222
AddressFamily inet
ListenAddress 0.0.0.0
# Host keys
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# Logging
SyslogFacility AUTH
LogLevel VERBOSE
# Authentication
LoginGraceTime 60
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 10
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
# Allow specific users only
AllowUsers admin deploy
# X11 and TCP forwarding
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
# Client alive interval
ClientAliveInterval 300
ClientAliveCountMax 3
# Banner
Banner /etc/ssh/banner**Apply configuration**:
# Test configuration
sudo sshd -t
# Restart SSH service
sudo systemctl restart ssh**Warning**: Before disabling password authentication, ensure your SSH key is working. Test in a separate session before closing your current session.
**Expected output**: SSH daemon running with hardened configuration.
**Verification**:
sudo sshd -t
# No output means configuration is valid
sudo systemctl status ssh
# Shows active and runningStep 5: Create SSH Warning Banner
**Purpose**: Display legal warning banner before authentication.
**Commands**:
# Create banner file
sudo nano /etc/ssh/banner**Example banner content**:
**************************************************************************
WARNING: This system is for authorized use only.
All activities on this system are monitored and recorded.
Unauthorized access is prohibited and may be subject to criminal prosecution.
By accessing this system, you consent to monitoring and recording.
****************************************************************************Expected output**: Banner displayed before login prompt.
**Verification**:
ssh user@server-ip
# Banner displayed before authentication promptStep 6: Install and Configure Fail2Ban
**Purpose**: Protect against brute-force attacks by banning malicious IPs.
**Commands**:
# Install fail2ban (Ubuntu/Debian)
sudo apt install fail2ban
# Install fail2ban (RHEL/CentOS/Fedora)
sudo dnf install fail2ban
# Create local configuration
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit jail.local
sudo nano /etc/fail2ban/jail.local**Example fail2ban configuration**:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
backend = systemd
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 1h**Apply configuration**:
# Enable and start fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd**Expected output**: Fail2ban running; malicious IPs banned after failed attempts.
**Verification**:
sudo fail2ban-client status sshd
# Shows banned IPs and currently failed attemptsStep 7: Implement Two-Factor Authentication (Optional)
**Purpose**: Add second authentication factor for SSH.
**Commands**:
# Install Google Authenticator PAM module (Ubuntu/Debian)
sudo apt install libpam-google-authenticator
# Run Google Authenticator for user
google-authenticator
# Follow prompts to generate QR code and secret key
# Save emergency scratch codes
# Edit PAM configuration for SSH
sudo nano /etc/pam.d/sshd
# Add at top of auth section:
auth required pam_google_authenticator.so
# Edit SSH configuration to enable challenge-response
sudo nano /etc/ssh/sshd_config
# Set: ChallengeResponseAuthentication yes
# Or: KbdInteractiveAuthentication yes
# Restart SSH
sudo systemctl restart ssh**Expected output**: SSH requires both key/password and verification code.
**Verification**:
ssh user@server-ip
# Prompted for verification code after key/passwordConfiguration Examples
Complete Hardened sshd_config
# /etc/ssh/sshd_config - Hardened Configuration
# Network
Port 2222
AddressFamily inet
ListenAddress 0.0.0.0
# Host Keys (only modern algorithms)
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# Logging
SyslogFacility AUTH
LogLevel VERBOSE
# Authentication
LoginGraceTime 60
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxStartups 10:30:100
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
# Access Control
AllowUsers admin deploy
DenyUsers test guest
# Session
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
PermitUserRC no
# Keepalive
ClientAliveInterval 300
ClientAliveCountMax 3
# Banner
Banner /etc/ssh/banner
# Subsystem
Subsystem sftp /usr/lib/openssh/sftp-serverSSH Client Configuration (`~/.ssh/config`)
Host server01
HostName 192.168.1.10
Port 2222
User admin
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3
Host *.ityantra.lab
User admin
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yesSSH Key Generation with Specific Options
# Generate key with passphrase
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "admin@ityantra.lab" -o -a 100
# Copy key to multiple servers
for host in server01 server02 server03; do
ssh-copy-id -i ~/.ssh/id_ed25519.pub admin@$host
doneVerification
SSH Security Verification Commands
# Test SSH configuration
sudo sshd -t
# Check SSH service
sudo systemctl status ssh
# Check listening port
sudo ss -tlnp | grep 22
# Test key-based authentication
ssh -v user@server-ip
# Verify root login disabled
ssh root@server-ip # Should fail
# Verify password authentication disabled
ssh -o PubkeyAuthentication=no user@server-ip # Should fail
# Check fail2ban status
sudo fail2ban-client status sshd
# View SSH logs
sudo tail -f /var/log/auth.log
# or
sudo journalctl -u ssh -fExpected Results
- `sshd -t` returns no errors
- SSH listens on configured port (not 22 if changed)
- Key-based authentication succeeds
- Password authentication fails
- Root login denied
- Fail2ban bans IPs after maxretry attempts
Troubleshooting
| Symptom | Possible Cause | Diagnostic Command | Corrective Action | Verification | |---------|---------------|-------------------|-------------------|--------------| | Cannot connect to SSH | Service not running or firewall | `sudo systemctl status ssh` | Start service; open firewall port | Connection succeeds | | Key authentication fails | Wrong key or permissions | `ssh -v user@host` | Fix key permissions; verify authorized_keys | Key auth works | | Permission denied | authorized_keys wrong permissions | `ls -la ~/.ssh/` | chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys | Auth succeeds | | Root login still allowed | PermitRootLogin not set to no | `grep PermitRootLogin /etc/ssh/sshd_config` | Set PermitRootLogin no; restart sshd | Root login denied | | Password auth still enabled | PasswordAuthentication not set to no | `grep PasswordAuthentication /etc/ssh/sshd_config` | Set PasswordAuthentication no; restart sshd | Password auth denied | | Fail2ban not banning | Wrong log path or filter | `sudo fail2ban-client status sshd` | Fix logpath in jail.local | IPs banned after failures | | SSH slow to connect | DNS lookups enabled | `grep UseDNS /etc/ssh/sshd_config` | Set UseDNS no | Faster connection | | Locked out after config change | Syntax error or wrong settings | Access via console | Fix sshd_config; restart sshd | Access restored |
Best Practices
1. **Use key-based authentication**: Disable password authentication entirely. 2. **Disable root login**: Use a regular user account with sudo instead. 3. **Use Ed25519 keys**: More secure and faster than RSA. 4. **Protect private keys**: chmod 600 on private keys; use passphrases. 5. **Use non-standard port**: Reduces automated attack noise (not security by itself). 6. **Limit allowed users**: Use AllowUsers to restrict SSH access. 7. **Install fail2ban**: Blocks brute-force attack sources. 8. **Monitor SSH logs**: Alert on failed authentication attempts. 9. **Use SSH certificates**: For large environments, use SSH CA for key management. 10. **Test before closing session**: Always test new SSH config in a separate session.
Conclusion
SSH hardening is critical for Linux server security. By implementing key-based authentication, disabling root login and password authentication, using non-standard ports, installing fail2ban, and configuring warning banners, administrators significantly reduce the attack surface. Regular monitoring of SSH logs and periodic review of authorized keys ensures ongoing security. Combined with firewall rules and system updates, hardened SSH provides secure remote administration for Linux servers.
References
- OpenSSH Manual (https://man.openbsd.org/sshd_config)
- SSH Hardening Guide (https://wiki.archlinux.org/title/Secure_Shell)
- Fail2Ban Documentation (https://www.fail2ban.org/)
- NIST SP 800-53 - Access Control (https://csrc.nist.gov/)
- CIS Benchmarks for Linux (https://www.cisecurity.org/)