SSH Tunneling

Tutorial and script to use SSH tunneling like a ninja.

Starting point

Information security established as best practice, that accesses between different platform components or system must be implemented using the principle of least privilege. What has not happened, for instance, to have wanted to connect from a client installed on its computer to a remote database, which did not have a direct connection but does through another intermediate server (for example a bastion host)?

If you have ever seen yourself in this kind of trouble, then I encourage you to continue reading since you will find the weapons you need to become a true ninja of the tunneled connections in this post.

Below are the most frequent use cases we can deal with, presented from the least to the greatest complexity.

The typical case…

Surely on more than one occasion you have seen it for yourself in some similar situation to the aforementioned, or if you have wanted to request a web service protected by IP whitelist from the network where your computer is connected, whose public IP is not included in that list, knowing that you have SSH access to an intermediate server whose public IP is contained in the whitelist. Well, as simple as using the intermediate server as a hop machine via the following SSH tunnel:

$ ssh -L <local_port>:<target_host>:<target_port> <user>@<hop_host>

Where:

Let’s look at a couple of practical examples:

# Connection from port 54321 of your computer to internal RDS instance with PostgreSQL, through a public EC2 instance with access to the RDS instance
$ ssh -L 54321:<rds_endpoint>:5432 <user>@<ec2_host>

# Connection from port 80 of your computer to a web service protected by IP whitelist, through a server with access to that service
$ sudo ssh -L 80:<service_url>:80 <user>@<server_host>

After establishing the SSH tunnel, you can connect to the target service through the port that you have specified as <local_port> parameter, which is open on your computer (localhost). Since this port is associated to the loopback interface (127.0.0.1), it will only be accessible from your own computer in which the SSH tunnel has been run.

Sharing is caring!

Let’s consider your willing to contribute to others: you decide you want to share the connection you have created through the SSH tunnel with other computers connected to the network. This way, they can also connect to the target service through the <local_port> port open on your computer, so what then? The solution is to use the gateway mode (-g) and your colleagues will be grateful:

$ ssh -g -L <local_port>:<target_host>:<target_port> <user>@<hop_host>

Considering the examples used previously:

# Connection from port 54321 of your computer to internal RDS instance with PostgreSQL, through a public EC2 instance with access to the RDS instance
$ ssh -g -L 54321:<rds_endpoint>:5432 <user>@<ec2_host>

# Connection from port 80 of your computer to a web service protected by IP whitelist, through a server with access to that service
$ sudo ssh -g -L 80:<service_url>:80 <user>@<server_host>

When an SSH tunnel is performed in gateway mode, the <local_port> port is opened on the network interface (eth0) of the computer, and as a consequence, the said port will be accessible from any computer which has access to the host from which the SSH tunnel has been established.

Tunnels as daemons!

Now that you have already shared the tunneled connection with your colleagues, it is very likely that you want to keep the SSH tunnel up for a long time, so it seems more comfortable to run it daemonized so that it does not depend on the terminal on which you have performed it. To achieve this, it is simply neccesary to add the -fN parameters to any of the ssh commands described above:

$ ssh -fN [-g] -L <local_port>:<target_host>:<target_port> <user>@<hop_host>

Publishing local services through reverse SSH tunnels in gateway mode

Have you ever wanted to publish a service that you have configured or deployed on your computer, or a service that is accessible from your network, so that other people who are working from networks without direct connection to your computer and neither to your network? Well, if you have SSH access to any public server (for example a public EC2 instance), you already have 90% of the work done! Now you just need to perform a reverse SSH tunnel in gateway mode:

$ ssh [-fN] -g -R <source_port>:<target_host>:<target_port> <user>@<source_host>

Where:

Let’s look at a couple of practical examples:

# Publication of a MySQL service configured on port 3306 of a server belonging to your network to be accessible from port 3308 of a public EC2 instance
$ ssh [-fN] -g -R 3308:<mysql_host>:3306 <user>@<ec2_host>

# Publication of a web application deployed on port 80 of your computer to be accessible from port 80 of a public EC2 instance
$ ssh [-fN] -g -R 80:localhost:80 root@<ec2_host>

Once establishing the SSH tunnel, the local service will be accessible from any source IP address which has access to the port to the one you have indicated in the <source_port> parameter opened on the network interface (eth0) of the source host (<source_host>).

A word of warning: To be able to perform these types of tunnels, it is necessary to enable the ‘GatewayPorts yes’ parameter in the configuration of sshd service (/etc/ssh/sshd_config) of the source host (public server), since for security reasons, this option is disabled by default.

Gilding the lily…

If all of this appeals to you and you’re eager to try some of this, I propose a couple of further links to exploration: