Andrew Mercer

OpenSSH Port Forwarding & Tunneling

For the three forwarding modes and their sshd_config restrictions (AllowTcpForwarding, GatewayPorts, PermitTunnel), see openssh-comprehensive-guide.md §8. This doc collects working examples.


1. X11 forwarding

ssh -X username@hostname

See openssh-troubleshooting.md if this produces xauth errors.

2. Dynamic forwarding (SOCKS proxy) — route browser traffic through a remote host

ssh -D 5000 -p 9999 user@remote-host
# or with an explicit identity file:
ssh -D 5000 -p 9999 user@remote-host -i ~/.ssh/id_ed25519_remote

This turns the local SSH client into a SOCKS5 proxy on 127.0.0.1:5000. Point browser traffic at it:

Chrome (Windows):

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --proxy-server="socks5://127.0.0.1:5000" --proxy-bypass-list="localhost;127.0.0.1"

Firefox: Settings → General → Network Settings → Manual proxy configuration → SOCKS v5, host 127.0.0.1 port 5000, check "Proxy DNS when using SOCKS v5" (this is the "remote DNS" setting — without it, DNS lookups leak outside the tunnel even though traffic itself is tunneled). Equivalent via about:config: network.proxy.socks_remote_dns = true.

3. Local forwarding — reach an internal-only web admin page

ssh -p 9999 -L 8888:192.168.13.30:80 user@hostname

8888 is an arbitrary free local port, 192.168.13.30:80 is the service's address as reachable from the SSH server, not from your machine. Browse to http://localhost:8888.

PuTTY equivalent: Connection → SSH → Tunnels → Source port 8888, Destination 192.168.13.30:80, Add, then Open.

4. Remote forwarding — reverse tunnel

Use when the remote host can't reach back to you directly, but you can reach it:

# Run from your machine
ssh -f -N -R 2222:127.0.0.1:22 10.25.140.52
# From the remote host, connects back through the tunnel to port 22 on your machine
git clone ssh://localhost:2222/usr/local/unixteam/bin.git

-f backgrounds after auth, -N means "no remote command" (tunnel only). Same pattern for reaching a lab environment from inside a restricted network:

# From your workstation, into the lab jump point
ssh -p 9999 amercer@<external_ip> -R 2222:localhost:22

# From the lab server you just landed on
ssh -p 2222 student@localhost

5. sshuttle — transparent subnet-wide tunneling

sshuttle builds a VPN-like tunnel over plain SSH without needing root/tunnel support on the remote end — useful for reaching an entire internal subnet (e.g. a Kubernetes cluster's internal service range) through one jump point, rather than forwarding individual ports one at a time.

# Find the target subnet/IP — e.g. a Kubernetes ingress endpoint
kubectl -n openstack get svc ingress

# Route traffic to that address through an SSH jump host
sshuttle -r [email protected] 10.172.1.101/32 --ssh-cmd 'ssh -i ~/.ssh/amercer.pub'

-r is the jump host, the CIDR is the destination range to route through the tunnel, --ssh-cmd lets you pass a specific identity/options to the underlying ssh call sshuttle wraps.

Note: --ssh-cmd 'ssh -i ~/.ssh/amercer.pub' should point at the private key, not the .pub file — ssh -i expects a private key path. Worth double-checking this on next use.

Further reading

  • man ssh-D, -L, -R, -N, -f flag semantics
  • man sshuttle