Skip to content

SSH Hardening and Key Management

Chapter 32: SSH Hardening and Key Management

Section titled “Chapter 32: SSH Hardening and Key Management”

Mastering SSH Security for Production Environments

Section titled “Mastering SSH Security for Production Environments”

SSH is the primary access method for servers in production:

SSH for DevOps/SRE
+------------------------------------------------------------------+
| |
| Server Access: |
| +----------------------------------------------------------+ |
| | All production servers accessed via SSH | |
| | CI/CD pipelines use SSH for deployments | |
| | Ansible/Terraform use SSH for connectivity | |
| | Jump hosts/bastions → Single entry point | |
| +----------------------------------------------------------+ |
| |
| Security Hardening: |
| +----------------------------------------------------------+ |
| | Key-based auth only → No passwords | |
| | Disable root login → Prevent direct root access | |
| | Use non-standard port → Reduce attack surface | |
| | Fail2Ban → Block brute force attacks | |
| +----------------------------------------------------------+ |
| |
| Automation: |
| +----------------------------------------------------------+ |
| | SSH keys → Deploy keys via Ansible | |
| | SSH agent → Avoid re-entering passphrases | |
| | ProxyJump → Bastion host access | |
| | Config management → Centralized SSH config | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Practical Impact:

  • Secure remote access to all production systems
  • Enable secure automation without password prompts
  • Protect against brute force attacks
  • Comply with security policies (key-based auth only)

