14 min to read
Mastering SSH Tunneling for Secure Remote Access
A comprehensive guide to SSH tunneling techniques and real-world applications

Understanding SSH Tunneling
What is SSH Tunneling?
SSH tunneling (also called SSH port forwarding) creates an encrypted channel through an SSH connection to transfer data securely. This technique can:
- Provide secure access to services behind firewalls
- Encrypt traffic that would otherwise be transmitted in plaintext
- Allow access to internal network resources from external locations
- Bypass network restrictions in controlled environments
SSH tunneling is an essential skill for system administrators, developers, and security professionals.
Three Types of SSH Tunneling
Tunneling Type | Purpose |
---|---|
Local Port Forwarding | Forward traffic from a port on your local machine to a port on a remote server |
Remote Port Forwarding | Forward traffic from a port on the remote server to a port on your local machine |
Dynamic Port Forwarding | Create a SOCKS proxy for routing various types of traffic through an SSH tunnel |
Local Port Forwarding
Basic Syntax
ssh -L [local_port]:[destination_host]:[destination_port] [username]@[ssh_server]
Each part of the local port forwarding command has a specific purpose:
- local_port: The port on your local machine to listen on
- destination_host: The server you want to reach (from the SSH server's perspective)
- destination_port: The port on the destination host to connect to
- username@ssh_server: SSH server that will relay the connection
Practical Example
# Access a remote web server through SSH tunnel
ssh -L 8080:localhost:80 user@example.com
Use Cases for Local Port Forwarding
Scenario | Example Command |
---|---|
Accessing a database server | ssh -L 3306:database-server:3306 user@jump-server |
Accessing internal web applications | ssh -L 8080:internal-app:8080 user@bastion-host |
Securely browsing via a remote server | ssh -L 8080:www.example.com:80 user@ssh-server |
Remote Port Forwarding
Basic Syntax
ssh -R [remote_port]:[local_host]:[local_port] [username]@[ssh_server]
The components of remote port forwarding are:
- remote_port: The port on the remote SSH server to listen on
- local_host: The local machine or service to forward to (usually localhost)
- local_port: The port of your local service
- username@ssh_server: SSH server that will relay the connection
Practical Example
# Share a local development web server with a remote server
ssh -R 9000:localhost:3000 user@example.com
Use Cases for Remote Port Forwarding
Scenario | Example Command |
---|---|
Sharing local development server | ssh -R 8000:localhost:3000 user@public-server |
Remote support access | ssh -R 2222:localhost:22 user@support-server |
Exposing local API to remote services | ssh -R 5000:localhost:5000 user@integration-server |
Dynamic Port Forwarding
Basic Syntax
ssh -D [local_socks_port] [username]@[ssh_server]
The dynamic port forwarding command is simpler:
- local_socks_port: The port on your local machine for the SOCKS proxy
- username@ssh_server: SSH server that will relay connections
Applications must be configured to use the SOCKS proxy (e.g., browser settings).
Practical Example
# Create a SOCKS proxy on port 1080
ssh -D 1080 user@example.com
localhost:1080| B[SSH Client] B -->|Encrypted SSH| C[SSH Server] C --> D[Website 1] C --> E[Website 2] C --> F[Website 3] style A stroke:#333,stroke-width:1px,fill:#f5f5f5 style B stroke:#333,stroke-width:1px,fill:#a5d6a7 style C stroke:#333,stroke-width:1px,fill:#64b5f6 style D stroke:#333,stroke-width:1px,fill:#ffcc80 style E stroke:#333,stroke-width:1px,fill:#ffcc80 style F stroke:#333,stroke-width:1px,fill:#ffcc80
Use Cases for Dynamic Port Forwarding
Dynamic port forwarding is particularly useful for:
- Secure browsing on public Wi-Fi: Encrypt all your web traffic
- Bypassing geo-restrictions: Access region-restricted content
- Accessing multiple internal services: Without setting up individual port forwards
- Debugging network applications: Examine traffic through the proxy
Advanced Techniques for Production Environments
Setting Up Persistent Tunnels
These options make SSH tunnels more robust for production use:
- -f: Runs SSH in the background
- -N: Does not execute a remote command (tunnel only)
- -C: Compresses data for better performance
- -q: Quiet mode, suppresses most warnings and diagnostic messages
- -T: Disables pseudo-terminal allocation
Example: Redis Access via SSH Tunnel
Command | Description |
---|---|
ssh -fN -L 0.0.0.0:6395:redis-server.example.com:6379 user@jumphost |
Create a background tunnel to Redis, allowing connections from any IP to the local machine |
Using 0.0.0.0
binds the port to all network interfaces, making it accessible from any IP that can reach your machine. For better security:
- Use
127.0.0.1
instead to restrict access to localhost only - Implement firewall rules to restrict which IPs can connect
- Use key-based authentication instead of passwords
- Consider implementing jump hosts for sensitive environments
Creating Systemd Services for Persistent Tunnels
Step 1: Create Tunnel Script
#!/bin/bash
# Redis tunnel script
/usr/bin/ssh -N -i /home/ec2-user/.ssh/id_rsa \
-L 0.0.0.0:6395:redis-endpoint.example.com:6379 \
ec2-user@10.33.1.252 &
# Add more tunnels as needed
/usr/bin/ssh -N -i /home/ec2-user/.ssh/id_rsa \
-L 0.0.0.0:3336:mysql-endpoint.example.com:3306 \
ec2-user@10.33.1.252 &
wait
Step 2: Create Systemd Service File
# /etc/systemd/system/ssh-tunnels.service
[Unit]
Description=SSH Tunnels for Database Access
After=network.target
[Service]
User=ec2-user
ExecStart=/usr/local/bin/ssh-tunnels.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Step 3: Enable and Start the Service
# Set executable permissions
sudo chmod +x /usr/local/bin/ssh-tunnels.sh
# Reload systemd configuration
sudo systemctl daemon-reload
# Enable and start the service
sudo systemctl enable ssh-tunnels.service
sudo systemctl start ssh-tunnels.service
# Check status
sudo systemctl status ssh-tunnels.service
Useful commands for managing your SSH tunnel service:
sudo systemctl start ssh-tunnels.service
: Start the servicesudo systemctl stop ssh-tunnels.service
: Stop the servicesudo systemctl restart ssh-tunnels.service
: Restart the servicesudo systemctl status ssh-tunnels.service
: Check the service statussudo journalctl -u ssh-tunnels.service
: View service logs
SSH Tunnel Security Best Practices
Authentication and Access Control
Best Practice | Implementation |
---|---|
Use SSH Keys | Always prefer SSH key authentication over passwords. Disable password authentication when possible. |
Restrict SSH Users | Create dedicated users for tunneling with restricted shell access (/bin/false or /usr/sbin/nologin). |
Implement Jump Hosts | Use dedicated jump servers as intermediaries between untrusted and trusted networks. |
Limit SSH Access | Configure firewall rules to restrict SSH access to specific IP addresses or networks. |
Monitoring and Maintenance
Regular monitoring and maintenance activities are essential:
- Log Review: Regularly check SSH logs for unauthorized access attempts
- Connection Auditing: Monitor active tunnels and connections
- Key Rotation: Periodically rotate SSH keys
- Patching: Keep SSH software updated to address security vulnerabilities
- Alternative Paths: Have backup connection methods in case tunnels fail
Real-World Application Examples
Example 1: Secure Access to AWS ElastiCache
You need to access an AWS ElastiCache Redis instance that's only accessible from within a VPC, but you're working from an external location.
Solution: Create an SSH tunnel through an EC2 instance in the VPC.
# Create the tunnel
ssh -fN -L 6379:my-redis-cluster.abc123.amazonaws.com:6379 ec2-user@bastion-host.example.com
# Access Redis locally
redis-cli -h localhost -p 6379
Example 2: Secure Database Administration
# Create tunnel to MySQL database
ssh -L 3306:prod-db.internal:3306 user@jump-server.example.com
# Connect using local client
mysql -h 127.0.0.1 -u admin -p
Example 3: Accessing Internal Web Applications
You need to access internal monitoring dashboards or admin interfaces that are not exposed to the internet.
Solution: Set up local port forwarding to access the internal applications through a secure tunnel.
# Create tunnels for multiple internal services
ssh -L 8080:monitoring.internal:80 -L 8081:admin.internal:8080 user@jump-server.example.com
# Access in browser
# http://localhost:8080 (monitoring)
# http://localhost:8081 (admin interface)
Troubleshooting SSH Tunnels
Common Issues and Solutions
Problem | Solution |
---|---|
Connection refused |
|
Tunnel closes unexpectedly |
|
Slow performance |
|
Permission denied |
|
Debugging SSH Tunnels
Use these tools to diagnose SSH tunnel issues:
- Verbose Mode: Add
-v
,-vv
, or-vvv
for increasingly detailed logs - netstat/ss: Check if ports are listening (
netstat -tuln
orss -tuln
) - lsof: See which process is using a port (
lsof -i :port_number
) - tcpdump: Capture network traffic for analysis
- telnet/nc: Test if a port is reachable (
telnet host port
ornc -zv host port
)
SSH Tunnel Alternatives
Alternative | Description | Best For |
---|---|---|
VPN | Creates an encrypted network connection, routing all or selected traffic through it. | Full network integration, multiple users, persistent connections |
Reverse Proxies | Servers that sit between clients and backend services, forwarding requests and responses. | Web applications, load balancing, TLS termination |
Wireguard | Modern, fast VPN with simple setup and strong encryption. | Persistent connections, mobile devices, simpler than OpenVPN |
Cloudflare Tunnel | Service that creates a secure tunnel between your service and Cloudflare's edge network. | Public-facing web services without opening firewall ports |
Consider alternatives to SSH tunneling when:
- Multiple Users: Need to provide access to many users simultaneously
- All Traffic: Want to route all network traffic, not just specific ports
- Better Management: Need centralized management, monitoring, or scaling
- Performance: Require higher throughput or lower latency than SSH provides
- Compliance: Have specific compliance requirements that SSH can't satisfy
Key Points and Best Practices
-
Tunnel Types
- Local forwarding: Access remote services locally (-L)
- Remote forwarding: Expose local services remotely (-R)
- Dynamic forwarding: Create a SOCKS proxy for flexible routing (-D) -
Security Considerations
- Always use SSH key authentication, not passwords
- Restrict listening interfaces when possible (127.0.0.1 vs 0.0.0.0)
- Implement proper firewalls and access controls
- Regularly audit and rotate SSH keys -
Production Deployment
- Use systemd services for persistence and automatic restarts
- Consider tools like autossh for better reliability
- Implement monitoring to detect tunnel failures
- Document tunnel configurations and purposes -
Performance Tuning
- Enable compression for better performance over slow links
- Consider multiplexing multiple services over a single SSH connection
- Set appropriate timeouts and keepalives
- Monitor bandwidth and latency
Comments