SSHwatch Insights Blog

SSH Tunneling: The Swiss Army Knife for Linux Power Users

Ever had that frustrating moment when you needed to access your work database from home, but the VPN was acting up? Or maybe you wanted to show a colleague your latest project running on localhost, without deploying it to a server? If you’ve been in these situations, you already understand why SSH tunneling is about to become your new best friend.

SSH tunneling is one of those hidden superpowers that experienced Linux users quietly rely on every day, yet many never fully explore. Sure, we all use SSH to log into remote servers, but its tunneling capabilities transform this everyday tool into something far more powerful – a secure networking Swiss Army knife that can solve dozens of connectivity headaches with just a few keystrokes.

“What I love about SSH tunneling is its elegant simplicity. One command creates a secure pathway through the most complex network environments.”

Think of SSH tunneling as creating an encrypted pipeline between computers. Through this pipeline, you can securely route traffic to otherwise inaccessible services, bypass network restrictions, or even share your local development environment with clients halfway across the world. The best part? You don’t need to install any extra software – if you’re using Linux, you already have everything you need.

What makes SSH tunneling particularly valuable is its ubiquity and simplicity compared to alternative solutions. Rather than deploying complex VPN infrastructures or dedicated proxy servers, SSH tunneling leverages existing SSH servers that are already present in most environments. This means you can establish secure networking channels without additional software, special permissions, or infrastructure changes. The ability to quickly create on-demand secure pathways through networks with just a single command makes SSH tunneling an essential technique in any Linux user’s toolkit, especially when working in restricted, segmented, or unfamiliar network environments.

Understanding SSH Tunneling Types

SSH offers three primary tunneling methods, each serving different use cases and solving specific networking problems. Understanding when to apply each type will significantly expand your ability to work around network limitations and security constraints.

  1. Local Port Forwarding: Forwards a local port to a remote destination, allowing you to access remote services through your local machine.
  2. Remote Port Forwarding: Exposes a local service to remote clients, enabling others to access services running on your machine.
  3. Dynamic Port Forwarding: Creates a SOCKS proxy for flexible routing of multiple applications’ traffic through an encrypted tunnel.

Each tunneling type serves distinct purposes and understanding their differences is crucial for applying them effectively in various scenarios.

Local Port Forwarding

Local port forwarding allows you to access a remote service as if it were running locally. This method creates a listening port on your local machine that forwards all connections to a specified remote destination through the SSH server. It’s particularly useful for:

  • Accessing services behind firewalls that restrict direct connections
  • Encrypting unencrypted traffic for sensitive applications
  • Bypassing network restrictions that block specific services
  • Simplifying connections to complex network environments

Basic Syntax

The syntax for local port forwarding follows this pattern:

ssh -L local_port:destination_host:destination_port ssh_server

In this command:

  • local_port is the port on your local machine where you’ll connect
  • destination_host is the target service as reachable from the SSH server
  • destination_port is the port number of the target service
  • ssh_server is the SSH server that will relay the connection

The SSH server acts as an intermediary, forwarding traffic from your local port to the destination service.

Practical Examples

Access a remote database securely:

ssh -L 3306:database.internal:3306 user@jumphost

This command creates a tunnel for MySQL traffic (port 3306). The database.internal host might be inaccessible directly from your machine, but the jumphost server can reach it. After executing this command, you can connect your MySQL client to localhost:3306, and the traffic will be securely tunneled to the internal database server. This is especially valuable when database servers don’t accept external connections for security reasons.

Access a web admin interface:

ssh -L 8080:internal-web.example:80 user@server

This establishes a tunnel to an internal web server. After running this command, opening http://localhost:8080 in your browser will show you the website hosted at internal-web.example:80. The traffic travels encrypted through the SSH tunnel to the server, which then forwards it to the internal web server. This approach is ideal for accessing internal administrative interfaces without exposing them to the internet or configuring a VPN.

“Your SSH client isn’t just for terminal access – it’s a complete encrypted networking toolkit hiding in plain sight.”

Remote Port Forwarding