SSH Protocol Layers
+------------------------------------------------------------------+
| |
| SSH Application Layer |
| +-------------------------------------------------------------+ |
| | sftp, scp, ssh-add, ssh-keygen, ssh-agent | |
| +-------------------------------------------------------------+ |
| | |
| v |
| SSH Connection Protocol |
| +-------------------------------------------------------------+ |
| | - Channel management | |
| | - Session multiplexing | |
| | - X11 forwarding | |
| | - Agent forwarding | |
| | - TCP forwarding | |
| +-------------------------------------------------------------+ |
| | |
| v |
| SSH Authentication Protocol |
| +-------------------------------------------------------------+ |
| | - publickey (RSA, ECDSA, Ed25519) | |
| | - password | |
| | - keyboard-interactive | |
| | - hostbased | |
| +-------------------------------------------------------------+ |
| | |
| v |
| SSH Transport Layer Protocol |
| +-------------------------------------------------------------+ |
| | - Server authentication | |
| | - Key exchange (DH, ECDH) | |
| | - Encryption (AES, ChaCha20) | |
| | - MAC (HMAC, Poly1305) | |
| | - Compression (zlib) | |
| +-------------------------------------------------------------+ |
| | |
| v |
| TCP/IP Layer |
| |
+------------------------------------------------------------------+
SSH Protocol Comparison
+------------------------------------------------------------------+
| |
| SSH Version 1 |
| +----------------------------------------------------------+ |
| | - Deprecated due to security vulnerabilities | |
| | - Single encryption tunnel | |
| | - Vulnerable to man-in-the-middle attacks | |
| | - No integrity checking for channels | |
| | - Still exists on some legacy systems | |
| +----------------------------------------------------------+ |
| |
| SSH Version 2 |
| +----------------------------------------------------------+ |
| | - Current standard (RFC 4251-4256) | |
| | - Multiple encryption channels | |
| | - Strong key exchange algorithms | |
| | - Better integrity checking (MAC) | |
| | - Modular design | |
| | - Extension support | |
| +----------------------------------------------------------+ |
| |
| Key Differences: |
| +----------------------------------------------------------+ |
| | Feature | SSHv1 | SSHv2 | |
| | ---------------|------------|----------------------------| |
| | Encryption | Single | Multiple channels | |
| | Key Exchange | Fixed | Modular (diffie-hellman) | |
| | Integrity | Weak | HMAC | |
| | Security | Weak | Strong | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Terminal window
# /etc/ssh/sshd_config - Production Hardened Configuration
# =============================================================================
# SSH PROTOCOL SETTINGS
# =============================================================================
# Use only SSH Protocol 2 (mandatory for security)
Protocol 2
# Listen on specific interfaces (reduce attack surface)
ListenAddress 0.0.0.0
ListenAddress ::
# =============================================================================
# AUTHENTICATION SETTINGS
# =============================================================================
# Disable root login (critical security measure)
PermitRootLogin no
# Disable empty passwords
PermitEmptyPasswords no
# Disable password authentication (use keys only)
PasswordAuthentication no
PermitUserEnvironment no
ChallengeResponseAuthentication no
# Maximum authentication attempts
MaxAuthTries 3
MaxSessions 10
# Login grace time
LoginGraceTime 30
# Strict modes - check file permissions
StrictModes yes
# Allow specific users/groups (whitelist approach)
AllowUsers admin deployuser automation
# AllowGroups sudo developers
# =============================================================================
# KEY/BANNER SETTINGS
# =============================================================================
# Login banner
Banner /etc/ssh/banner
# Public key authentication
PubkeyAuthentication yes
# AuthorizedKeysFile location
AuthorizedKeysFile .ssh/authorized_keys
# =============================================================================
# SESSION SETTINGS
# =============================================================================
# Client alive settings (detect disconnected clients)
ClientAliveInterval 300
ClientAliveCountMax 2
# Session timeout
# Idle timeout (set in client)
# Add to client config: ServerAliveInterval 300
# =============================================================================
# FORWARDING SETTINGS
# =============================================================================
# Disable all forwarding (if not needed)
# Disable X11 forwarding (security risk)
X11Forwarding no
# Disable agent forwarding
AllowAgentForwarding no
# Disable TCP forwarding
AllowTcpForwarding no
# Disable tunnel forwarding
# PermitTunnel no
# =============================================================================
# ENCRYPTION AND HASHING
# =============================================================================
# Ciphers (order matters - put strongest first)
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
# MAC (Message Authentication Codes)
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
# Key exchange algorithms
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256
# =============================================================================
# HOSTKEY SETTINGS
# =============================================================================
# HostKeys for protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
# Use privileged port (security)
# HostKey /etc/ssh/ssh_host_key (SSHv1 - disable)
# =============================================================================
# SUBSYSTEMS
# =============================================================================
Subsystem sftp /usr/lib/ssh/sftp-server -f AUTHPRIV -l INFO
# =============================================================================
# ACCESS CONTROL
# =============================================================================
# Deny certain users
# DenyUsers baduser
# Kerberos options
KerberosAuthentication no
KerberosOrLocalPasswd yes
KerberosTicketCleanup yes
# GSSAPI options
GSSAPIAuthentication no
GSSAPICleanupCredentials yes
# =============================================================================
# LOGGING
# =============================================================================
# Log level
LogLevel VERBOSE
# =============================================================================
# ENVIRONMENT
# =============================================================================
# Accept environment variables from client (disable for security)
AcceptEnv LANG LC_*
# =============================================================================
# PAM SETTINGS
# =============================================================================
UsePAM yes
# =============================================================================
# OTHER SETTINGS
# =============================================================================
# Print last login info
PrintLastLog yes
# Print MOTD
PrintMotd no
# Handle deprecated options
# IgnoreRhosts yes
# RhostsRSAAuthentication no
# HostbasedAuthentication no
# Allow shared connections
# ControlMaster auto
# ControlPath /tmp/ssh_mux_%h_%p
# ControlPersist 4h
/etc/ssh/banner
+------------------------------------------------------------------+
| |
| AUTHORIZED ACCESS ONLY |
| |
| If you are not authorized to access or use this system, |
| disconnect immediately. |
| |
| All connections are logged and monitored. |
| Unauthorized access is prohibited and will be prosecuted. |
| |
| By using this system, you consent to these terms. |
| |
+------------------------------------------------------------------+

