Contents

Linux SSH Tunnels

In this blog, lets explore the various types of SSH tunnels using scenario based use cases.

  • Local port forwarding (Forward SSH Tunnel)
  • Remote port forwarding (Reverse SSH Tunnel)
  • Proxy SSH tunnel

Introduction

SSH (Secure Shell) tunnels are a powerful feature of the SSH protocol, which help in securing network traffic, circumventing firewalls, and accessing remote systems. At their core, SSH tunnels create encrypted connections between hosts over which data can be securely transmitted. SSH tunnels can be used in a wide range of scenarios such as remote system administration, secure file transfers, setting up secure proxies and bypassing network restrictions.

The versatility of SSH tunnels lies in their ability to forward any kind of network traffic over a secure SSH connection. Whether it’s securing a web browsing session, safely connecting to a remote database, or even exposing a local development server to the internet for testing, SSH tunnels can do it all. They work by leveraging the SSH protocol to encapsulate the traffic of other protocols, ensuring that data remains encrypted and secure as it travels over the internet or through potentially unsafe networks.

There are three primary types of SSH tunnels - Local Port Forwarding, Remote Port Forwarding, and Dynamic Port Forwarding. Each serves a unique purpose and solves different problems:

  1. Local Port Forwarding allows you to forward traffic from your local machine to a remote server via an SSH server.
  2. Remote Port Forwarding (often referred to as a reverse SSH tunnel) enables remote systems to access services on your local machine, even if it’s behind a firewall or lacks a public IP address.
  3. Dynamic Port Forwarding creates a SOCKS proxy that can route data from your client machine through an SSH server to various destinations, providing a flexible and secure method to browse the internet or access remote resources.

This blog will delve into each tunnel type, exploring how they work and when to use them using certain scenaios as an example.

Local Port Forwarding

Local port forwarding is also called as Forward SSH tunnel. In this mechanism, a resource running on a remote server port is made accessible on a local port. We can better appreciate the usefulness of this scheme by looking at an example scenario.

Lets say, we have a remote server (raspberry-pi) on which a hugo webserver is running on port 8080. The webserver has got bound to the local host and is only available from inside the same node. We need to connect to the webserver via a browser from our local machine which is not possible in this scenario.

In order to solve this problem, we can use local port forwarding where we establish an ssh tunnel from our local machine to the remote server. Any traffic going to the local port is forwarded via the SSH tunnel to the remote server. The SSH server there then forwards the traffic to the specified destination port.

The below diagram shows the scenario in detail:

/posts/linux/networking/ssh-tunnels/local-port-forwarding.png
Local Port Forwarding

The generic command to setup a local port forwarding is as below:

ssh -L <localhost>:<localport>:<remote_server_ip or dns>:<remote_server_port> user@remote_ssh_server

For our scenario it will translate to the below command, which is executed from the local server

ssh -N -L localhost:8000:192.168.10.10:8080 [email protected]

Mentioning localhost is optional

where:

  • -N: (optional) keeps the connection open (no login) by specifying that no remote commands should get executed.
  • -L: specifies local port forwarding is being used.
  • localhost:8000: The port on your local machine to listen on.
  • 192.168.10.10:8080: The destination server and port you want to access via the SSH server.
    Note
    The destination server can be any server reachable by the remote ssh server.
    
  • [email protected]: Your username and the hostname or IP address of the remote SSH server.
Limitations
  • TLS certificate validation will fail as the certificate Common Name does not match the localhost in the address bar.
  • Redirects to another url will fail.

Remote Port Forwarding

Remote port forwarding is also called as Reverse SSH tunnel. In this mechanism, the intention is to access a resource running on a local server (behind nat/firewall) via a port on a remote server. Thus enabling remote access to resources that are not directly reachable due to network restrictions.

Lets say, we have a service on our local machine and want to access it via the internet. But, we dont have an external static ip given to us by our ISP. We are behind thier nat.

In order to solve this problem, we can use remote port forwarding where we establish an ssh tunnel from our local machine to the remote server. Any traffic arriving at the remote server port is forwarded via the SSH tunnel to the local server. The SSH server there then forwards the traffic to the specified destination port.

