·11 min read

SSH Tunneling Masterclass: Local, Remote, and Dynamic Port Forwarding

Master all three types of SSH tunneling to securely access databases, bypass firewalls, and proxy traffic through encrypted connections.

SSHTunnelingPort ForwardingSecurityNetworking

SSH isn't just for remote shells. Its port forwarding capabilities create secure tunnels for any TCP traffic. Database connections, web services, API calls — all can flow through encrypted SSH tunnels, bypassing firewalls and protecting data in transit.

This guide covers all three types of SSH tunneling with practical examples you can use today.

Type 1: Local Port Forwarding (L)

Use case: Access a remote service as if it were running on your local machine.

How It Works

Local Machine                    SSH Server
localhost:8080 ──→ SSH Tunnel ──→ localhost:3306 (MySQL)

You connect to localhost:8080 on your machine, and the traffic is forwarded through the SSH tunnel to port 3306 on the remote server.

Basic Example: Remote MySQL

ssh -L 8080:localhost:3306 user@server.example.com

Now connect your MySQL client to localhost:8080:

mysql -h 127.0.0.1 -P 8080 -u root -p

The connection flows: Your MySQL Client → Local Port 8080 → SSH Tunnel → Server → MySQL on Port 3306

Advanced: Forward to Third-Party Host

ssh -L 8080:db.internal.company.com:3306 user@server.example.com

Traffic flows: Your Machine → SSH Tunnel → Server → db.internal.company.com:3306

The SSH server acts as a bridge to other hosts on its network.

Real-World Scenarios

Access internal dashboards:

# Forward Grafana dashboard
ssh -L 3000:monitoring.internal:3000 user@bastion.company.com
# Open http://localhost:3000 in browser

Connect to Redis:

ssh -L 6379:localhost:6379 user@redis-server.example.com
redis-cli -h 127.0.0.1 -p 6379

Test staging APIs:

ssh -L 8000:staging-api.internal:8000 user@staging-bastion.example.com
curl http://localhost:8000/api/health

FoxTerm Integration

FoxTerm makes local port forwarding visual:

  1. Connect to server via SSH
  2. Click "Port Forwarding" in toolbar
  3. Click "Add Local Forward"
  4. Enter local port (e.g., 8080)
  5. Enter remote host and port (e.g., localhost:3306)
  6. Click "Start"

FoxTerm manages the tunnel in the background. No terminal commands needed.

Type 2: Remote Port Forwarding (R)

Use case: Expose a local service to a remote server or the internet.

How It Works

Remote Server                  Your Machine
port 9090 ──→ SSH Tunnel ──→ localhost:3000 (dev server)

Anyone connecting to port 9090 on the remote server reaches your local port 3000.

Basic Example: Expose Dev Server

ssh -R 9090:localhost:3000 user@server.example.com

Now anyone can access your local dev server:

curl http://server.example.com:9090

Advanced: Gateway Ports

By default, remote forwards only listen on 127.0.0.1. To allow external access:

ssh -R 0.0.0.0:9090:localhost:3000 user@server.example.com

Server config must allow this:

# /etc/ssh/sshd_config
GatewayPorts yes

Real-World Scenarios

Demo local work to teammates:

ssh -R 8080:localhost:3000 user@demo-server.example.com
# Share: http://demo-server.example.com:8080

Bypass corporate firewall:

# At office, expose internal tool to home server
ssh -R 443:internal-tool.corp:443 user@home-server.example.com
# At home: https://home-server.example.com

Temporary webhook endpoint:

# Expose local webhook handler to Stripe/PayPal
ssh -R 8443:localhost:8080 user@public-server.example.com
# Configure webhook URL: https://public-server.example.com:8443/webhook

Security Warning

Remote port forwarding exposes your local services. Always:

  • Use strong SSH authentication (keys, not passwords)
  • Limit which interfaces the remote port binds to
  • Close tunnels when done
  • Consider using ngrok or cloudflared for production exposure

Type 3: Dynamic Port Forwarding (D)

Use case: Create a SOCKS proxy to route arbitrary traffic through the SSH server.

How It Works

Your Apps → SOCKS Proxy (localhost:1080) → SSH Tunnel → SSH Server → Any Destination

Unlike local/remote forwarding (which forward specific ports), dynamic forwarding creates a general-purpose proxy.

Basic Example: SOCKS Proxy

ssh -D 1080 user@server.example.com

Configure your browser or system to use SOCKS proxy at localhost:1080. All traffic routes through the SSH server.

Browser Configuration

Firefox:

  1. Settings → Network Settings → Manual proxy configuration
  2. SOCKS Host: 127.0.0.1, Port: 1080
  3. Select "SOCKS v5"
  4. Check "Proxy DNS when using SOCKS v5"

Chrome (command line):

chrome --proxy-server="socks5://127.0.0.1:1080"

System-wide (macOS):

