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/configSSH 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-sha512Apply Changes
# Test configuration
sudo sshd -t
# Reload SSH (keep existing connections)
sudo systemctl reload sshd
# Restart SSH (closes all connections)
sudo systemctl restart sshdSSH 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.comConnect Using Config
# Instead of: ssh -i ~/.ssh/work_key -p 22022 [email protected]
ssh work
# Jump through bastion
ssh internalSSH 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 -DSSH 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@bastionRemote Port Forwarding
Expose local service to remote:
# Expose local port 3000 on server's port 8080
ssh -R 8080:localhost:3000 user@serverDynamic 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.localTwo-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-interactiveFail2Ban 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.100SSH 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 22Key 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