SSH Tunnel Configuration (Port Forwarding): Complete Guide
Introduction
SSH tunneling, also known as SSH port forwarding, is a powerful technique that leverages the secure SSH protocol to create encrypted tunnels for forwarding network traffic between systems. This versatile method allows you to securely access services running on remote networks, bypass firewall restrictions, encrypt otherwise insecure protocols, and create temporary VPN-like connections without requiring administrative privileges or complex VPN infrastructure.
SSH tunneling works by encapsulating arbitrary network traffic within SSH connections, providing the same encryption and authentication benefits that protect your SSH sessions. Whether you need to access a database server behind a firewall, secure web browsing on untrusted networks, or expose local development servers to the Internet temporarily, SSH tunneling provides elegant solutions that system administrators and developers rely on daily.
This comprehensive guide covers all types of SSH tunneling including local port forwarding, remote port forwarding, dynamic port forwarding (SOCKS proxy), and advanced multi-hop scenarios. You'll learn practical use cases, security considerations, troubleshooting techniques, and best practices for implementing SSH tunnels in development, testing, and production environments.
Understanding SSH Tunneling Types
Local Port Forwarding
Local port forwarding creates a tunnel from your local machine to a remote destination through an SSH server. Traffic sent to a local port is forwarded through the SSH connection to a specified destination.
Syntax:
ssh -L [local_port]:[destination_host]:[destination_port] [user]@[ssh_server]
Flow:
Your Computer:local_port → SSH Server → destination_host:destination_port
Use Cases:
- Access remote databases securely
- Connect to services behind firewalls
- Secure web browsing through remote server
- Access internal network resources
Remote Port Forwarding
Remote port forwarding creates a tunnel from the SSH server back to your local machine or another destination. Traffic sent to a port on the SSH server is forwarded back through the SSH connection.
Syntax:
ssh -R [remote_port]:[destination_host]:[destination_port] [user]@[ssh_server]
Flow:
SSH Server:remote_port → Your Computer → destination_host:destination_port
Use Cases:
- Expose local development server to Internet
- Allow remote access to services on your machine
- Create reverse tunnels through NAT/firewalls
- Provide temporary external access
Dynamic Port Forwarding (SOCKS Proxy)
Dynamic port forwarding creates a SOCKS proxy server on your local machine. Applications configured to use this proxy have their traffic forwarded through the SSH server.
Syntax:
ssh -D [local_port] [user]@[ssh_server]
Flow:
Application → SOCKS Proxy:local_port → SSH Server → Destination
Use Cases:
- Secure all browser traffic on public WiFi
- Bypass geographical restrictions
- Route multiple applications through tunnel
- Temporary VPN alternative
Prerequisites
Before configuring SSH tunnels, ensure you have:
- SSH client installed (OpenSSH on Linux/macOS, PuTTY on Windows)
- SSH access to remote server with valid credentials
- Knowledge of target service's host and port
- Understanding of basic networking concepts
- Firewall permissions for required ports (if applicable)
- SSH key-based authentication configured (recommended)
Verify SSH Installation
# Check SSH client version
ssh -V
# Check SSH server status (on remote server)
sudo systemctl status sshd
Local Port Forwarding
Basic Local Port Forwarding
Forward local port to remote service:
# Forward local port 3307 to MySQL server on remote host
ssh -L 3307:localhost:3306 [email protected]
# Now connect to MySQL locally
mysql -h 127.0.0.1 -P 3307 -u dbuser -p
Explanation:
- Traffic to
localhost:3307is forwarded throughremote-server.com - To MySQL running on
localhost:3306(from remote server's perspective) - SSH session must remain open for tunnel to work
Binding to Specific Interface
# Bind to localhost only (default)
ssh -L 127.0.0.1:3307:localhost:3306 [email protected]
# Bind to all interfaces (allow connections from other machines)
ssh -L 0.0.0.0:3307:localhost:3306 [email protected]
# Bind to specific IP
ssh -L 192.168.1.100:3307:localhost:3306 [email protected]
Forwarding to Different Host
Forward to a service accessible from SSH server but on different host:
# Access database server through jump host
ssh -L 5432:db-server.internal:5432 [email protected]
# Connect to PostgreSQL
psql -h localhost -p 5432 -U dbuser database
Scenario:
jump-host.comcan accessdb-server.internal- Your machine cannot directly access
db-server.internal - Tunnel through
jump-host.comto reach database
Multiple Port Forwards
Create multiple tunnels in single SSH session:
# Forward multiple services
ssh -L 3307:localhost:3306 \
-L 6380:localhost:6379 \
-L 8080:localhost:80 \
[email protected]
# Now access:
# MySQL on localhost:3307
# Redis on localhost:6380
# Web server on localhost:8080
Background SSH Tunnel
Run SSH tunnel in background:
# -f: background process
# -N: don't execute remote command
ssh -f -N -L 3307:localhost:3306 [email protected]
# Verify tunnel is running
ps aux | grep ssh
# Kill background tunnel
pkill -f "ssh.*3307"
Persistent SSH Tunnel with autossh
Install and use autossh for automatic reconnection:
# Install autossh
sudo apt install autossh # Ubuntu/Debian
sudo dnf install autossh # RHEL/CentOS
# Create persistent tunnel
autossh -M 0 -f -N -L 3307:localhost:3306 [email protected]
# With reconnection options
autossh -M 0 -f -N \
-o "ServerAliveInterval 30" \
-o "ServerAliveCountMax 3" \
-L 3307:localhost:3306 [email protected]
Practical Example: Secure Database Access
Scenario: Access remote MySQL database securely
# Create tunnel to MySQL server
ssh -f -N -L 3307:localhost:3306 [email protected]
# Connect using local MySQL client
mysql -h 127.0.0.1 -P 3307 -u admin -p
# In database client
mysql> SELECT @@hostname;
# Shows remote server hostname, confirming tunnel works
Benefits:
- Encrypted connection to database
- No need to expose MySQL port to Internet
- Can use local database tools with remote servers
Remote Port Forwarding
Basic Remote Port Forwarding
Make local service accessible from remote server:
# Forward port 8080 on remote server to local port 3000
ssh -R 8080:localhost:3000 [email protected]
# On remote server, access:
curl http://localhost:8080
# Connects to your local machine's port 3000
Use Case: Expose local development server for testing
Expose Local Service to Internet
# Make local web app accessible from remote server
ssh -R 0.0.0.0:8080:localhost:3000 [email protected]
# Now accessible at:
# http://remote-server.com:8080
Note: Requires GatewayPorts yes in remote server's /etc/ssh/sshd_config
Configure GatewayPorts on SSH Server
# On remote SSH server
sudo nano /etc/ssh/sshd_config
# Add or modify:
GatewayPorts yes
# Or for more security:
GatewayPorts clientspecified
# Restart SSH service
sudo systemctl restart sshd
Reverse Tunnel Through Firewall
Access machine behind NAT/firewall:
# From machine behind firewall
ssh -R 2222:localhost:22 [email protected]
# From public server, SSH back to internal machine
ssh -p 2222 user@localhost
Scenario:
- Home/office machine behind NAT
- Need remote access without port forwarding on router
- Create reverse tunnel to public server
- Connect back through tunnel
Persistent Reverse Tunnel
# Use autossh for persistent reverse tunnel
autossh -M 0 -f -N \
-o "ServerAliveInterval 30" \
-o "ServerAliveCountMax 3" \
-R 2222:localhost:22 [email protected]
Create systemd service for automatic startup:
sudo nano /etc/systemd/system/reverse-tunnel.service
[Unit]
Description=Reverse SSH Tunnel
After=network.target
[Service]
User=tunneluser
ExecStart=/usr/bin/autossh -M 0 -N \
-o "ServerAliveInterval 30" \
-o "ServerAliveCountMax 3" \
-o "ExitOnForwardFailure yes" \
-R 2222:localhost:22 [email protected]
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable reverse-tunnel
sudo systemctl start reverse-tunnel
sudo systemctl status reverse-tunnel
Dynamic Port Forwarding (SOCKS Proxy)
Create SOCKS Proxy
# Create SOCKS proxy on local port 1080
ssh -D 1080 [email protected]
# Keep tunnel open
# Configure applications to use SOCKS5 proxy at localhost:1080
Configure Browser to Use SOCKS Proxy
Firefox:
- Settings → General → Network Settings
- Select "Manual proxy configuration"
- SOCKS Host:
localhost, Port:1080 - Select "SOCKS v5"
- Check "Proxy DNS when using SOCKS v5"
Chrome/Chromium (Linux):
google-chrome --proxy-server="socks5://localhost:1080"
System-wide proxy (Linux):
export ALL_PROXY=socks5://localhost:1080
Test SOCKS Proxy
# Check IP address before tunnel
curl ifconfig.me
# Start SOCKS tunnel
ssh -D 1080 -f -N [email protected]
# Check IP through proxy
curl --socks5 localhost:1080 ifconfig.me
# Should show remote server's IP
Using proxychains with SOCKS Tunnel
# Install proxychains
sudo apt install proxychains
# Configure proxychains
sudo nano /etc/proxychains.conf
# Add at end:
socks5 127.0.0.1 1080
# Use with any command
proxychains curl ifconfig.me
proxychains wget http://example.com
proxychains git clone https://github.com/user/repo
Secure Browsing on Public WiFi
# Create tunnel to your home/office server
ssh -D 1080 -f -N -C [email protected]
# Configure browser to use SOCKS proxy
# All traffic now encrypted and routed through home server
Advanced SSH Tunneling Techniques
Multi-Hop SSH Tunneling
Jump through multiple SSH servers:
# Method 1: ProxyJump (OpenSSH 7.3+)
ssh -J jump1.example.com,jump2.example.com [email protected]
# Method 2: ProxyCommand
ssh -o ProxyCommand="ssh -W %h:%p [email protected]" [email protected]
# With port forwarding through jump hosts
ssh -J jump.example.com -L 3307:db.internal:3306 [email protected]
Configure in ~/.ssh/config:
nano ~/.ssh/config
Host jump
HostName jump.example.com
User jumpuser
Host final
HostName final-destination.com
User finaluser
ProxyJump jump
Host db-tunnel
HostName final-destination.com
User finaluser
ProxyJump jump
LocalForward 3307 db.internal:3306
Usage:
# Connect through jump host
ssh final
# Create database tunnel through jump host
ssh db-tunnel
X11 Forwarding with Tunneling
# Forward X11 and create port tunnel
ssh -X -L 5901:localhost:5901 [email protected]
# Run GUI applications remotely
firefox &
VPN-like Configuration
Route all traffic through SSH tunnel:
# Create tunnel with IP forwarding
ssh -D 1080 -f -N -C [email protected]
# Configure routing (requires root)
# Route all traffic through SOCKS proxy
# Use tools like redsocks or proxychains-ng
Compressed Tunneling
# Enable compression for slow connections
ssh -C -D 1080 [email protected]
# Compression useful for:
# - Slow network connections
# - Text-heavy protocols
# - Transferring compressible data
Tunnel with Specific Cipher
# Use specific cipher for performance
ssh -c [email protected] -D 1080 [email protected]
# List available ciphers
ssh -Q cipher
SSH Tunnel Configuration Files
Using SSH Config File
Simplify complex tunnel commands:
nano ~/.ssh/config
# Database tunnel
Host dbtunnel
HostName remote-server.example.com
User dbadmin
LocalForward 3307 localhost:3306
LocalForward 6380 localhost:6379
# SOCKS proxy
Host socksproxy
HostName proxy-server.example.com
User proxyuser
DynamicForward 1080
# Reverse tunnel
Host reversetunnel
HostName public-server.example.com
User tunneluser
RemoteForward 8080 localhost:3000
Usage:
# Create database tunnel
ssh dbtunnel
# Create SOCKS proxy
ssh socksproxy
# Create reverse tunnel
ssh reversetunnel
Persistent Options
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
ExitOnForwardFailure yes
Port Forwarding Use Cases
Use Case 1: Secure Database Administration
# Access MySQL without exposing to Internet
ssh -L 3307:localhost:3306 [email protected]
# Connect with MySQL Workbench
# Host: 127.0.0.1
# Port: 3307
Use Case 2: Access Internal Web Applications
# Access internal corporate app
ssh -L 8080:internal-app.company.local:80 [email protected]
# Open browser to http://localhost:8080
Use Case 3: Development Server Testing
# Share local development with remote team
ssh -R 0.0.0.0:8080:localhost:3000 [email protected]
# Team accesses: http://demo-server.com:8080
Use Case 4: Bypass Geographic Restrictions
# Connect through server in different country
ssh -D 1080 [email protected]
# Configure browser to use SOCKS proxy
# Access region-restricted content
Use Case 5: Secure Remote Desktop
# Forward VNC through SSH
ssh -L 5901:localhost:5901 [email protected]
# Connect VNC client to localhost:5901
vncviewer localhost:5901
Use Case 6: Git Through Corporate Firewall
# Access GitHub through corporate firewall
ssh -D 1080 [email protected]
# Configure Git to use SOCKS proxy
git config --global http.proxy socks5://127.0.0.1:1080
git config --global https.proxy socks5://127.0.0.1:1080
Monitoring and Managing Tunnels
List Active SSH Tunnels
# Show SSH processes
ps aux | grep ssh
# Show detailed SSH connections
ss -tuln | grep ssh
netstat -tuln | grep ssh
# Show forwarded ports
lsof -i -n | grep ssh
Kill Specific Tunnel
# Find SSH process
ps aux | grep "ssh.*3307"
# Kill by PID
kill 12345
# Or by pattern
pkill -f "ssh.*3307"
Monitor Tunnel Traffic
# Monitor bandwidth usage
iftop -i tun0
# Monitor connections through tunnel
sudo tcpdump -i lo port 3307
# Check tunnel is forwarding
nc -zv localhost 3307
Debugging Tunnels
# Verbose SSH output
ssh -v -L 3307:localhost:3306 [email protected]
# Extra verbose
ssh -vvv -L 3307:localhost:3306 [email protected]
# Check specific error
journalctl -u sshd | grep "forward"
Security Considerations
Use SSH Key Authentication
# Generate SSH key
ssh-keygen -t ed25519 -C "tunnel-key"
# Copy to remote server
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
# Use key for tunnel
ssh -i ~/.ssh/id_ed25519 -L 3307:localhost:3306 [email protected]
Restrict Port Forwarding
On SSH server, limit forwarding capabilities:
sudo nano /etc/ssh/sshd_config
# Disable all forwarding
AllowTcpForwarding no
# Allow local forwarding only
AllowTcpForwarding local
# Allow remote forwarding only
AllowTcpForwarding remote
# Disable X11 forwarding if not needed
X11Forwarding no
# Restrict to specific users
Match User tunneluser
AllowTcpForwarding yes
Restart SSH:
sudo systemctl restart sshd
Limit Binding Interfaces
# Bind to localhost only (most secure)
ssh -L 127.0.0.1:3307:localhost:3306 [email protected]
# Avoid binding to 0.0.0.0 unless necessary
# This exposes port to all network interfaces
Use Firewall Rules
# Allow only localhost connections to forwarded port
sudo ufw allow from 127.0.0.1 to any port 3307
# Or with iptables
sudo iptables -A INPUT -p tcp --dport 3307 -s 127.0.0.1 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 3307 -j DROP
Monitor for Unauthorized Tunnels
# Check for unexpected SSH processes
ps aux | grep ssh
# Monitor SSH authentication logs
sudo tail -f /var/log/auth.log | grep sshd
# List all listening ports
sudo ss -tuln
Audit Tunnel Usage
# Enable SSH logging
sudo nano /etc/ssh/sshd_config
# Set:
LogLevel VERBOSE
# Monitor logs
sudo journalctl -u sshd -f
Troubleshooting Common Issues
Issue 1: Port Already in Use
Symptoms:
bind: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3307
Solutions:
# Find process using port
sudo lsof -i :3307
sudo ss -tulpn | grep 3307
# Kill process
sudo kill -9 <PID>
# Or use different local port
ssh -L 3308:localhost:3306 [email protected]
Issue 2: Connection Refused
Symptoms:
channel 2: open failed: connect failed: Connection refused
Diagnosis:
# On remote server, verify service is running
sudo systemctl status mysql
sudo ss -tuln | grep 3306
# Check service is listening on correct interface
netstat -tuln | grep 3306
Solutions:
# Start service on remote server
sudo systemctl start mysql
# Verify service binds to correct interface
# Edit service config to listen on 127.0.0.1 or 0.0.0.0
Issue 3: Tunnel Disconnects
Symptoms:
- SSH tunnel drops after period of inactivity
- "Broken pipe" error
Solutions:
# Enable keepalive in SSH config
nano ~/.ssh/config
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
# Or use in command
ssh -o ServerAliveInterval=60 -L 3307:localhost:3306 [email protected]
# Use autossh for auto-reconnect
autossh -M 0 -o "ServerAliveInterval 30" -L 3307:localhost:3306 [email protected]
Issue 4: Permission Denied (Port < 1024)
Symptoms:
bind: Permission denied
Solutions:
# Use port >= 1024
ssh -L 8080:localhost:80 [email protected]
# Or run with sudo (not recommended)
sudo ssh -L 80:localhost:80 [email protected]
Issue 5: GatewayPorts Not Working
Symptoms:
- Remote forward only accessible from localhost on remote server
Solutions:
# On SSH server
sudo nano /etc/ssh/sshd_config
# Change:
GatewayPorts yes
# Restart SSH
sudo systemctl restart sshd
# Verify setting
sudo sshd -T | grep gatewayports
Best Practices
1. Use SSH Config File
Organize tunnels in ~/.ssh/config:
Host prod-db
HostName production.example.com
User dbadmin
IdentityFile ~/.ssh/prod_key
LocalForward 3307 localhost:3306
ServerAliveInterval 60
2. Implement Least Privilege
- Create dedicated SSH users for tunneling
- Limit forwarding permissions
- Restrict shell access if only tunneling needed
# In sshd_config
Match User tunneluser
AllowTcpForwarding yes
X11Forwarding no
PermitTunnel no
ForceCommand /bin/false
3. Document Tunnel Configurations
Maintain documentation:
- Purpose of each tunnel
- Source and destination details
- Security considerations
- Contact information for troubleshooting
4. Monitor Tunnel Activity
# Create monitoring script
#!/bin/bash
# check-tunnel.sh
TUNNEL_PORT=3307
if ! nc -z localhost $TUNNEL_PORT; then
echo "Tunnel down, recreating..."
autossh -M 0 -f -N -L $TUNNEL_PORT:localhost:3306 [email protected]
fi
Add to cron:
*/5 * * * * /usr/local/bin/check-tunnel.sh
5. Use Automation Tools
For production environments:
- Ansible for tunnel deployment
- systemd services for persistent tunnels
- Monitoring tools (Nagios, Zabbix) for tunnel health checks
6. Secure Tunnel Endpoints
- Keep SSH server updated
- Use strong SSH keys (ED25519, RSA 4096)
- Enable fail2ban for brute force protection
- Regularly audit SSH access logs
Conclusion
SSH tunneling provides a versatile, secure method for port forwarding and creating encrypted network pathways without requiring VPN infrastructure or administrative privileges. Whether you need local port forwarding for secure database access, remote port forwarding to expose local services, or dynamic forwarding for SOCKS proxy functionality, SSH tunneling offers elegant solutions for common networking challenges.
Key takeaways:
- Local forwarding provides secure access to remote services
- Remote forwarding exposes local services through remote servers
- Dynamic forwarding creates SOCKS proxies for flexible routing
- SSH config files simplify complex tunnel management
- autossh ensures persistent tunnel connectivity
- Security considerations are essential for safe tunnel deployment
- Monitoring and documentation maintain reliable tunnel infrastructure
Master SSH tunneling techniques to enhance your system administration toolkit, improve security posture, and solve complex networking challenges with minimal overhead. The skills covered in this guide apply across development, testing, and production environments, making SSH tunneling an indispensable technique for modern infrastructure management.
For advanced scenarios, explore tools like sshuttle for transparent proxying, stunnel for legacy protocol encryption, and WireGuard for dedicated VPN requirements beyond SSH capabilities.