Remote port forwarding is essentially the inverse of local forwarding. It exposes a local service to remote clients through an SSH server. This allows you to make services running on your local machine available to others without requiring direct connections to your computer. This is ideal for:

  • Sharing a development server with clients or teammates
  • Providing temporary access to local services for demonstration purposes
  • Creating simple demos without deploying to public servers
  • Working around NAT or firewall restrictions on your local network

Basic Syntax

The syntax for remote port forwarding follows this pattern:

ssh -R remote_port:local_host:local_port ssh_server

In this command:

  • remote_port is the port on the SSH server where the service will be accessible
  • local_host is usually localhost or 127.0.0.1 (your local machine)
  • local_port is the port of your local service
  • ssh_server is the SSH server that will expose your service

By default, the service will only be accessible on the SSH server itself. To make it available to other machines, you need to enable GatewayPorts in the SSH server configuration or use 0.0.0.0 binding (explained later).

Practical Examples

Share your local development server:

ssh -R 8080:localhost:3000 user@public-server

This command exposes your local development server running on port 3000 to the public server on port 8080. Anyone who can access the public server can now interact with your development environment by connecting to public-server:8080. This is extremely useful for showing work-in-progress to clients or getting help with debugging from colleagues without deploying your code or implementing complex networking.

Expose a local API for testing:

ssh -R 9000:localhost:5000 user@remote-server

This forwards your local API running on port 5000 to the remote server’s port 9000. This allows you to test your API with external services that need callback URLs, or to integrate with other systems temporarily during development. This technique is particularly valuable when working with webhook-based APIs that need to call back to your service.

Dynamic Port Forwarding (SOCKS Proxy)

Dynamic port forwarding creates a SOCKS proxy server on your local machine that can route traffic to multiple destinations through the SSH connection. Unlike the specific port forwarding methods discussed earlier, a SOCKS proxy can handle various types of traffic to different destinations all through a single tunnel. This is excellent for:

  • Secure browsing through an encrypted tunnel
  • Accessing multiple services behind firewalls without creating separate tunnels
  • Bypassing geographic restrictions or network filtering
  • Enhancing privacy by routing traffic through a trusted server

Basic Syntax

The syntax for dynamic port forwarding is simpler than the other methods:

ssh -D local_port ssh_server

In this command:

  • local_port is the port where the SOCKS proxy will listen on your machine
  • ssh_server is the exit point for your traffic

All applications configured to use this SOCKS proxy will have their traffic routed through the SSH server.

Practical Example

ssh -D 1080 user@remote-server

This command creates a SOCKS proxy listening on port 1080 of your local machine. All traffic sent to this proxy will be forwarded through your SSH connection to remote-server and then out to its destination. This effectively makes remote-server the origin point of your traffic as seen by the destination services.

To utilize this tunnel, you need to configure your applications to use the SOCKS proxy. For Firefox:

  1. Go to Preferences > Network Settings
  2. Select Manual proxy configuration
  3. Enter “127.0.0.1” for SOCKS Host and “1080” for Port
  4. Select SOCKS v5

With this configuration, all your Firefox web browsing will be tunneled through the SSH connection, which can help bypass restrictions, enhance security on untrusted networks, or access resources only available from the server’s location.

Advanced Techniques

Persistent Tunnels with autossh

Standard SSH tunnels will disconnect if the connection is interrupted, which can be frustrating when you need reliable access. The autossh utility solves this problem by automatically monitoring and restarting SSH connections when they drop, making tunnels more reliable for long-term use.

sudo apt install autossh
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 8080:internal:80 user@server

In this command:

  • -M 0 disables autossh’s built-in monitoring and relies on SSH’s alive mechanisms
  • ServerAliveInterval 30 sends a packet every 30 seconds to keep the connection active
  • ServerAliveCountMax 3 allows three missed responses before considering the connection dead
  • The rest of the command is a standard local port forwarding setup

This creates a self-healing tunnel that will automatically reconnect if the network connection is temporarily lost, making it ideal for critical services or unstable networks.

Creating System Services for Tunnels

For tunnels that should be permanently available, creating a systemd service provides a more robust solution than manual commands. This ensures the tunnel starts automatically on boot and restarts if it fails.

