SSH Tunneling Guide

Tunnel Types Overview

TypeFlagUse Case
Local Forwarding-LAccess remote DB/service locally
Remote Forwarding-RExpose local service to internet
Dynamic (SOCKS)-DFull SOCKS proxy via SSH

Local Port Forwarding

# Access remote PostgreSQL locally at localhost:5432
ssh -L 5432:localhost:5432 user@remote-server

# Access service on private network via jump host
ssh -L 3306:db.internal:3306 user@bastion-host

# Keep connection alive (background)
ssh -L 5432:localhost:5432 -N -f user@remote-server
# -N = no command, -f = background

# Now connect: psql -h localhost -p 5432 -U dbuser mydb

Remote Port Forwarding

# Expose local web server port 3000 on remote server port 8080
# People visiting remote:8080 โ†’ your localhost:3000
ssh -R 8080:localhost:3000 user@remote-server

# For public access, add in /etc/ssh/sshd_config on remote:
# GatewayPorts yes

Jump Host (ProxyJump)

# Connect to private server via bastion
ssh -J user@bastion user@private-server

# ~/.ssh/config (recommended)
Host private-server
    HostName 10.0.0.5
    User deploy
    ProxyJump bastion

Host bastion
    HostName bastion.example.com
    User admin
    IdentityFile ~/.ssh/bastion_key

# Now just: ssh private-server

Dynamic SOCKS5 Proxy

# Create SOCKS5 proxy on local port 1080
ssh -D 1080 -N -f user@remote-server

# Use with curl
curl --socks5 localhost:1080 https://internal.site

# Use with Chrome (proxy all traffic)
google-chrome --proxy-server="socks5://localhost:1080"

SSH Config Hardening

# /etc/ssh/sshd_config best practices
PermitRootLogin no
PasswordAuthentication no    # Keys only
PubkeyAuthentication yes
X11Forwarding no
AllowTcpForwarding yes      # required for tunnels
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2