networksetup -setsocksfirewallproxystate Wi-Fi on
networksetup -setsocksfirewallproxy Wi-Fi 127.0.0.1 1080

Real-World Scenarios

Access internal websites:
Browse internal company sites that are only accessible from the office network.

Bypass geo-restrictions:

ssh -D 1080 user@server-in-japan.example.com
# Now browse as if you're in Japan

Secure public WiFi:

ssh -D 1080 user@trusted-server.example.com
# All traffic encrypted through tunnel, safe from WiFi snooping

Debug production APIs:

ssh -D 1080 user@bastion.production.com
# Configure API client to use SOCKS proxy
# Access internal APIs as if you're on the production network

FoxTerm Integration

FoxTerm includes a built-in SOCKS proxy:

  1. Connect to server via SSH
  2. Click "Port Forwarding" → "Enable SOCKS Proxy"
  3. FoxTerm starts a SOCKS5 proxy on localhost:1080
  4. Configure apps to use the proxy
  5. Toggle on/off from the menu bar

No command-line setup required.

Combining Tunnel Types

Real-world scenarios often combine multiple tunnel types.

Example: Full Development Environment

# Local forward: Access remote database
ssh -L 5432:db.internal:5432 \
    # Remote forward: Expose local dev server
    -R 8080:localhost:3000 \
    # Dynamic forward: SOCKS proxy for internal APIs
    -D 1080 \
    user@bastion.example.com

Now you have:

  • PostgreSQL available at localhost:5432
  • Dev server accessible at bastion.example.com:8080
  • SOCKS proxy at localhost:1080 for internal API access

Jump Hosts (Bastion Servers)

Many organizations use bastion servers as gateways to internal networks.

Single Jump Host

ssh -J user@bastion.example.com user@internal-server

With port forwarding:

ssh -L 8080:db.internal:5432 -J user@bastion.example.com user@internal-server

Multiple Jump Hosts

ssh -J user@bastion1.example.com,user@bastion2.example.com user@internal-server

FoxTerm Configuration

FoxTerm supports jump hosts natively:

  1. Settings → SSH → Proxy/Jump Host
  2. Enter bastion server details
  3. FoxTerm automatically chains connections

SSH Config File

Simplify complex tunnel setups with ~/.ssh/config:

# ~/.ssh/config

# Bastion server
Host bastion
    HostName bastion.example.com
    User deploy
    IdentityFile ~/.ssh/bastion_key

# Internal server via bastion
Host internal
    HostName 10.0.1.100
    User admin
    ProxyJump bastion
    # Local forward: database
    LocalForward 5432 db.internal:5432
    # Dynamic forward: SOCKS proxy
    DynamicForward 1080

# Production web server
Host prod-web
    HostName 10.0.2.50
    User www
    ProxyJump bastion
    # Remote forward: expose local debug server
    RemoteForward 9090 localhost:3000

Now connect with:

ssh internal
# Automatically sets up both port forwards

Troubleshooting

"Address already in use"

Another process is using the local port. Find and kill it:

lsof -i :8080
kill <PID>

Or use a different local port.

"Cannot bind to port"

Port < 1024 requires root. Use higher ports or run with sudo.

Tunnel Disconnects

SSH connections drop. Keep them alive:

ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@server

Or in ~/.ssh/config:

Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3

Debugging

Add -v flag to see tunnel establishment:

ssh -v -L 8080:localhost:3306 user@server

Look for: Local forwarding listening on 127.0.0.1 port 8080

Security Best Practices

1. Use Key-Based Authentication

Never rely on passwords for tunnel connections:

ssh-keygen -t ed25519 -f ~/.ssh/tunnel_key
ssh-copy-id -i ~/.ssh/tunnel_key user@server

2. Restrict Tunnel Permissions

Limit what users can do with tunnels:

# /etc/ssh/sshd_config
AllowTcpForwarding yes
GatewayPorts no
PermitOpen localhost:3306 localhost:5432

PermitOpen restricts which destinations are allowed.

3. Monitor Active Tunnels

Check what tunnels are active:

ss -tlnp | grep ssh
netstat -tlnp | grep sshd

4. Log Tunnel Usage

Enable logging:

# /etc/ssh/sshd_config
LogLevel VERBOSE

Logs show tunnel establishment in /var/log/auth.log.

5. Time-Limited Tunnels

For temporary access, use timeout:

timeout 3600 ssh -L 8080:localhost:3306 user@server
# Tunnel closes after 1 hour

Conclusion

SSH tunneling is a powerful tool for secure networking. Local forwarding brings remote services to your machine. Remote forwarding exposes local services to remote networks. Dynamic forwarding creates flexible SOCKS proxies. Combined with jump hosts and SSH config, you can navigate complex network topologies securely.

FoxTerm makes all of this visual and manageable, so you spend less time on commands and more time on your actual work.


FoxTerm Pro includes visual port forwarding management. Download at foxterm.app.