# Create /etc/systemd/system/ssh-tunnel.service
[Unit]
Description=SSH Tunnel
After=network.target

[Service]
ExecStart=/usr/bin/ssh -N -L 8080:internal-service:80 user@server
RestartSec=5
Restart=always
User=yourusername

[Install]
WantedBy=multi-user.target

In this service definition:

  • Description provides a name for the service
  • After=network.target ensures the network is available before starting
  • ExecStart contains the SSH command to establish the tunnel
  • -N tells SSH not to execute a remote command (tunnel only)
  • RestartSec=5 waits 5 seconds before restarting after a failure
  • Restart=always ensures the service restarts if it crashes
  • User specifies which user context runs the tunnel

Enable and start the service with:

sudo systemctl enable ssh-tunnel
sudo systemctl start ssh-tunnel

This configuration integrates your SSH tunnel with the system service management, providing better logging, automatic startup, and improved reliability compared to manual tunnel creation.

Jump Hosts and Multi-hop Tunneling

In complex network environments, you might need to traverse multiple SSH servers to reach your destination. The jump host feature (-J) allows you to specify a chain of servers to connect through before establishing your tunnel.

ssh -L 8080:final-destination:80 -J user1@jumphost1,user2@jumphost2 user3@destination

This command:

  1. First connects to jumphost1 as user1
  2. From there, connects to jumphost2 as user2
  3. Finally connects to destination as user3
  4. Establishes a local port forwarding from your 8080 to final-destination:80 as reachable from the destination server

This approach is particularly valuable in segmented networks where direct access to internal systems requires traversing multiple security boundaries, such as in high-security environments or complex corporate networks.

Tunnel Specific Applications

Sometimes you want only certain applications to use your tunnel, rather than configuring system-wide proxy settings. Tools like proxychains allow you to selectively route individual applications through your SSH tunnel.

# Using proxychains
sudo apt install proxychains
# Edit /etc/proxychains.conf and add:
# socks5 127.0.0.1 1080
ssh -D 1080 user@server
proxychains firefox

This sequence:

  1. Installs the proxychains utility for application-specific proxy routing
  2. Configures proxychains to use your SOCKS proxy on port 1080
  3. Establishes a dynamic SSH tunnel on port 1080
  4. Launches Firefox through proxychains, forcing all its traffic through the tunnel

This technique is valuable when you want to route specific applications through different tunnels or when you need tunneling for only certain tasks while maintaining direct connections for others.

Security Considerations

When implementing SSH tunnels, security should be a primary concern to avoid creating new vulnerabilities while solving connectivity problems.

  • Server Configuration: Ensure your SSH server is properly configured to control tunneling capabilities:
    # In /etc/ssh/sshd_config
    AllowTcpForwarding yes       # Enable tunneling
    GatewayPorts no              # Restrict to localhost by default
    PermitOpen host:port         # Restrict forwarding destinations
    

    These settings control:

    • Whether forwarding is allowed at all
    • If remote forwards are accessible only from the server itself or from other hosts
    • Which specific destinations can be reached through forwarding
  • Selective Forwarding: Allow specific users or restrict allowed destinations for better security control:
    # In /etc/ssh/sshd_config
    Match User developer
      PermitOpen database.internal:5432 redis.internal:6379
    

    This configuration allows the user “developer” to create tunnels only to the specified database and Redis services, preventing unauthorized access to other internal systems.

  • Audit Tunnels: Regularly monitor active connections and tunnels to detect unauthorized use:
    # On the SSH server
    ss -tulpn | grep ssh
    netstat -tlpn | grep ssh
    

    These commands show all listening ports associated with SSH, helping you identify unexpected tunnels that might indicate compromise or misuse.

Troubleshooting Common Issues

