SSH (Secure Shell) remains the primary method for remote Linux server management, making it one of the most targeted attack vectors. In 2026, with automated credential-stuffing botnets scanning millions of IPs daily, properly securing SSH is not optional — it is essential for server survival.
This guide covers every aspect of SSH security, from basic hardening through advanced techniques like SSH certificates and port knocking.
1. Key-Based Authentication
The first and most critical step: disable password authentication entirely and use SSH keys.
# Generate a strong Ed25519 key pair
ssh-keygen -t ed25519 -a 100 -C "admin@company.com"
# Or RSA with 4096 bits if Ed25519 is not supported
ssh-keygen -t rsa -b 4096 -a 100 -C "admin@company.com"
# Copy public key to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# Disable password authentication on server
sudo sed -i "s/^#*PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config
sudo sed -i "s/^#*ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/" /etc/ssh/sshd_config
sudo sed -i "s/^#*UsePAM.*/UsePAM yes/" /etc/ssh/sshd_config
sudo systemctl restart sshd
2. Hardened sshd_config
# /etc/ssh/sshd_config — Production hardened config
Port 2222
AddressFamily inet
ListenAddress 0.0.0.0
# Authentication
PermitRootLogin no
MaxAuthTries 3
MaxSessions 3
LoginGraceTime 20
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
AuthenticationMethods publickey
# Access Control
AllowUsers deploy admin monitoring
DenyUsers root guest
# Security
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
GatewayPorts no
PrintMotd no
PermitUserEnvironment no
StrictModes yes
# Connection
TCPKeepAlive no
ClientAliveInterval 300
ClientAliveCountMax 2
# Cryptography — modern algorithms only
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512
# Logging
LogLevel VERBOSE
SyslogFacility AUTH
# Banner
Banner /etc/ssh/banner.txt
3. SSH Certificate Authority
SSH certificates are superior to authorized_keys: they expire automatically, can be revoked centrally, and scale to thousands of servers.
# Create a Certificate Authority
ssh-keygen -t ed25519 -f /etc/ssh/ssh_ca -C "Company SSH CA"
# Sign a user key (valid for 8 hours)
ssh-keygen -s /etc/ssh/ssh_ca \
-I "user-john-20260411" \
-n deploy,admin \
-V +8h \
-z 1001 \
~/.ssh/id_ed25519.pub
# Configure server to trust the CA
echo "TrustedUserCAKeys /etc/ssh/ssh_ca.pub" >> /etc/ssh/sshd_config
# Sign host keys (for host verification)
ssh-keygen -s /etc/ssh/ssh_ca \
-I "server-web01" \
-h \
-V +52w \
/etc/ssh/ssh_host_ed25519_key.pub
# Revoke a certificate
ssh-keygen -k -f /etc/ssh/revoked_keys -s /etc/ssh/ssh_ca compromised_key.pub
echo "RevokedKeys /etc/ssh/revoked_keys" >> /etc/ssh/sshd_config
4. Two-Factor Authentication
# Install Google Authenticator
sudo apt install libpam-google-authenticator
# Configure per user
google-authenticator -t -d -f -r 3 -R 30 -w 3
# PAM configuration
# /etc/pam.d/sshd — add at the top:
auth required pam_google_authenticator.so nullok
# SSH config
AuthenticationMethods publickey,keyboard-interactive
sudo systemctl restart sshd
5. Port Knocking
# Install knockd
sudo apt install knockd
# /etc/knockd.conf
[options]
UseSyslog
Interface = eth0
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 15
command = /usr/sbin/iptables -I INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
cmd_timeout = 30
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 15
command = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
# Block SSH by default
sudo iptables -A INPUT -p tcp --dport 2222 -j DROP
# Enable knockd
sudo systemctl enable knockd
# Client usage: knock then connect
knock server.com 7000 8000 9000 && ssh -p 2222 user@server.com
6. Jump Host / Bastion Server
# SSH config for jump host access
# ~/.ssh/config
Host bastion
HostName bastion.company.com
User admin
Port 2222
IdentityFile ~/.ssh/id_ed25519
Host web-*.internal
ProxyJump bastion
User deploy
IdentityFile ~/.ssh/id_ed25519
Host db-*.internal
ProxyJump bastion
User dbadmin
IdentityFile ~/.ssh/id_ed25519_db
# Usage: connects through bastion automatically
ssh web-01.internal
7. Fail2ban for SSH
# /etc/fail2ban/jail.d/sshd.conf
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 86400
action = %(action_mwl)s
[sshd-aggressive]
enabled = true
port = 2222
filter = sshd[mode=aggressive]
logpath = /var/log/auth.log
maxretry = 1
findtime = 3600
bantime = 604800
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
8. SSH Audit and Monitoring
# Install ssh-audit for configuration testing
pip install ssh-audit
ssh-audit localhost:2222
# Monitor SSH sessions in real-time
watch -n5 "who -u; echo ---; ss -tnp | grep :2222"
# Log all SSH commands (add to /etc/bash.bashrc)
export PROMPT_COMMAND='RETRN_VAL=$?; logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]+[ ]*//")"'
# Audit SSH key usage
grep "Accepted publickey" /var/log/auth.log | tail -20
Recommended Reading
Master SSH and remote administration:
Download our SSH Security Cheat Sheet for a printable quick-reference guide.