The below diagram shows the scenario:

/posts/linux/networking/ssh-tunnels/remote-port-forwarding.png
Remote Port Forwarding

ssh -R <remote_server_ip or dns>:<remote_server_port>:<localhost>:<localport> user@remote_ssh_server

For our scenario it will translate to the below command, which is executed from the local server:

ssh -N -R 10.240.22.7:8000:localhost:8080 [email protected]

Mentioning remote server ip is optional

Tip

By default, SSH only allows port forwarding to localhost. Add the following line to /etc/ssh/sshd_config on your server to enable remote hosts to connect to the forwarded ports:

GatewayPorts yes
# Then, restart the SSH service:
sudo systemctl restart ssh

where:

  • -N: (optional) keeps the connection open (no login) by specifying that no remote commands should get executed.
  • -R: specifies remote port forwarding is being used.
  • 10.240.22.7: Remote server ip to which SSH tunnel will be established from the local server.
  • 8000: The port on which remote machine will listen on.
  • localhost:8080: The local server and port you want to expose to the remote server. In this case, the localhost is 192.168.10.10.
  • [email protected]: Your username and the hostname or IP address of the remote SSH server.

Dynamic Port Forwarding

Dynamic port forwarding is also called as SOCKS proxying. This mechanism is very flexibile. Unlike local port forwarding which forwards to a single port, dynamic port forwarding can forward to all the destinations and ports that are reachable via the destination server. In this way, you can think of the destination server as the bastion or jump server from which you can reach out to all the networks and services accessible from that server.

Dynamic port forwarding setups up a local SOCKS proxy server on the client machine. This proxy server listens on the specified local port, which by default is 1080. When traffic on the client machine is sent to this local port, it gets encrypted and forwarded via the SSH tunnel to the destination SSH server. From there, the SSH server routes the traffic to its final destination with in or outside (internet) the remote network.

Lets understand the dynamic port forwarding using a scenario. Lets say, we have a remote server on which worker nodes of a kubernetes cluster are running as virtual machines. We have deployed a nginx pod on one of the worker nodes and we have exposed it as a node port service. Now the nginx service is accessible via a exposed port on the worker node (VM). We want to access the nginx site using a browser on our client machine. As the worker node is in a private network which is accessible only from the remote server, its not possible to do this.

In order to solve this problem, lets use the dynamic port forwarding. Where a SOCKS proxy server is started on the client machine on port 1080 and all the traffic coming from the web browser are sent via the SOCKS tunnel to the remote server. The remote server in turn will deliver the traffic to the VM on the targetted port.

The below picture shows the scenario in detail:

/posts/linux/networking/ssh-tunnels/dynamic-port-forwarding.png
Dynamic Port Forwarding

The generic command to setup a local port forwarding is as below:

ssh -D 1080 user@remote-server

For our scenario it will translate to the below command, which is executed from the local server

ssh -N -D 1080 [email protected]

where:

  • -N: (optional) keeps the connection open (no login) by specifying that no remote commands should get executed.
  • -D: specifies dynamic port forwarding is being used.
  • user@remote-server : the user account used to ssh into the remote-server

SOCKS Proxy configuraiton in the Browser

Now, we can setup our browser to send its traffic via SOCKS proxy. For example, in Firefox browser you need to do the below steps:

  • Go to Settings
  • Scroll to the bottom and click on the settings button under the Network settings section.
  • Select Manual proxy configuration
  • SOCKS Host : localhost
  • SOCKS Port : 1080
  • Select SOCKS V5 protocol
  • Enable Proxy DNS when using SOCKS v5

/posts/linux/networking/ssh-tunnels/browser-socks-configuration.png
Browser Configuraiton

Testing dynamic port forwarding

Now we can start accessing any host, service or network as if we are calling them from the remote server. Due to this reason, we can access the nginx service by using 172.18.0.3:30080 from the web browser.

/posts/linux/networking/ssh-tunnels/browser-success.png
Accessing nginx service from the client machine

Hope this article helped you in getting a quick overview of the various types of SSH tunneling mechanisms.