SSH tunneling can encounter various problems. Here’s how to diagnose and fix the most common issues:

  1. Connection Refused: This typically means the destination service isn’t running or isn’t accessible from the SSH server.
    • Verify the service is running: ssh user@server 'nc -zv destination_host destination_port'
    • Check firewall rules on the SSH server and destination host
    • Ensure the destination host and port are correct
  2. Permission Denied: This indicates your SSH server configuration restricts forwarding.
    • Check the SSH server’s sshd_config file for restrictive settings
    • Verify the user has permission to create tunnels
    • Look for AllowTcpForwarding no or restrictive PermitOpen settings
  3. Address Already In Use: This occurs when the local port you’re trying to use is already taken.
    • Choose a different local port above 1024 (non-privileged ports)
    • Check for existing processes: ss -tulpn | grep 'port_number'
    • Kill the process using the port or select an alternative port
  4. Slow Connections: Tunneling adds overhead, but excessive slowness indicates problems.
    • Enable compression with the -C flag to improve performance
    • Reduce verbosity with the -q option
    • Consider using an alternative method for large data transfers

Script: SSH Tunnel Manager

Here’s a simple script to manage your SSH tunnels. This utility simplifies the creation, listing, and termination of tunnels through a consistent interface.

#!/bin/bash
# ssh-tunnel.sh - Manage SSH tunnels

case "$1" in
  local)
    ssh -fN -L "$2":"$3":"$4" "$5"
    echo "Local tunnel created: localhost:$2 -> $3:$4 via $5"
    ;;
  remote)
    ssh -fN -R "$2":localhost:"$3" "$4"
    echo "Remote tunnel created: $4:$2 -> localhost:$3"
    ;;
  dynamic)
    ssh -fN -D "$2" "$3"
    echo "SOCKS proxy created on localhost:$2 via $3"
    ;;
  list)
    ps aux | grep 'ssh -fN' | grep -v grep
    ;;
  kill)
    tunnel_pid=$(ps aux | grep "ssh -fN" | grep "$2" | awk '{print $2}')
    if [ -n "$tunnel_pid" ]; then
      kill "$tunnel_pid"
      echo "Tunnel killed: $tunnel_pid"
    else
      echo "No matching tunnel found"
    fi
    ;;
  *)
    echo "Usage:"
    echo "  $0 local local_port destination_host destination_port ssh_server"
    echo "  $0 remote remote_port local_port ssh_server"
    echo "  $0 dynamic proxy_port ssh_server"
    echo "  $0 list"
    echo "  $0 kill [search_term]"
    ;;
esac

This script provides several key functions:

  • local creates a local port forwarding tunnel, running it in the background with the -f (fork) and -N (no command) options to prevent it from occupying your terminal
  • remote establishes a remote port forwarding tunnel, also in the background
  • dynamic sets up a SOCKS proxy as a background process
  • list shows all currently running SSH tunnels, helping you keep track of active connections
  • kill terminates a tunnel matching the specified search term by finding and ending its process

Make it executable with chmod +x ssh-tunnel.sh and use it to simplify tunnel creation. For example, instead of remembering the full syntax, you can just run:

./ssh-tunnel.sh local 8080 internal-web.example 80 user@server

This creates a local port forwarding tunnel from your port 8080 to the internal web server, running in the background so you can continue using your terminal for other tasks.

Conclusion

SSH tunneling is a powerful technique that extends far beyond simple remote access. By mastering these methods, Linux users can enhance security, overcome network limitations, and simplify complex networking tasks. The ability to create encrypted tunnels through potentially hostile networks provides both security and flexibility that few other tools can match.

Whether you’re a system administrator needing to access restricted services, a developer sharing local work with clients, or a privacy-conscious user seeking to protect your browsing, SSH tunneling offers elegant solutions to diverse networking challenges. By incorporating these techniques into your workflow, you’ll unlock new levels of productivity and security that make the learning investment well worthwhile.

Remember that the greatest strength of SSH tunneling is its ability to solve complex connectivity problems with simple, standard tools available on virtually every Linux system. No additional software is required beyond the SSH client you likely already use daily, making tunneling a zero-cost solution to expensive networking problems.

Secure Your Infrastructure Today!

Sign up now to gain comprehensive insights into your SSH access logs. Start monitoring, alerting, and analyzing your entire infrastructure effortlessly.
Get started for free

Book a demo

Fill in the form below to book a demo without obligation.
Request a demo