SSH (Secure Shell) is more than just a tool for remote login—it’s a secure networking Swiss Army knife. One of its most powerful features is SSH tunneling (or port forwarding), which lets you encrypt and route traffic between local and remote systems. Whether you need to access a restricted database, share a local app with the world, or browse securely on public Wi-Fi, SSH tunneling has you covered.
In this guide, we’ll break down what SSH tunneling is, the three main types of tunnels, and step-by-step instructions to set them up. We’ll also cover advanced tips, troubleshooting, and critical security best practices.
Table of Contents#
- Introduction to SSH Tunneling
- Prerequisites
- Types of SSH Tunnels Explained
- Step-by-Step Guides for Each Tunnel Type
- Advanced Tips for Power Users
- Troubleshooting Common Issues
- Security Considerations You Can’t Ignore
- Conclusion
- References
2. Prerequisites#
Before we dive in, ensure you have:
- OpenSSH Client: Preinstalled on most Linux distributions (check with
ssh -V). - SSH Server Access: A remote server with SSH enabled (e.g., a VPS, home server, or cloud instance).
- Permissions:
- To bind to ports <1024 (privileged ports), you’ll need
sudo(non-root users can’t use these). - The SSH server must allow TCP forwarding (default: enabled).
- To bind to ports <1024 (privileged ports), you’ll need
- Basic CLI Knowledge: Comfort with running commands in a terminal.
3. Types of SSH Tunnels Explained#
SSH supports three tunnel types—each for a different use case. Let’s define them:
| Type | Direction | Use Case |
|---|---|---|
| Local Port Forwarding | Local → Remote | Access a remote service (e.g., MySQL) from your machine. |
| Remote Port Forwarding | Remote → Local | Expose a local service (e.g., dev server) to the internet. |
| Dynamic Port Forwarding | SOCKS Proxy (all traffic via tunnel) | Secure browsing on public Wi-Fi or bypass geo-blocks. |
4. Step-by-Step Guides for Each Tunnel Type#
We’ll use real-world examples for each tunnel. Let’s start with the most common: local port forwarding.
A. Local Port Forwarding#
Goal: Access a remote service (e.g., MySQL on remote-server.com:3306) from your local machine.
Example Scenario#
You’re working on your laptop and need to connect to a MySQL database on remote-server.com. The database is configured to only accept connections from localhost (a common security measure).
How to Set It Up#
-
Run the Local Forward Command:
ssh -L [local-port]:[remote-target-host]:[remote-target-port] [user]@[remote-server]For our MySQL example:
ssh -L 33060:localhost:3306 [email protected]-L: Flag for local port forwarding.33060: Local port on your machine (use any unused port >1024).localhost: The target host from the SSH server’s perspective (since MySQL is on the same server).3306: The remote service port (MySQL default).[email protected]: Your SSH user and server.
-
Test the Tunnel:
From your local machine, connect to the local port (33060) instead of the remote port (3306):mysql -u dbuser -p -h 127.0.0.1 -P 33060If successful, you’ll log into the remote MySQL server!
Pro Tip: Forward to a Different Host#
If the service is on a different server (e.g., db-server.internal:3306), adjust the target host:
ssh -L 33060:db-server.internal:3306 [email protected]The SSH server (remote-server.com) will forward traffic to db-server.internal:3306.
B. Remote Port Forwarding#
Goal: Expose a local service (e.g., a dev web server on localhost:8080) to the internet via a public SSH server.
Example Scenario#
You’re developing a web app on your laptop (localhost:8080). You want to share it with a colleague, but your laptop is behind a NAT/firewall (so they can’t access it directly).
How to Set It Up#
-
Run the Remote Forward Command:
ssh -R [remote-port]:[local-target-host]:[local-target-port] [user]@[public-server]For our dev server example:
ssh -R 80:localhost:8080 [email protected]-R: Flag for remote port forwarding.80: Port on the public server that will accept traffic.localhost: Your local dev server (from your laptop’s perspective).8080: Your local dev server port.
-
Allow External Access (Optional):
By default, the remote port (80onpublic-server.com) only listens onlocalhost. To let your colleague access it, you need to:
a. Edit the SSH server config onpublic-server.com:sudo nano /etc/ssh/sshd_configb. Set
GatewayPorts yes(this allows the remote port to listen on all interfaces).
c. Restart the SSH server:sudo systemctl restart sshd -
Test the Tunnel:
Your colleague can now access your dev server viahttp://public-server.com:80(or justhttp://public-server.comif using port 80).
C. Dynamic Port Forwarding (SOCKS Proxy)#
Goal: Route all traffic from your machine through an encrypted SSH tunnel (e.g., secure browsing on public Wi-Fi).
Example Scenario#
You’re at a coffee shop with untrusted Wi-Fi. You want to protect your web traffic from eavesdroppers.
How to Set It Up#
-
Run the Dynamic Forward Command:
ssh -D [local-proxy-port] [user]@[remote-server]For a SOCKS proxy on port
1080:ssh -D 1080 [email protected]-D: Flag for dynamic port forwarding (SOCKS proxy).1080: Local port where the SOCKS proxy will run.
-
Configure Your App to Use the Proxy:
You need to tell your browser (or any app) to use the SOCKS proxy atlocalhost:1080. Here’s how for Chrome:- Open Chrome → Settings → System → Open your computer’s proxy settings.
- Select “Manual proxy setup” → Enable “Use a proxy server”.
- Set:
- SOCKS proxy:
127.0.0.1 - Port:
1080
- SOCKS proxy:
- Check “Use SOCKS v5” (better for DNS privacy).
-
Test the Proxy:
Visit whatismyip.com to verify your IP matcheshome-server.com(your remote server’s IP).
5. Advanced Tips for Power Users#
Now that you’ve mastered the basics, here are some pro tips to level up:
A. Persistent Tunnels with autossh#
If your tunnel drops (e.g., network outage), autossh automatically restarts it. Install it first:
sudo apt install autossh # Debian/Ubuntu
sudo yum install autossh # RHEL/CentOSRun a persistent local forward:
autossh -M 0 -fN -L 33060:localhost:3306 [email protected]-M 0: Disable monitor port (use SSH keepalives instead).-fN: Run in the background (-f) without executing a remote command (-N).
B. Save Tunnel Configs in ~/.ssh/config#
Avoid typing long commands by saving tunnel settings in ~/.ssh/config. Example:
# ~/.ssh/config
Host mytunnel
HostName remote-server.com
User john
LocalForward 33060 localhost:3306
RemoteForward 80 localhost:8080
DynamicForward 1080Now you can start all tunnels with:
ssh mytunnelC. Use Non-Default SSH Ports#
If your SSH server uses a non-standard port (e.g., 2222), add -p [port] to your command:
ssh -L 33060:localhost:3306 -p 2222 [email protected]D. Debug Tunnels with Verbose Mode#
If a tunnel isn’t working, use -v (verbose) to see logs:
ssh -v -L 33060:localhost:3306 [email protected]6. Troubleshooting Common Issues#
Let’s fix the most frustrating problems:
A. “Bind: Address Already in Use”#
Cause: The local port you’re trying to use is occupied (e.g., 33060 is used by another app).
Fix:
- Find the process using the port:
lsof -i :33060 - Kill it (replace
1234with the PID):kill -9 1234 - Or use a different port (e.g.,
33061).
B. “Connection Refused”#
Cause: The remote target (e.g., localhost:3306) is down or blocked.
Fix:
- Check if the service is running on the remote server:
ssh [email protected] "systemctl status mysql" - Ensure the firewall on the remote server allows traffic to the port:
ssh [email protected] "ufw allow 3306"
C. “Permission Denied (Publickey)”#
Cause: You’re using password auth, but the SSH server requires keys.
Fix:
- Generate SSH keys (if you haven’t):
ssh-keygen -t ed25519 -C "[email protected]" - Copy keys to the server:
ssh-copy-id [email protected]
D. “GatewayPorts No”#
Cause: The SSH server blocks external access to remote ports.
Fix:
Edit /etc/ssh/sshd_config on the remote server:
sudo nano /etc/ssh/sshd_configSet:
GatewayPorts yes
Restart the SSH server:
sudo systemctl restart sshd7. Security Considerations You Can’t Ignore#
SSH tunneling is secure—but only if you use it wisely. Here’s what to avoid:
A. Don’t Forward Privileged Ports Unnecessarily#
Ports <1024 (e.g., 80, 443) require sudo. Only use them if you must (e.g., exposing a web server to the internet).
B. Use Key-Based Authentication#
Passwords are vulnerable to brute-force attacks. Always use SSH keys (we covered this earlier).
C. Restrict Tunnel Access#
On the SSH server, you can limit who can create tunnels. Edit /etc/ssh/sshd_config:
# Allow forwarding only for user "john"
Match User john
AllowTcpForwarding yes
# Block forwarding for everyone else
AllowTcpForwarding noD. Disable X11 Forwarding#
If you don’t need to run GUI apps remotely, disable X11 forwarding (it’s a security risk). Add this to /etc/ssh/sshd_config:
X11Forwarding no8. Conclusion#
SSH tunneling is one of the most underrated tools in a Linux user’s toolkit. Whether you’re a developer, sysadmin, or casual user, it lets you:
- Secure unencrypted traffic.
- Access restricted services.
- Share local apps with the world.
Remember:
- Choose the right tunnel type for your use case.
- Follow security best practices (keys > passwords, restrict access).
- Use
autosshfor persistent tunnels.
Now go experiment—tunneling is fun when it works!
9. References#
- OpenSSH Manual Pages
- Ubuntu SSH Guide
- Autossh Documentation
- SSH Port Forwarding Example
- sshd_config Man Page
Let me know if you have questions—happy tunneling! 🚀