Why SSH Keys Instead of Passwords
SSH password authentication has two critical problems: passwords can be brute-forced, and they're susceptible to phishing. SSH keys solve both: a key pair is mathematically impossible to brute-force, and the private key never leaves your machine.
Generating SSH Keys
# Ed25519 (recommended — smaller, faster, more secure than RSA)
ssh-keygen -t ed25519 -C "your.email@example.com"
# RSA 4096 (for systems that don't support Ed25519)
ssh-keygen -t rsa -b 4096 -C "your.email@example.com"
# Prompts:
# Enter file: ~/.ssh/id_ed25519 (default) or choose a custom path
# Enter passphrase: USE ONE! (protects the private key if stolen)
# Files created:
# ~/.ssh/id_ed25519 ← PRIVATE KEY: never share this!
# ~/.ssh/id_ed25519.pub ← Public key: copy to servers
# View your public key (copy this to servers)
cat ~/.ssh/id_ed25519.pub
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your.email@example.com
# Multiple keys for different purposes
ssh-keygen -t ed25519 -f ~/.ssh/id_work -C "work@company.com"
ssh-keygen -t ed25519 -f ~/.ssh/id_github -C "github@personal.com"
ssh-keygen -t ed25519 -f ~/.ssh/id_servers -C "servers-2026"
Copying Keys to Servers
# Easiest method (installs key in ~/.ssh/authorized_keys on server)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server.example.com
# Manual method (when ssh-copy-id isn't available)
cat ~/.ssh/id_ed25519.pub | ssh user@server 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
# On the server, verify:
cat ~/.ssh/authorized_keys
# Should contain your public key
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
SSH Config File
The most underused SSH feature:
# ~/.ssh/config
# Permissions must be: chmod 600 ~/.ssh/config
Host *
ServerAliveInterval 60 # Send keepalive every 60s
ServerAliveCountMax 3 # Disconnect after 3 missed keepalives
ControlMaster auto # Share connections (faster multiple sessions)
ControlPath ~/.ssh/cm_%h_%p_%r
ControlPersist 10m
# Development server
Host dev
HostName 192.168.1.50
User developer
IdentityFile ~/.ssh/id_ed25519
Port 22
# Production via bastion host
Host prod-bastion
HostName bastion.mycompany.com
User ec2-user
IdentityFile ~/.ssh/id_work
Host prod-*
User ubuntu
IdentityFile ~/.ssh/id_work
ProxyJump prod-bastion # Jump through bastion automatically!
Host prod-web
HostName 10.0.1.10 # Internal IP
Host prod-api
HostName 10.0.1.20
# GitHub
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_github
# Work GitHub
Host github.work
HostName github.com
User git
IdentityFile ~/.ssh/id_work
# Usage after config setup:
ssh dev # Connects to 192.168.1.50 automatically
ssh prod-api # Jumps through bastion automatically!
# Clone work repo using different identity
git clone git@github.work:mycompany/repo.git
ssh-agent: Managing Passphrase-Protected Keys
# Start agent (usually auto-started by OS)
eval "$(ssh-agent -s)"
# Add your key (prompted for passphrase once)
ssh-add ~/.ssh/id_ed25519
ssh-add ~/.ssh/id_work
# List loaded keys
ssh-add -l
# Add key temporarily (expires after 4 hours)
ssh-add -t 14400 ~/.ssh/id_ed25519
# Remove all keys from agent
ssh-add -D
# macOS: Store passphrase in Keychain (persist across reboots)
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# ~/.zshrc / ~/.bashrc — auto-add keys on login
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi
SSH Tunneling
# Local port forwarding — access remote service locally
# Access remote PostgreSQL at localhost:5432
ssh -L 5432:localhost:5432 user@server -N
# With SSH config:
Host db-tunnel
HostName server.example.com
User user
LocalForward 5432 localhost:5432
LocalForward 6379 localhost:6379 # Also tunnel Redis
# Usage:
ssh -N db-tunnel # Start tunnel
psql -h localhost -p 5432 -U myuser mydb # Connect locally!
# Remote port forwarding — expose local service to remote
# Expose local dev server to the internet via remote server
ssh -R 8080:localhost:3000 user@server -N
# Now: http://server.example.com:8080 → your local :3000
# SOCKS5 proxy — route browser traffic through server
ssh -D 1080 user@server -N
# Configure browser to use SOCKS5 proxy: localhost:1080
Hardening SSH Server Security
# /etc/ssh/sshd_config — server hardening
# Disable password auth (key-only)
PasswordAuthentication no
ChallengeResponseAuthentication no
# Disable root login
PermitRootLogin no
# Use only modern algorithms
Protocol 2
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Limit login attempts
MaxAuthTries 3
MaxSessions 5
# Disconnect idle sessions
ClientAliveInterval 300
ClientAliveCountMax 2
# Log level (for auditing)
LogLevel VERBOSE
# Allow only specific users
AllowUsers deploy devteam
# Restart SSH after changes:
sudo systemctl restart sshd
# Test in another terminal before closing current session!
Debugging SSH
# Verbose output for debugging
ssh -v user@server # Verbose
ssh -vv user@server # More verbose
ssh -vvv user@server # Maximum verbosity
# Common issues:
# "Permission denied (publickey)"
ssh -v user@server 2>&1 | grep "Offering|Trying|Authentications"
# Check: correct key loaded? correct user? authorized_keys permissions?
# Check authorized_keys on server
cat ~/.ssh/authorized_keys
ls -la ~/.ssh/
# Should be: 700 for ~/.ssh, 600 for authorized_keys
# Test specific key
ssh -i ~/.ssh/id_ed25519 user@server
# Check if server accepts your key
ssh-keyscan server.example.com >> ~/.ssh/known_hosts
GitHub/GitLab SSH Setup
# Test connection
ssh -T git@github.com
# Hi username! You've successfully authenticated
# Add GitHub's host keys (or verify fingerprints on github.com/docs)
ssh-keyscan github.com >> ~/.ssh/known_hosts
# Multiple GitHub accounts
# ~/.ssh/config:
Host github.com-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_github_personal
Host github.com-work
HostName github.com
User git
IdentityFile ~/.ssh/id_github_work
# Usage:
git clone git@github.com-personal:username/repo.git
git clone git@github.com-work:company/repo.git
→ Generate RSA key pairs for SSH and other cryptographic purposes with the RSA Key Pair Generator.