SSH Tunneling¶
Local Port Forwarding¶
Forward a remote port to your local machine:
# Remote MySQL (3306) → localhost:3307
ssh -L 3307:localhost:3306 user@server
# Remote service on specific host
ssh -L 8080:internal-server:80 user@bastion
# Multiple tunnels
ssh -L 3307:localhost:3306 -L 6379:localhost:6379 user@server
Use case: Access a database that only listens on localhost.
Remote Port Forwarding¶
Forward your local port to the remote server:
# Local web server → remote:8080
ssh -R 8080:localhost:3000 user@server
# Multiple
ssh -R 8080:localhost:3000 -R 9090:localhost:9000 user@server
Use case: Share your local dev server with a colleague via a public server.
Dynamic Port Forwarding (SOCKS Proxy)¶
# Create SOCKS proxy on localhost:1080
ssh -D 1080 user@server
# Then configure browser/application to use SOCKS5 proxy:
# Host: localhost
# Port: 1080
Use case: Browse the web through the remote server's network (bypass geo-restrictions).
Jump Host (Bastion)¶
# Connect to internal server via bastion
ssh -J user@bastion user@internal-server
# In SSH config (~/.ssh/config)
Host internal
HostName 10.0.1.50
User admin
ProxyJump user@bastion.example.com
ForwardAgent yes
# Then: ssh internal
Use case: Reach servers that are not directly accessible from the internet.
SSH Config for Tunnels¶
~/.ssh/config
# Database tunnel
Host db-tunnel
HostName db-server.example.com
User devops
LocalForward 3307 localhost:3306
LocalForward 6379 localhost:6379
# Persistent tunnel (auto-reconnect)
Host persistent-tunnel
HostName server.example.com
User devops
ServerAliveInterval 60
ServerAliveCountMax 3
ExitOnForwardFailure yes
LocalForward 3307 localhost:3306
LocalForward 8080 localhost:80
Auto-Reconnect Tunnel¶
/usr/local/bin/ssh-tunnel.sh
#!/bin/bash
while true; do
ssh -o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-L 3307:localhost:3306 \
-L 6379:localhost:6379 \
user@server
sleep 5
done
Systemd Service¶
/etc/systemd/system/ssh-tunnel.service
[Unit]
Description=SSH Tunnel
After=network.target
[Service]
Type=simple
User=devops
ExecStart=/usr/local/bin/ssh-tunnel.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now ssh-tunnel
Reverse Tunnel (Expose Local to Internet)¶
# On local machine: forward local:3000 → server:8080
ssh -R 8080:localhost:3000 user@public-server
# Now anyone accessing public-server:8080 sees your local app
Auto-SSH for Reverse Tunnel¶
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-R 8080:localhost:3000 user@public-server -N
Tunnel Through HTTPS Proxy¶
# Using corkscrew or netcat
ssh -o "ProxyCommand corkscrew proxy.example.com 3128 %h %p" user@server
# With netcat
ssh -o "ProxyCommand nc -X connect -x proxy.example.com:3128 %h %p" user@server
Copy Files Through Tunnel¶
# SCP through jump host
scp -o "ProxyJump user@bastion" file.txt user@internal-server:/tmp/
# Rsync through tunnel
rsync -avz -e "ssh -J user@bastion" ./ user@internal-server:/var/www/
Knowledge Base Access¶
# Access web UI on remote server
ssh -L 8080:localhost:8080 user@server
# Then open http://localhost:8080
# Access internal service through bastion
ssh -J bastion -L 9090:internal-web:80 user@bastion
# Then open http://localhost:9090
Common Use Cases¶
| Tunnel | Command | Purpose |
|---|---|---|
| MySQL | -L 3306:localhost:3306 |
Access remote MySQL locally |
| Redis | -L 6379:localhost:6379 |
Debug Redis from local |
| Adminer | -L 8080:localhost:80 |
Access internal web UI |
| Kubernetes | -L 6443:localhost:6443 |
Control remote cluster |
| Prometheus | -L 9090:localhost:9090 |
View remote metrics |
| Grafana | -L 3000:localhost:3000 |
View remote dashboards |
Troubleshooting¶
# Verbose tunnel debug
ssh -vvv -L 3307:localhost:3306 user@server
# Check if tunnel port is listening
ss -tlnp | grep 3307
# Kill hanging tunnel
lsof -ti:3307 | xargs kill
# GatewayPorts (allow others to use your tunnel)
ssh -R 0.0.0.0:8080:localhost:3000 user@server
# Requires GatewayPorts yes in sshd_config
Security¶
/etc/ssh/sshd_config
# Allow only specific users to create tunnels
AllowTcpForwarding yes
GatewayPorts no
PermitOpen localhost:3306 localhost:6379
Verification¶
- Tunnel established (can connect to forwarded port)
- Auto-reconnect configured (autossh/systemd)
- Jump host working for internal servers
- Tunnels restart on boot
- Only necessary ports forwarded