Skip to content

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