HxHippy

SSH Security Best Practices

Secure your SSH server and client configuration. Key-based auth, hardening, and tunnels.

Last updated: 2024-12-18

SSH is the primary way to remotely access Linux systems. Proper configuration is critical for security.

SSH Key Authentication

Generate SSH Keys

# Generate Ed25519 key (recommended)
ssh-keygen -t ed25519 -C "[email protected]"

# Generate RSA key (4096 bits)
ssh-keygen -t rsa -b 4096 -C "[email protected]"

# Generate with specific filename
ssh-keygen -t ed25519 -f ~/.ssh/work_key -C "work"

# Key files created:
# ~/.ssh/id_ed25519       (private key - keep secret!)
# ~/.ssh/id_ed25519.pub   (public key - share this)

Copy Key to Server

# Best method (creates .ssh dir, sets permissions)
ssh-copy-id user@server

# With specific key
ssh-copy-id -i ~/.ssh/work_key.pub user@server

# Manual method
cat ~/.ssh/id_ed25519.pub | ssh user@server 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'

Key File Permissions

# Set proper permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keys
chmod 644 ~/.ssh/known_hosts
chmod 600 ~/.ssh/config

SSH Server Hardening

sshd_config Best Practices

# Edit: /etc/ssh/sshd_config

# Change default port (security through obscurity)
Port 22022

# Disable root login
PermitRootLogin no

# Disable password authentication
PasswordAuthentication no

# Disable empty passwords
PermitEmptyPasswords no

# Enable public key authentication
PubkeyAuthentication yes

# Specify allowed users
AllowUsers alice bob
# or allowed groups
AllowGroups sshusers admins

# Disable X11 forwarding (if not needed)
X11Forwarding no

# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2

# Limit authentication attempts
MaxAuthTries 3

# Disable unused authentication methods
ChallengeResponseAuthentication no
KerberosAuthentication no
GSSAPIAuthentication no

# Use strong ciphers and MACs
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected]
KexAlgorithms curve25519-sha256,[email protected],diffie-hellman-group16-sha512

Apply Changes

# Test configuration
sudo sshd -t

# Reload SSH (keep existing connections)
sudo systemctl reload sshd

# Restart SSH (closes all connections)
sudo systemctl restart sshd

SSH Client Configuration

~/.ssh/config

# Default settings
Host *
    AddKeysToAgent yes
    IdentitiesOnly yes
    ServerAliveInterval 60
    ServerAliveCountMax 3

# Work server
Host work
    HostName work.example.com
    User alice
    Port 22022
    IdentityFile ~/.ssh/work_key

# Jump host (bastion)
Host bastion
    HostName bastion.example.com
    User admin
    IdentityFile ~/.ssh/bastion_key

# Server behind bastion
Host internal
    HostName 192.168.1.100
    User admin
    ProxyJump bastion
    IdentityFile ~/.ssh/internal_key

# Multiple servers with wildcard
Host web-*
    User deploy
    IdentityFile ~/.ssh/deploy_key

Host web-1
    HostName web1.example.com

Host web-2
    HostName web2.example.com

Connect Using Config

# Instead of: ssh -i ~/.ssh/work_key -p 22022 [email protected]
ssh work

# Jump through bastion
ssh internal

SSH Agent

# Start SSH agent
eval "$(ssh-agent -s)"

# Add key to agent
ssh-add ~/.ssh/id_ed25519

# Add with timeout (keys removed after 1 hour)
ssh-add -t 3600 ~/.ssh/id_ed25519

# List loaded keys
ssh-add -l

# Remove all keys
ssh-add -D

SSH Tunnels

Local Port Forwarding

Access remote service through local port:

# Access remote PostgreSQL (5432) via localhost:15432
ssh -L 15432:localhost:5432 user@server

# Access internal web server via SSH
ssh -L 8080:internal-web:80 user@bastion

# In background
ssh -fNL 8080:internal-web:80 user@bastion

Remote Port Forwarding

Expose local service to remote:

# Expose local port 3000 on server's port 8080
ssh -R 8080:localhost:3000 user@server

Dynamic Port Forwarding (SOCKS Proxy)

# Create SOCKS proxy on port 1080
ssh -D 1080 user@server

# Use with curl
curl --socks5 localhost:1080 http://internal-site.local

Two-Factor Authentication

Install Google Authenticator

# Install
sudo apt install libpam-google-authenticator

# Configure for user
google-authenticator
# Answer prompts, save backup codes

# Enable in PAM
# Edit /etc/pam.d/sshd, add:
auth required pam_google_authenticator.so

# Enable in SSH
# Edit /etc/ssh/sshd_config:
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

Fail2Ban for SSH

# Install
sudo apt install fail2ban

# Configure
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

# Edit /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 22022
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

# Start
sudo systemctl enable --now fail2ban

# Check status
sudo fail2ban-client status sshd

# Unban IP
sudo fail2ban-client set sshd unbanip 192.168.1.100

SSH Troubleshooting

# Verbose output
ssh -v user@server    # Level 1
ssh -vv user@server   # Level 2
ssh -vvv user@server  # Level 3

# Test connection
ssh -T [email protected]

# Check server authentication
ssh-keyscan server.example.com

# Remove old host key
ssh-keygen -R server.example.com

# Check if port is open
nc -zv server.example.com 22

Key Revocation

If a key is compromised:

# Remove from authorized_keys on all servers
# Edit ~/.ssh/authorized_keys on each server
# Delete the line with the compromised key

# Generate new key
ssh-keygen -t ed25519 -C "new-key"

# Add new key to servers
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

# Delete old key files
rm ~/.ssh/old_key ~/.ssh/old_key.pub
intermediate Security 30 min read

Related Tutorials

sshssh keysssh securityssh hardeningopensshssh tunnel