SSH
SSH (Secure Shell) is the encrypted remote-shell + tunneling protocol. ssh user@host opens a shell; scp copies files; -L tunnels ports. Public-key auth is the safe default.
Keys, config, tunnels, agent
EXAMPLE
# 1) Basic connection
ssh ada@server.example.com
ssh ada@server.example.com -p 2222 # non-default port
ssh ada@server.example.com 'uname -a' # run a single command + exit
# 2) Generate a keypair (Ed25519 — modern, fast, small)
ssh-keygen -t ed25519 -C 'ada@example.com'
# Press Enter to accept default location (~/.ssh/id_ed25519)
# Set a passphrase (or skip with empty input)
# Inspect public key
cat ~/.ssh/id_ed25519.pub
# 3) Install public key on a server
ssh-copy-id ada@server.example.com
# Appends your ~/.ssh/id_ed25519.pub to ~/.ssh/authorized_keys on the server
# After this, ssh ada@server.example.com works without a password
# Or manually:
cat ~/.ssh/id_ed25519.pub | ssh ada@server 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
# 4) SSH config — name your hosts
# ~/.ssh/config
Host prod-web
HostName prod-web-1.example.com
User ada
Port 22
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
ForwardAgent no
Host prod-jump
HostName bastion.example.com
User ada
Host prod-db
HostName 10.0.5.10
User ada
ProxyJump prod-jump # bounce via bastion
Host github.com
User git
IdentityFile ~/.ssh/id_github
# Now:
ssh prod-web # all settings applied
ssh prod-db # connects via bastion automatically
# 5) ssh-agent — cache passphrases
# Linux
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519
ssh-add -l # list keys
ssh-add -d ~/.ssh/id_ed25519 # remove
# macOS — Keychain integration
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# Auto-unlock on login
# ssh-agent forwarding (-A) — sensitive; only forward to TRUSTED servers
ssh -A ada@trusted-server
# 6) Copy files — scp
scp file.txt ada@server:/path/to/ # local → remote
scp ada@server:/path/file.txt ./ # remote → local
scp -r dir/ ada@server:/path/ # recursive
scp -P 2222 file.txt ada@server:/path/ # custom port
# 7) Sync files — rsync (better than scp for big trees)
rsync -avz --progress dir/ ada@server:/path/ # archive, verbose, compress
rsync -avz --delete dir/ ada@server:/path/ # delete files on dest not in source
rsync -avz -e 'ssh -p 2222' dir/ ada@server:/path/
# 8) Tunnels — port forwarding
# Local forward — connect to local port → forwarded to remote
ssh -L 5432:db.internal:5432 ada@bastion
# localhost:5432 → bastion → db.internal:5432
# Access prod DB through bastion without exposing it publicly
# Remote forward — remote port → forwarded to local
ssh -R 8080:localhost:3000 ada@server
# server:8080 → my-laptop:3000
# Useful for sharing a local dev server temporarily
# Dynamic forward — SOCKS proxy
ssh -D 1080 ada@bastion
# All traffic through localhost:1080 tunnels through bastion
# Configure browser to use SOCKS5 localhost:1080
# 9) Run a remote command and stream output
ssh ada@server 'tail -f /var/log/app.log'
ssh ada@server 'cat /etc/os-release'
ssh ada@server 'docker ps' | grep nginx
# 10) Heredoc — multi-line remote scripts
ssh ada@server 'bash -s' << 'EOF'
echo 'Hello from $(hostname)'
uptime
free -h
EOF
# 11) Run a remote sudo command
ssh -t ada@server 'sudo systemctl restart nginx' # -t allocates a pseudo-tty
# 12) Multiplexing — reuse connections (much faster than reconnecting)
# ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/cm-%r@@%h:%p
ControlPersist 10m
# First connection establishes; subsequent ones use the same tunnel.
# Cuts ~500ms off every ssh invocation.
# 13) Server-side authorized_keys options
# /home/ada/.ssh/authorized_keys
# Restrict to a single command (e.g. for backup automation)
command="/usr/local/bin/backup.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... backup-key
# 14) SSH server hardening (sshd_config)
# /etc/ssh/sshd_config
Port 2222 # change default 22 (security through obscurity, weak win)
PermitRootLogin no
PasswordAuthentication no # ONLY public-key auth
PubkeyAuthentication yes
AuthenticationMethods publickey
AllowUsers ada bo
ClientAliveInterval 300
ClientAliveCountMax 2
LogLevel VERBOSE
MaxAuthTries 3
LoginGraceTime 30
sudo systemctl reload sshd
# Test in a NEW terminal before closing the current one!
# 15) Fail2ban — auto-ban brute force attempts
sudo apt install fail2ban
# /etc/fail2ban/jail.local
# [sshd]
# enabled = true
# maxretry = 5
# bantime = 3600
# 16) Known hosts — first-time-trust pattern
# ~/.ssh/known_hosts gets a server's host key on first connection.
# If it changes, ssh refuses (MITM warning).
# Clear a host's entry (e.g. after legitimate server change)
ssh-keygen -R server.example.com
ssh-keygen -R 192.0.2.1
# Verify a host's fingerprint OUT OF BAND (DNS, official docs)
ssh-keyscan server.example.com
# 17) GitHub / GitLab SSH
# Generate a key, add to GitHub Settings → SSH keys
ssh-keygen -t ed25519 -f ~/.ssh/id_github
# Test:
ssh -T git@github.com
# 'Hi Ada! You've successfully authenticated.'
# 18) SCP vs SFTP vs rsync
# scp : simple file copy; legacy; lacks delete + resume
# sftp : interactive (sftp ada@server) + scriptable; supports resume
# rsync : best for syncing trees; delta transfer; resume; --delete
# 19) Tunneling pattern — secure DB access
# 1. SSH-tunnel: ssh -L 5432:db:5432 bastion
# 2. Use 'psql -h localhost -p 5432' from your laptop
# 3. No need to expose db publicly
# 20) Common bugs
# • Permission denied: check ~/.ssh and key file modes (700 + 600)
# • Host key changed: someone may have reset the server; verify before clearing
# • Slow connections: ssh multiplexing (ControlMaster auto)
# • Wrong key used: -i flag or IdentityFile in config + IdentitiesOnly yes
# • Sudo without -t: 'sudo: no tty present'; use -t flag
# 21) Best practices
# ✅ Ed25519 keys (or RSA 4096); never use DSA / ECDSA P-256 (revocation issues)
# ✅ Set passphrase on private keys
# ✅ Use ssh-agent (with macOS Keychain or KeePassXC)
# ✅ Disable password authentication on servers
# ✅ Use ~/.ssh/config for hosts you connect to often
# ✅ ProxyJump for bastion hosts
# ✅ ssh-keyscan + DNS for verifying new hosts
# ✅ fail2ban / rate limits on internet-facing SSH
# ✅ Use a separate key per service (one for personal, one for work)
# ✅ Multiplexing for snappier feel
# 22) Modern alternatives
# • Tailscale / WireGuard — VPN with SSH on top
# • Cloudflare Access / Teleport / StrongDM — managed SSH access with audit
# • SSH certificates (signed by CA) — instead of authorized_keys
# • AWS SSM Session Manager — no SSH port at all (uses agent + IAM)
Why it matters
SSH config + Ed25519 keys + ssh-agent + ProxyJump turns daily server access into one-word commands. For internet-facing SSH, disable password auth, restrict by AllowUsers, and add fail2ban — otherwise the bots will find you.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…