Linux & Networking12 min
SSH — Key Management, Config & Tunnelling Cheat Sheet
Complete SSH reference — key generation, config file, port forwarding, tunnels, jump hosts, SCP, rsync, and hardening tips.
Connect & Basic Usage
# Connect
ssh user@hostname
ssh user@192.168.1.10
ssh -p 2222 user@hostname # Non-default port
# Run remote command (non-interactive)
ssh user@host "uptime"
ssh user@host "df -h && free -m"
# Run remote command with sudo (requires TTY)
ssh -t user@host "sudo systemctl restart nginx"
# Exit
exit
# or Ctrl+D
Key Generation
# Ed25519 (recommended — fast, secure, small key)
ssh-keygen -t ed25519 -C "your@email.com"
# RSA 4096 (more compatible)
ssh-keygen -t rsa -b 4096 -C "your@email.com"
# Custom filename
ssh-keygen -t ed25519 -f ~/.ssh/work_key -C "work laptop"
# Keys are stored at:
ls ~/.ssh/
# id_ed25519 ← private key (600, never share!)
# id_ed25519.pub ← public key (safe to copy anywhere)
Copy Public Key to Server
# Easiest way (recommended)
ssh-copy-id user@hostname
ssh-copy-id -i ~/.ssh/my_key.pub user@hostname
# Manual (if ssh-copy-id not available)
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# Or on server:
mkdir -p ~/.ssh
echo "ssh-ed25519 AAAA... your@email.com" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
SSH Config File (~/.ssh/config)
# Default settings for all hosts
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
# Work server
Host work
HostName 203.0.113.10
User alice
Port 2222
IdentityFile ~/.ssh/work_key
# Bastion / jump host
Host bastion
HostName bastion.example.com
User ops
IdentityFile ~/.ssh/bastion_key
# Internal server via bastion
Host internal
HostName 10.0.1.20
User ops
ProxyJump bastion
# Now connect with simple alias
ssh work
ssh internal # automatically hops through bastion
Jump Hosts (Bastion)
# Ad-hoc jump (OpenSSH 7.3+)
ssh -J user@bastion user@internal-host
# Multiple hops
ssh -J user@bastion1,user@bastion2 user@final-host
# Config-based (preferred)
# Add to ~/.ssh/config:
# Host internal
# ProxyJump bastion
# Legacy ProxyCommand (older OpenSSH)
ssh -o ProxyCommand="ssh -W %h:%p bastion" user@internal-host
Port Forwarding & Tunnels
# Local forward: access remote-host:5432 via localhost:5432
# (connect a DB on a remote private network)
ssh -L 5432:remote-db:5432 user@bastion
ssh -L 8080:internal-api:80 user@gateway
# Usage after forwarding:
psql -h localhost -p 5432 -U admin mydb
curl http://localhost:8080/api
# Remote forward: expose local port 8080 on remote server's port 80
# (create a temporary public URL for local dev)
ssh -R 80:localhost:8080 user@public-server
# Dynamic (SOCKS5 proxy — route all browser traffic through SSH)
ssh -D 1080 user@host
# Then configure browser to use SOCKS5 proxy at localhost:1080
# Background tunnel (don't open shell)
ssh -N -L 5432:db:5432 -f user@bastion # -N: no command, -f: background
SCP — Secure Copy
# Copy local file to remote
scp file.txt user@host:/remote/path/
# Copy remote file to local
scp user@host:/remote/file.txt ./local/
# Copy directory recursively
scp -r ./local-dir/ user@host:/remote/path/
# Via non-default port
scp -P 2222 file.txt user@host:/path/
# Via jump host
scp -J user@bastion file.txt user@internal:/path/
# Preserve timestamps
scp -p file.txt user@host:/path/
rsync — Efficient Sync
# Sync local dir to remote (only changed files)
rsync -av ./local-dir/ user@host:/remote/dir/
# Dry run (see what would change)
rsync -avn ./local-dir/ user@host:/remote/dir/
# Delete files on remote not in local
rsync -av --delete ./local-dir/ user@host:/remote/dir/
# Exclude patterns
rsync -av --exclude='*.log' --exclude='.git/' ./src/ user@host:/deploy/
# Progress bar
rsync -av --progress large-file.tar.gz user@host:/path/
# Over non-default SSH port
rsync -av -e "ssh -p 2222" ./dir/ user@host:/path/
SSH Agent
# Start agent
eval "$(ssh-agent -s)"
# Add key to agent (prompted for passphrase once)
ssh-add ~/.ssh/id_ed25519
ssh-add ~/.ssh/work_key
# List loaded keys
ssh-add -l
# Remove all keys
ssh-add -D
# Agent forwarding (use local keys on remote host)
ssh -A user@host # -A: forward agent
# Or in ~/.ssh/config: ForwardAgent yes
Server Hardening (/etc/ssh/sshd_config)
# Disable root login
PermitRootLogin no
# Disable password authentication (key-only)
PasswordAuthentication no
ChallengeResponseAuthentication no
# Allow only specific users/groups
AllowUsers alice bob
AllowGroups ops
# Change default port (obscurity, not security)
Port 2222
# Limit auth attempts
MaxAuthTries 3
LoginGraceTime 20
# Disable empty passwords
PermitEmptyPasswords no
# Disable X11 forwarding if not needed
X11Forwarding no
# Restrict to IPv4 only
AddressFamily inet
# Reload after changes
sudo systemctl reload sshd
# Test config syntax before reloading
sudo sshd -t
Key Management
# View public key fingerprint
ssh-keygen -lf ~/.ssh/id_ed25519.pub
ssh-keygen -lf ~/.ssh/id_ed25519.pub -E md5
# Convert OpenSSH to PEM
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa
# Remove a host from known_hosts
ssh-keygen -R hostname
ssh-keygen -R 192.168.1.10
# Verify remote host key fingerprint
ssh-keyscan -H hostname >> ~/.ssh/known_hosts
ssh-keyscan -H hostname | ssh-keygen -lf -
# Change passphrase on existing key
ssh-keygen -p -f ~/.ssh/id_ed25519
Escape Sequences (Inside SSH Session)
| Sequence | Action |
|---|---|
~. | Disconnect (when stuck) |
~C | Open command line (for port forwarding) |
~& | Background the session |
~? | Show all escape sequences |
Prefix: Enter then ~ then the character.
