SSH Cheat Sheet¶
Fun fact: SSH (Secure Shell) was created by Tatu Ylonen in 1995 at Helsinki University of Technology after a password-sniffing attack on the university network. It replaced telnet, rsh, and rlogin — all of which sent credentials in plaintext. SSH uses port 22 because Ylonen noticed ports 21 (FTP) and 23 (Telnet) were taken, so he requested port 22 from IANA and got it approved the same day.
Remember: SSH debug verbosity levels:
-v(basic handshake),-vv(key exchange details),-vvv(full packet-level debug). Start with-vand increase only if needed. For 99% of connection issues,-vshows the problem: wrong key offered, host key mismatch, or authentication method rejected.
Basic Connection¶
ssh user@host # Connect with default key
ssh -p 2222 user@host # Non-default port
ssh -i ~/.ssh/mykey user@host # Specific private key
ssh -v user@host # Verbose (debug connection issues)
ssh -vvv user@host # Maximum verbosity
Common Flags¶
| Flag | Meaning |
|---|---|
-p PORT |
Connect to non-standard port |
-i KEY |
Use specific identity file |
-v / -vv / -vvv |
Increasing verbosity levels |
-o OPTION=VALUE |
Set config option inline |
-N |
No remote command (tunnels only) |
-f |
Go to background after auth |
-q |
Quiet mode (suppress warnings) |
-T |
Disable pseudo-terminal allocation |
-t |
Force pseudo-terminal allocation |
-C |
Enable compression |
-A |
Enable agent forwarding |
-J JUMP_HOST |
ProxyJump through a bastion |
Key Management¶
# Generate a key pair (Ed25519 preferred)
ssh-keygen -t ed25519 -C "user@host" -f ~/.ssh/mykey
# Generate RSA key (when Ed25519 not supported)
ssh-keygen -t rsa -b 4096 -C "user@host"
# Copy public key to remote host
ssh-copy-id -i ~/.ssh/mykey.pub user@host
# View key fingerprint
ssh-keygen -lf ~/.ssh/mykey.pub
# Change passphrase on existing key
ssh-keygen -p -f ~/.ssh/mykey
# Convert key format (OpenSSH <-> PEM)
ssh-keygen -p -m PEM -f ~/.ssh/mykey # To PEM
ssh-keygen -p -m RFC4716 -f ~/.ssh/mykey # To RFC4716
SSH Agent¶
# Start the agent
eval "$(ssh-agent -s)"
# Add key to agent
ssh-add ~/.ssh/mykey
# Add key with timeout (seconds)
ssh-add -t 3600 ~/.ssh/mykey
# List loaded keys
ssh-add -l
# Remove all keys from agent
ssh-add -D
# Forward agent to remote host (use with caution)
ssh -A user@host
SSH Config File (~/.ssh/config)¶
# Default for all hosts
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
# Named host
Host prod-bastion
HostName 10.0.1.50
User admin
Port 2222
IdentityFile ~/.ssh/prod-key
# Jump through bastion
Host prod-app
HostName 10.0.2.100
User deploy
ProxyJump prod-bastion
# Wildcard pattern
Host *.staging.example.com
User staging
IdentityFile ~/.ssh/staging-key
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Tunneling (Port Forwarding)¶
# Local port forward: access remote:5432 via localhost:5432
ssh -L 5432:db.internal:5432 user@bastion
# Local forward (background, no shell)
ssh -fNL 5432:db.internal:5432 user@bastion
# Remote port forward: expose local:3000 on remote:8080
ssh -R 8080:localhost:3000 user@remote
# Dynamic SOCKS proxy
ssh -D 1080 user@host
# Then configure browser/app to use SOCKS5 proxy at localhost:1080
# Tunnel through multiple hops
ssh -J bastion1,bastion2 user@target
ProxyJump (Bastion/Jump Host)¶
# Command line
ssh -J user@bastion user@internal-host
# Multiple jumps
ssh -J bastion1,bastion2 user@target
# In SSH config (preferred)
Host internal
HostName 10.0.2.50
ProxyJump bastion
# Legacy ProxyCommand equivalent
Host internal
HostName 10.0.2.50
ProxyCommand ssh -W %h:%p bastion
File Transfer¶
# SCP: copy file to remote
scp file.txt user@host:/path/to/dest/
# SCP: copy from remote
scp user@host:/path/to/file.txt ./
# SCP: recursive directory copy
scp -r mydir/ user@host:/path/to/dest/
# Rsync over SSH (preferred over SCP)
rsync -avz -e ssh localdir/ user@host:/remotedir/
# SFTP interactive session
sftp user@host
Troubleshooting¶
# Test connection without login
ssh -o BatchMode=yes -o ConnectTimeout=5 user@host echo ok
# Check which key the server accepted
ssh -v user@host 2>&1 | grep "Authenticated"
# Debug permission denied
ssh -vvv user@host 2>&1 | grep -A2 "Trying\|Offering\|Server accepts"
# Check sshd config syntax
sudo sshd -t
# View active SSH sessions on a server
who | grep pts
ss -tnp | grep :22
# Check known_hosts for a host
ssh-keygen -F hostname
# Remove old host key (after server rebuild)
ssh-keygen -R hostname
Security Best Practices¶
# Disable password auth (in /etc/ssh/sshd_config)
PasswordAuthentication no
ChallengeResponseAuthentication no
# Restrict to specific users
AllowUsers deploy admin
# Disable root login
PermitRootLogin no
# Use strong key exchange algorithms
KexAlgorithms curve25519-sha256@libssh.org
# Limit auth attempts
MaxAuthTries 3
# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
Quick Recipes¶
# Run remote command and exit
ssh user@host 'df -h && free -m'
# Run local script on remote host
ssh user@host 'bash -s' < local-script.sh
# Multiplex connections (ControlMaster)
# In ~/.ssh/config:
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# Create the socket directory
mkdir -p ~/.ssh/sockets
# Kill a stuck SSH session
# Press: Enter, ~, .