SSH Key Types Comparison
+------------------------------------------------------------------+
| |
| RSA (Rivest-Shamir-Adleman) |
| +----------------------------------------------------------+ |
| | Key Sizes: 1024, 2048, 3072, 4096 bits | |
| | Compatibility: Excellent (all SSH clients) | |
| | Security: Good (2048+ bits recommended) | |
| | Performance: Slower than Ed25519 | |
| | Use Case: Legacy systems, compatibility | |
| +----------------------------------------------------------+ |
| |
| ECDSA (Elliptic Curve DSA) |
| +----------------------------------------------------------+ |
| | Key Sizes: 256, 384, 521 bits | |
| | Compatibility: Good (modern clients) | |
| | Security: Good (depends on curve) | |
| | Performance: Faster than RSA | |
| | Use Case: Balanced performance/security | |
| | Concerns: NIST curves (potential backdoor concerns) | |
| +----------------------------------------------------------+ |
| |
| Ed25519 (Edwards-curve DSA) |
| +----------------------------------------------------------+ |
| | Key Size: 256 bits (fixed) | |
| | Compatibility: Very Good (modern clients) | |
| | Security: Excellent (modern, well-reviewed) | |
| | Performance: Fastest | |
| | Key Size: Smallest (37 bytes public, 64 bytes priv) | |
| | Use Case: RECOMMENDED for new deployments | |
| +----------------------------------------------------------+ |
| |
| Ed448 (Edwards-curve DSA) |
| +----------------------------------------------------------+ |
| | Key Size: 448 bits (fixed) | |
| | Compatibility: Limited (newer) | |
| | Security: Highest (largest key) | |
| | Performance: Very fast | |
| | Use Case: Maximum security requirements | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
Terminal window
# =============================================================================
# ED25519 KEY (RECOMMENDED)
# =============================================================================
# Generate Ed25519 key with comment
ssh-keygen -t ed25519 -C "work@company.com - Production"
# Ed25519 with custom filename
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_production -C "production-server"
# Ed25519 with passphrase (REQUIRED for production)
ssh-keygen -t ed25519 -C "production" -a 100
# -a rounds: Key derivation function iterations (more = slower but secure)
# =============================================================================
# RSA KEY (FOR LEGACY COMPATIBILITY)
# =============================================================================
# RSA 4096-bit key
ssh-keygen -t rsa -b 4096 -C "legacy@company.com"
# RSA with more KDF rounds (slower but secure)
ssh-keygen -t rsa -b 4096 -a 100 -C "production"
# =============================================================================
# ED448 KEY (MAXIMUM SECURITY)
# =============================================================================
ssh-keygen -t ed448 -C "maximum-security"
# =============================================================================
# VIEW KEY DETAILS
# =============================================================================
# View public key
cat ~/.ssh/id_ed25519.pub
# View key fingerprint
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# View key fingerprint (MD5 - useful for comparing)
ssh-keygen -lf ~/.ssh/id_ed25519.pub -E md5
# View key comments
ssh-keygen -y -f ~/.ssh/id_ed25519
# =============================================================================
# KEY MANAGEMENT
# =============================================================================
# Change passphrase
ssh-keygen -p -f ~/.ssh/id_ed25519
# Change comment
ssh-keygen -c -f ~/.ssh/id_ed25519 -C "new-comment"
# Generate public key from private key
ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
Terminal window
# =============================================================================
# METHOD 1: ssh-copy-id (Simplest)
# =============================================================================
# Standard copy
ssh-copy-id user@server
# Copy specific key
ssh-copy-id -i ~/.ssh/id_ed25519_production.pub user@server
# =============================================================================
# METHOD 2: Manual Copy
# =============================================================================
# Create .ssh directory and set permissions
ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh"
# Copy public key
cat ~/.ssh/id_ed25519.pub | ssh user@server "cat >> ~/.ssh/authorized_keys"
# Set correct permissions
ssh user@server "chmod 600 ~/.ssh/authorized_keys && chmod 700 ~/.ssh"
# =============================================================================
# METHOD 3: Using Ansible
# =============================================================================
ansible all -m authorized_key -a "user=deploy key={{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
# =============================================================================
# METHOD 4: SSH Key Deployment (Restricted)
# =============================================================================
# Create restricted authorized_keys
# Force specific options per key
# Command restriction (user can only run specific commands)
command="/usr/local/bin/deploy.sh",no-pty,no-agent-forwarding,no-X11-forwarding ssh-rsa AAAA... user@host
# From specific IP only
from="192.168.1.100" ssh-rsa AAAA... user@host
# Multiple restrictions
from="192.168.1.0/24",command="/usr/bin/git-shell" ssh-rsa AAAA... git@host

SSH Agent Architecture
+------------------------------------------------------------------+
| |
| SSH Agent: In-memory key manager for SSH authentication |
| |
| +----------------------------------------------------------+ |
| | ssh-agent (runs as background process) | |
| | +------------------------------------------------------+ | |
| | | - Holds decrypted private keys in memory | | |
| | | - Communicates with SSH client via socket | | |
| | | - Keys never written to disk in decrypted form | | |
| | +------------------------------------------------------+ | |
| +----------------------------------------------------------+ |
| |
| Security Concerns: |
| +----------------------------------------------------------+ |
| | 1. Keys in memory can be extracted (cold boot attack) | |
| | 2. Agent forwarding exposes keys to remote servers | |
| | 3. Agent can be hijacked if socket is compromised | |
| | 4. Lock agent when leaving workstation | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
Terminal window
# =============================================================================
# START SSH AGENT
# =============================================================================
# Start agent and set environment
eval "$(ssh-agent -s)"
# Or start agent in background
ssh-agent -s > /tmp/ssh-agent-env
source /tmp/ssh-agent-env
# Check if agent is running
echo $SSH_AUTH_SOCK
# =============================================================================
# ADD KEYS TO AGENT
# =============================================================================
# Add key with default settings
ssh-add
# Add specific key
ssh-add ~/.ssh/id_ed25519_production
# Add key with time limit (5 hours)
ssh-add -t 18000 ~/.ssh/id_ed25519
# Add with confirmation (macOS)
ssh-add -K ~/.ssh/id_ed25519
# =============================================================================
# LIST KEYS
# =============================================================================
# List all keys in agent
ssh-add -l
# List with fingerprints
ssh-add -l -E md5
ssh-add -l -E sha256
# List all (including those not in agent but with keys available)
ssh-add -L
# =============================================================================
# REMOVE KEYS
# =============================================================================
# Remove specific key
ssh-add -d ~/.ssh/id_ed25519
# Remove all keys
ssh-add -D
# Remove keys with timeout
ssh-add -t 300 -d ~/.ssh/id_ed25519
# =============================================================================
# LOCK/UNLOCK AGENT
# =============================================================================
# Lock agent with password
ssh-add -x
# Unlock agent
ssh-add -X
# =============================================================================
# AGENT FORWARDING
# =============================================================================
# In SSH config (client)
Host remote-server
HostName server.example.com
ForwardAgent yes
# On command line
ssh -A user@server
# CAUTION: Agent forwarding allows remote server to use your keys
# Only use with trusted servers

Terminal window
# ~/.ssh/config - Production SSH Client Configuration
# =============================================================================
# GLOBAL DEFAULTS
# =============================================================================
Host *
# Security defaults
PasswordAuthentication no
PubkeyAuthentication yes
IdentitiesOnly yes
# Connection settings
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
# Forwarding
ForwardAgent no
ForwardX11 no
ForwardX11Trusted no
# Security
HashKnownHosts yes
VerifyHostKeyDNS yes
StrictHostKeyChecking ask
# Performance
Compression yes
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# Logging
LogLevel ERROR
# =============================================================================
# PRODUCTION SERVERS
# =============================================================================
Host prod-*
User admin
Port 22
IdentityFile ~/.ssh/id_ed25519_production
ServerAliveInterval 30
ServerAliveCountMax 5
Host prod-web-1
HostName 192.168.1.10
Host prod-web-*
Host prod-web-2
HostName 192.168.1.11
Host prod-web-*
Host prod-db-1
HostName 192.168.1.20
User dbadmin
# =============================================================================
# DEVELOPMENT SERVERS
# =============================================================================
Host dev-*
User developer
Port 22
IdentityFile ~/.ssh/id_ed25519_dev
StrictHostKeyChecking no
Host dev-server
HostName dev.example.com
# =============================================================================
# JUMP HOST / BASTION
# =============================================================================
Host bastion
HostName bastion.example.com
User admin
Port 22
IdentityFile ~/.ssh/id_ed25519_bastion
ForwardAgent yes
Host internal-server
HostName 192.168.1.100
User admin
ProxyJump bastion
# Alternative using ProxyCommand
Host internal-server-alt
HostName 192.168.1.100
User admin
ProxyCommand ssh -W %h:%p bastion
# =============================================================================
# GITHUB / GIT SERVICES
# =============================================================================
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
Host gitlab.com
HostName gitlab.com
User git
IdentityFile ~/.ssh/id_ed25519_gitlab
IdentitiesOnly yes
# =============================================================================
# SPECIAL CONNECTIONS
# =============================================================================
# High latency connections
Host overseas-server
HostName server.example.com
Compression yes
Cipher aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com
# =============================================================================
# CREATE SOCKET DIRECTORY
# =============================================================================
# Run this once:
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets

Fail2Ban Architecture
+------------------------------------------------------------------+
| |
| +---------------+ |
| | Log Files | |
| | (/var/log/) | |
| +---------------+ |
| | |
| v |
| +---------------+ |
| | Fail2Ban | |
| | Server | |
| +---------------+ |
| | |
| +------+------+-------+------+ |
| | | | | | |
| v v v v v |
| +------+------+-------+------+------+ |
| |Filter|Filter|Filter |Filter |Filter | (Regex patterns) |
| |sshd |nginx |apache |postfix|mysql | |
| +------+------+-------+------+------+ |
| | | | | | |
| v v v v v |
| +------+------+-------+------+------+ |
| |Action|Action|Action |Action |Action | (Ban/Unban actions) |
| |iptabl|firew |abuse |notify |cloud | |
| +------+------+-------+------+------+ |
| |
+------------------------------------------------------------------+
Terminal window
# /etc/fail2ban/jail.local - Production Configuration
[DEFAULT]
# Global settings
bantime = 1h
findtime = 10m
maxretry = 5
destemail = admin@example.com
sender = fail2ban@example.com
action = %(action_mwl)s
# Override action defaults
banaction = iptables-multiport
banaction_allports = iptables-allports
# =============================================================================
# SSH JAIL
# =============================================================================
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
findtime = 1h
# Using recidive jail for repeat offenders
[sshd-ddos]
enabled = true
port = ssh
filter = sshd-ddos
logpath = /var/log/auth.log
maxretry = 20
bantime = 1w
findtime = 1d
# =============================================================================
# WEB SERVER JAILS
# =============================================================================
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 1h
[nginx-noscript]
enabled = true
port = http,https
filter = nginx-noscript
logpath = /var/log/nginx/access.log
maxretry = 6
bantime = 1h
[nginx-badrequests]
enabled = true
port = http,https
filter = nginx-badrequests
logpath = /var/log/nginx/access.log
maxretry = 3
bantime = 1h
[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/apache2/error.log
maxretry = 5
[apache-badbots]
enabled = true
port = http,https
filter = apache-badbots
logpath = /var/log/apache2/access.log
maxretry = 2
[apache-noscript]
enabled = true
port = http,https
filter = apache-noscript
logpath = /var/log/apache2/access.log
maxretry = 6
# =============================================================================
# DATABASE JAILS
# =============================================================================
[mysqld]
enabled = true
port = 3306
filter = mysqld-auth
logpath = /var/log/mysql/error.log
maxretry = 5
bantime = 1h
[postgresql]
enabled = true
port = 5432
filter = postgresql
logpath = /var/log/postgresql/postgresql.log
maxretry = 5
# =============================================================================
# MAIL SERVER JAILS
# =============================================================================
[postfix]
enabled = true
port = smtp,submission,imaps
filter = postfix
logpath = /var/log/mail.log
maxretry = 5
bantime = 1h
[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps
filter = dovecot
logpath = /var/log/mail.log
maxretry = 5
# =============================================================================
# FTP JAILS
# =============================================================================
[vsftpd]
enabled = true
port = ftp,ftp-data,ftps,ftps-data
filter = vsftpd
logpath = /var/log/vsftpd.log
maxretry = 5
bantime = 1h
# =============================================================================
# CUSTOM FILTERS
# =============================================================================
# Example: Protect API endpoints
[nginx-api]
enabled = true
port = http,https
filter = nginx-api
logpath = /var/log/nginx/api-access.log
maxretry = 10
bantime = 30m
findtime = 1m
# Create /etc/fail2ban/filter.d/nginx-api.conf
#[Definition]
#failregex = ^<HOST> .* "POST /api/.*" HTTP/1.1" (401|403|500|502|503|504)
#ignoreregex =
Terminal window
# =============================================================================
# MONITORING
# =============================================================================
# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd
# View banned IPs
sudo iptables -L -n
sudo fail2ban-client get sshd banned
# View logs
tail -f /var/log/fail2ban/fail2ban.log
# =============================================================================
# BANNING/UNBANNING
# =============================================================================
# Ban IP manually
sudo fail2ban-client set sshd banip 192.168.1.100
# Unban IP
sudo fail2ban-client set sshd unbanip 192.168.1.100
# Unban all
sudo fail2ban-client unban --all
# =============================================================================
# TESTING FILTERS
# =============================================================================
# Test regex against log
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
# Test with custom log
echo "Failed password for invalid user admin from 192.168.1.100" | \
fail2ban-regex /dev/stdin /etc/fail2ban/filter.d/sshd.conf

Important

  1. SSH Protocol: Always use SSHv2 (Protocol 2)
  2. Key Types: Ed25519 is recommended (fastest, most secure)
  3. Passphrases: Always use passphrases for production keys
  4. Root Login: Always disable PermitRootLogin in production
  5. Password Auth: Disable PasswordAuthentication in production
  6. Port: Change default port 22 (security through obscurity)
  7. Fail2Ban: Configure for SSH and web services
  8. Key Deployment: Use authorized_keys with restrictions
  9. Agent Forwarding: Use carefully, only with trusted servers
  10. Client Config: Use ~/.ssh/config for organization

Terminal window
# ❌ WRONG: Enabling password authentication
# In /etc/ssh/sshd_config:
PasswordAuthentication yes
PermitRootLogin yes
# ✅ CORRECT: Key-based auth only
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
Terminal window
# ❌ WRONG: Using weak key types
ssh-keygen -t rsa -b 2048 # Too small now
ssh-keygen -t dsa # Deprecated!
ssh-keygen -t ecdsa -b 256 # Too small
# ✅ CORRECT: Use strong key types
ssh-keygen -t ed25519 -a 100 # Recommended!
ssh-keygen -t rsa -b 4096 # If Ed25519 not supported
Terminal window
# ❌ WRONG: Leaving SSH agent unprotected
ssh-add # Adds all keys without passphrase
# Keys with empty passphrases are vulnerable
# ✅ CORRECT: Use ssh-agent with passphrase
ssh-agent bash
ssh-add # Enter passphrase for each key
# Or use keychain
source ~/.keychain/$HOSTNAME-sh
Terminal window
# ❌ WRONG: Insecure SSH client config
# ~/.ssh/config:
Host *
ServerAliveInterval 0
StrictHostKeyChecking no # Accepts any host key!
# ✅ CORRECT: Secure SSH config
Host *
ServerAliveInterval 60
StrictHostKeyChecking ask
IdentityFile ~/.ssh/id_ed25519
AddKeysToAgent yes

  1. What is the difference between SSH version 1 and 2?
  2. How do you harden SSH configuration for production?
  3. What are the advantages of Ed25519 over RSA keys?
  4. How does Fail2Ban protect SSH?
  5. Explain SSH agent forwarding and its security implications
  6. What is ProxyJump and how is it used?

In this chapter, you learned:

  • ✅ SSH protocol architecture (transport, authentication, connection)
  • ✅ Comprehensive sshd_config hardening
  • ✅ SSH key types (RSA, ECDSA, Ed25519, Ed448)
  • ✅ SSH key generation and deployment
  • ✅ SSH agent management and security
  • ✅ SSH client configuration best practices
  • ✅ Fail2Ban architecture and configuration
  • ✅ SSH security best practices for production

Chapter 33: Intrusion Detection and Fail2Ban


Last Updated: February 2026