SSH Reference — Config File, Port Forwarding, ProxyJump, Keys, and Tunneling
SSH is the backbone of remote infrastructure access. Beyond ssh user@host, the ~/.ssh/config file eliminates repetitive flags, and SSH tunnels let you securely access services that aren’t exposed publicly. The patterns here — jump hosts, SOCKS proxies, agent forwarding — are the ones platform and DevOps engineers use daily but rarely document.
1. Key Generation & Auth
Key types, generation flags, authorized_keys, and certificate-based auth
# Ed25519 — use this for new keys (smaller, faster, more secure than RSA): ssh-keygen -t ed25519 -C "your@email.com" -f ~/.ssh/id_ed25519 # RSA 4096 — for compatibility with older systems: ssh-keygen -t rsa -b 4096 -C "your@email.com" -f ~/.ssh/id_rsa # Always set a passphrase. Use ssh-agent to avoid typing it repeatedly: eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519 ssh-add -l # list loaded keys ssh-add -D # remove all keys from agent # Copy public key to server: ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host # Or manually: cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" # authorized_keys options (restrict what a key can do): # Restrict to specific command only: command="rsync --server -vlogDtpre.iLsfxC . /backups",no-pty,no-x11-forwarding ssh-ed25519 AAAA... # Restrict by source IP: from="192.168.1.0/24",no-port-forwarding,no-agent-forwarding ssh-ed25519 AAAA... # Key fingerprint (verify before trusting): ssh-keygen -lf ~/.ssh/id_ed25519.pub ssh-keygen -E md5 -lf ~/.ssh/id_ed25519.pub # MD5 format for comparison # Certificate-based auth (scales better than authorized_keys for fleets): # CA signs user keys — server trusts CA cert instead of individual keys ssh-keygen -s ca_key -I "user@host" -n username -V +52w ~/.ssh/id_ed25519.pub
2. ~/.ssh/config — Stop Typing Flags
Host aliases, IdentityFile, ProxyJump, ControlMaster for connection reuse
# ~/.ssh/config — evaluated top to bottom, first match wins:
# Specific host alias:
Host prod
HostName 91.98.198.236
User root
IdentityFile ~/.ssh/id_ed25519
Port 22
# Wildcard for all servers in a cluster:
Host *.internal.example.com
User ubuntu
IdentityFile ~/.ssh/deploy_key
StrictHostKeyChecking no # only for internal/known CIDRs
# Jump host (ProxyJump — replaced ProxyCommand for modern SSH):
Host private-server
HostName 10.0.1.50
User ubuntu
ProxyJump bastion.example.com # connects through bastion first
# Multi-hop:
Host deep-server
HostName 10.0.2.100
ProxyJump bastion.example.com,10.0.1.50
# ControlMaster: reuse connections (dramatically speeds up repeated SSH/SCP/rsync):
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600 # keep master open 10 min after last use
ServerAliveInterval 60
ServerAliveCountMax 3
# After editing config:
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh ~/.ssh/sockets
chmod 600 ~/.ssh/config
# Now: just `ssh prod` instead of `ssh -i ~/.ssh/id_ed25519 root@91.98.198.236`
3. Tunneling — Local, Remote & SOCKS
Forward local ports to remote services, expose local services, and SOCKS proxy
# Local port forwarding: access remote service as if it's local
# (e.g. access a database not exposed to the internet):
ssh -L 5432:localhost:5432 user@prod-server
# Now: psql -h localhost -p 5432 postgres
# Syntax: -L [local-addr:]local-port:remote-host:remote-port
# Access a service on a DIFFERENT host in the remote network:
ssh -L 6379:redis.internal:6379 user@bastion
# Now: redis-cli -h localhost -p 6379
# Remote port forwarding: expose local service through remote server
# (e.g. share a local dev server with someone externally):
ssh -R 8080:localhost:3000 user@public-server
# Anyone connecting to public-server:8080 reaches your localhost:3000
# Requires GatewayPorts yes in remote sshd_config
# Dynamic SOCKS proxy (browse through the remote server):
ssh -D 1080 user@remote-server
# Configure browser/curl to use SOCKS5 proxy at localhost:1080
curl --socks5 localhost:1080 https://internal-site.example.com
# All as background daemons:
ssh -fN -L 5432:localhost:5432 user@prod # -f background, -N no command
ssh -fN -D 1080 user@bastion
# Port forwarding in ~/.ssh/config:
Host prod-db-tunnel
HostName prod-server.example.com
User ubuntu
LocalForward 5432 db.internal:5432
LocalForward 6379 redis.internal:6379
ServerAliveInterval 30
4. sshd_config Hardening
Secure your SSH server: disable password auth, restrict users, change port
# /etc/ssh/sshd_config — production hardening:
Port 2222 # non-standard port reduces bot noise
AddressFamily inet # IPv4 only if not using IPv6
# Key auth only (most important setting):
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Disable legacy auth methods:
PermitRootLogin prohibit-password # root only with key, not password
# or: PermitRootLogin no (no root at all)
ChallengeResponseAuthentication no
KerberosAuthentication no
GSSAPIAuthentication no
# Limit who can connect:
AllowUsers deploy ubuntu # whitelist specific users
AllowGroups sshusers # or by group
# Timeouts:
LoginGraceTime 30 # 30s to authenticate
ClientAliveInterval 300 # ping client every 5 min
ClientAliveCountMax 2 # disconnect after 2 missed pings
# Disable forwarding if not needed:
AllowTcpForwarding no
X11Forwarding no
AllowAgentForwarding no
# Apply changes:
sshd -t # test config before reloading
systemctl reload sshd
# fail2ban watches /var/log/auth.log for repeated failures:
# sudo apt install fail2ban
# /etc/fail2ban/jail.local:
# [sshd]
# enabled = true
# bantime = 1h
# maxretry = 5
5. SCP, rsync & SFTP Patterns
File transfer, rsync over SSH, and mount remote filesystems with sshfs
# SCP (use rsync instead — scp has no resume and is slower for many files): scp file.txt user@host:/remote/path/ scp -r ./local-dir user@host:/remote/path/ scp user@host:/remote/file.txt ./local/ # rsync over SSH (resume, delta sync, progress): rsync -avz --progress ./local/ user@host:/remote/path/ # -a = archive (preserves permissions, timestamps, symlinks) # -v = verbose, -z = compress in transit # rsync with exclusions: rsync -avz \ --exclude='.git/' \ --exclude='node_modules/' \ --exclude='*.log' \ ./app/ user@prod:/var/www/app/ # rsync dry run first: rsync -avzn ./app/ user@prod:/var/www/ # -n = dry run, shows what would change # Deploy via rsync + restart: rsync -avz --delete ./dist/ user@prod:/var/www/html/ ssh user@prod "systemctl restart nginx" # sshfs: mount remote directory locally: brew install macfuse sshfs # macOS sudo apt install sshfs # Ubuntu sshfs user@prod:/var/www /mnt/prod # Now /mnt/prod is the remote /var/www — edit files locally fusermount -u /mnt/prod # unmount (Linux) umount /mnt/prod # macOS # SFTP interactive: sftp user@host sftp> ls, cd, get remote-file, put local-file, mget *.log
Track Linux toolchain releases at ReleaseRun. Related: Linux Admin Reference | Kubernetes RBAC Reference | Bash Reference
🔍 Free tool: HTTP Security Headers Analyzer — for web services you secure with SSH tunneling, check that they return the right HTTP security headers.
Founded
2023 in London, UK
Contact
hello@releaserun.com