Skip to content
C.W.K.
Stream
Lesson 02 of 08 · published

SSH Tunnels — -L, -R, -D

~13 min · ssh, tunnel, port-forward

Level 0Window Tourist
0 XP0/95 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

One TCP pipe, many tricks

SSH can forward arbitrary TCP ports through its encrypted connection. Three flags do almost everything:

  • -L localport:remoteHost:remotePort — local forward.
  • -R remoteport:localHost:localPort — remote forward.
  • -D socksport — SOCKS proxy.

-L: reach a remote service from your laptop

ssh -L 5432:localhost:5432 dbserver

Reads as: "open port 5432 on my laptop. Anything connecting to it gets tunneled through ssh to dbserver, which then connects to its own localhost:5432." Now psql -h localhost hits the remote Postgres without exposing it to the internet.

-R: expose your laptop to a server

ssh -R 8080:localhost:3000 publicbox

"Ask the SSH server to listen on port 8080 and forward accepted connections to port 3000 on this laptop." The default remote bind is commonly loopback-only; reaching it from another machine also depends on the server's GatewayPorts policy, firewall, and authorization. Treat a wider bind as deliberate service exposure.

-D: a SOCKS proxy through the server

ssh -D 1080 jumpbox

Configure a SOCKS-aware application to use 127.0.0.1:1080 and that application's supported TCP traffic can leave through jumpbox. This is not a device-wide VPN, and DNS behavior depends on the client.

Make tunnels permanent in config

Host dbserver
  HostName db.internal
  LocalForward 5432 localhost:5432
  LocalForward 6379 localhost:6379

Then ssh dbserver opens the configured tunnels. -N skips a remote command; -f backgrounds the client after authentication. For automation, add failure visibility such as ExitOnForwardFailure yes and give the tunnel a lifecycle owner instead of forgetting a hidden process.

autossh for resilience

brew install autossh. autossh wraps ssh with auto-reconnect on flaky networks — perfect for long-running tunnels.

Bind address is part of the security boundary

Prefer an explicit loopback bind such as -L 127.0.0.1:5432:localhost:5432 when only this machine should connect. Before widening either end, inspect listeners and confirm the SSH server policy and network firewall.

A tunnel can hide its lifecycle owner

Backgrounding SSH does not create monitoring, restart policy, logs, or a stop procedure. Give a persistent tunnel one service owner, enable forward-failure detection, and verify the listening address after startup.

Code

Reach internal Postgres·bash
ssh -fNL 5432:localhost:5432 dbserver
psql -h localhost -p 5432 -U me production
# Kill the tunnel later
pgrep -f 'ssh -fNL 5432' | xargs kill

External links

Exercise

If you have any internal service on a remote box (DB, Redis, web), open a tunnel: ssh -fNL <localport>:localhost:<remoteport> host. Connect from your laptop. Then add the same line as LocalForward in ssh_config and re-test.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.