dotlinux blog

How to Create SSH Tunneling or Port Forwarding in Linux

SSH tunneling works by encapsulating non-SSH traffic (e.g., HTTP, MySQL) within an encrypted SSH connection. This:

  • Secures unencrypted protocols (e.g., HTTP → encrypted SSH tunnel).
  • Bypasses firewalls/NAT (e.g., access a local dev server from the internet).
  • Grants access to restricted services (e.g., a remote database that only accepts localhost connections).

At a high level, here’s how it works:

  1. You initiate an SSH connection to a remote server.
  2. You specify a local port (on your machine) and a remote target (host:port on the SSH server’s network).
  3. The SSH client forwards traffic from your local port to the remote target via the encrypted tunnel.
2026-04

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#

  1. Introduction to SSH Tunneling
  2. Prerequisites
  3. Types of SSH Tunnels Explained
  4. Step-by-Step Guides for Each Tunnel Type
  5. Advanced Tips for Power Users
  6. Troubleshooting Common Issues
  7. Security Considerations You Can’t Ignore
  8. Conclusion
  9. 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).
  • 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:

TypeDirectionUse Case
Local Port ForwardingLocal → RemoteAccess a remote service (e.g., MySQL) from your machine.
Remote Port ForwardingRemote → LocalExpose a local service (e.g., dev server) to the internet.
Dynamic Port ForwardingSOCKS 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#

  1. 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.
  2. 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 33060

    If 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#

  1. 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.
  2. Allow External Access (Optional):
    By default, the remote port (80 on public-server.com) only listens on localhost. To let your colleague access it, you need to:
    a. Edit the SSH server config on public-server.com:

    sudo nano /etc/ssh/sshd_config

    b. Set GatewayPorts yes (this allows the remote port to listen on all interfaces).
    c. Restart the SSH server:

    sudo systemctl restart sshd
  3. Test the Tunnel:
    Your colleague can now access your dev server via http://public-server.com:80 (or just http://public-server.com if 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#

  1. 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.
  2. Configure Your App to Use the Proxy:
    You need to tell your browser (or any app) to use the SOCKS proxy at localhost: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
    • Check “Use SOCKS v5” (better for DNS privacy).
  3. Test the Proxy:
    Visit whatismyip.com to verify your IP matches home-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/CentOS

Run 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 1080

Now you can start all tunnels with:

ssh mytunnel

C. 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:

  1. Find the process using the port:
    lsof -i :33060
  2. Kill it (replace 1234 with the PID):
    kill -9 1234
  3. 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:

  1. Generate SSH keys (if you haven’t):
    ssh-keygen -t ed25519 -C "[email protected]"
  2. 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_config

Set:

GatewayPorts yes

Restart the SSH server:

sudo systemctl restart sshd

7. 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 no

D. 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 no

8. 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 autossh for persistent tunnels.

Now go experiment—tunneling is fun when it works!

9. References#

Let me know if you have questions—happy tunneling! 🚀