SSH port forwarding, otherwise known as SSH tunneling, is a method for sending traffic from a client machine port to a server port, or vice versa, through a secured SSH tunnel. Almost all Linux systems have SSH clients and SSH servers installed by default making this an easily accessible tool.

Use Cases

SSH Tunneling can be used to add encryption to traffic that otherwise would not be encrypted. If your business had an old legacy application that used telnet to communicate information back to a server, you could secure that connection with an SSH tunnel.

It can also be used for testing applications without opening their ports to the rest of the network. For example, if you were building a web-based application, you could leave port 80 and 443 closed to the outside on your host firewall and use an SSH tunnel to connect to the web server from your workstation to test it's functionality.

Employees often use it for back doors into their companies internal network. Most of the time for innocent reasons like convenience and allowing themselves access from home.

Often SSH tunneling is used for nefarious reasons. Malware and crackers use it to open access to the internal network of their targets from the internet or to mask data transmissions out to the internet.

Nomenclature

In the following examples we will be using 3 systems. Below is an outline of the system names, purpose and IP addresses.

  • Putor - Main Workstation - 10.0.0.2
  • Centos6 - MySQL Database Server - 192.168.122.91
  • Fenrir - Web Server - 10.0.0.5

Different Kinds of SSH Forwarding

Local Forwarding

Local forwarding is used to send traffic destined for a local port to a port on a remote machine through the SSH tunnel. The SSH client will listen for connections to the local port, when it receives that connection it forwards it to the remote system on the specified port.

Local Forwarding - Example 1

For this first example we will connect our local machine named putor to a legacy MySQL database server named centos6 to encrypt the traffic.

We will forward local port 6603 (putor:6603) to the remote port (centos6:3306) and make the database connection.

A diagram showing a basic example of SSH forwarding from a computer to a server
Diagram of SSH tunnel 1

Here are some configuration parameters of the server. You will see that we have MySQL running and listening on port 3306, BUT iptables (host firewall) DOES not have 3306 open. This means that normal communication to the database is blocked by the firewall. In this example, we are going to connect to the database through the SSH tunnel, so we do not need the port opened on the host system.

[root@centos6 ~]# ip addr | grep "inet 192"
inet 192.168.122.99/24 brd 192.168.122.255 scope global eth0
[root@centos6 ~]# service mysqld status
mysqld (pid 2794) is running…
[root@centos6 ~]# iptables -L -vn
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
984 86845 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
1 60 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
1 60 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
Chain OUTPUT (policy ACCEPT 576 packets, 71338 bytes)
pkts bytes target prot opt in out source destination
[root@centos6 ~]# netstat -anp | grep 3306
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 2794/mysqld

Now let's setup our SSH Forwarding to allow use to connect to the MySQL database on the remote server through the SSH connection.

[savona@putor ~]$ ssh -L 6033:127.0.0.1:3306 centos6
savona@centos6's password:
Last login: Sun Jan 27 18:32:11 2019 from 192.168.122.1

To reiterate, the command above forward port 6033 on the local system to port 3306 on the remote system.

Now we have the tunnel up, we can connect to the database from our workstation.

[savona@putor ~]$ mysql -s -h 127.0.0.1 -p --port 6033
Enter password:
MySQL [(none)]> show databases;
Database
information_schema
mysql
putorius
test
MySQL [(none)]>

Now my workstation (putor) is listening on port 6603 for incoming connections and it will forward those connections to the MySQL server at centos6:3306. This INCLUDES connections for another system. I can now make this database open to other systems the network, by allowing port 6603 on my firewall.

[savona@putor ~]$ netstat -anp | grep 6033
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 127.0.0.1:6033 0.0.0.0:* LISTEN 25479/ssh
tcp 0 0 127.0.0.1:39864 127.0.0.1:6033 TIME_WAIT -
tcp6 0 0 ::1:6033 :::* LISTEN 25479/ssh

To stop this behavior, we need to specify a BIND address. This mean we will tell SSH to only allows connections through this tunnel from the IP address we specify. Here is how that forwarding statement looks.

ssh -L putor:6033:127.0.0.1:3306 192.168.122.99

That is how local forwarding works. If you have any questions feel free to leave them in the comments.

Local Forwarding - Example 2

For this next example, we will use the the Centos6 system as a jump box.

We will open a connection to centos6, and forward all connections to port 8443 on the workstation to port 8443 on Fenrir, the web server.

A diagram showing an example of SSH forwarding packets through a server to another server
ssh -L 8443:fenrir:8443 centos6

Now that we have the tunnel up, let's open our web browser and go to https://putor:8443.

Browser screenshot on Linux showing access to a web application

In the above example, the request from our browser went to our local port 8443, which was sent through centos6 and off to Fenrir.

That's local forwarding in a nutshell. I hope I was able to explain it in a clear manner. If you have any questions leave them in the comments.

Remote Forwarding

Remote SSH forwarding works very similar to local forwarding, but in reverse. When you make the connection it forwards the port on the remote host back to the local host port.

Remote Forwarding - Example 1

So let's make a remote forwarding connection from centos6 MySQL server, back to my workstation and forward port 7777 to the database port 3306.

A diagram showing reverse ssh forwarding ports from a server to a workstation

As you can see from the diagram above, we are initiating the connection from the database server (centos6) for this example. We will connect to my workstation (putor) and forward port 7777 on putor back to 3306 on centos6.

[savona@centos6 ~]$ ssh -R 7777:127.0.0.1:3306 putor
savona@putor's password:
Last login: Sun Jan 27 19:47:08 2019 from 192.168.122.99

Now let's test the database connection to port 7777 on putor.

[savona@putor ~]$ mysql -s -h 127.0.0.1 -p --port 7777
Enter password:
MySQL [(none)]>

Success. We were able to connect back through the tunnel using remote forwarding.

Remote Forwarding - Example 2

For this example, we will initial a remote forwarding tunnel from Fenrir:8443 to Putor:6556. This will allow putor to connect to localhost:6565 and open the web application on Fenrir:8443.

Just as with local forwarding, if you reverse forward a port from a server to a client, anybody who can access that port on the server will be forwarded to the client. This can be controlled with the GatewayPorts option in the SSH daemon configuration (see server configurations below).

A second diagram showing reverse ssh forwarding ports from a server to a workstation
Remote Forwarding Diagram 2

NOTE: It is important to recognize that we are initiating the connect from the server.

Let's create the tunnel.

[savona@Fenrir ~]$ ssh -R 6565:localhost:8443 putor
savona@putor's password:
Last login: Sun Jan 27 20:00:11 2019 from 10.0.0.5

Now we should be able to open a browser on Putor and navigate to https://localhost:6565 and open the web application on Fenrir.

A browser screenshot on Linux showing access to a web application

Success. We were able to connect to the web application from putor through the tunnel we initiated on the server.

Server Configurations

The SSH server must be configured to allow port forwarding. By default, port forwarding is allowed. You can control this behavior by using the AllowTCPForwarding option. To allow SSH Forwarding, open the SSH daemon configuration file (usually /etc/ssh/sshd_config) and add or modify the following line.

AllowTCPForwarding yes

To denied forwarding:

AllowTCPForwarding no

When reverse forwarding is enabled from a one system to another, anyone that has access to the port on that host server will be forwarded to the client. This can be controlled by using the GatewayPorts option in the SSH daemon configuration.

To prevent connecting to forwarded ports from outside the server, add or modify the following line in the configuration.

GatewayPorts no

To allow anyone to connect to the forwarding ports:

GatewayPorts yes

Conclusion

SSH Forwarding can be used local or remotely. It is a great tool for Linux System Administrators to have in their toolkit, but can easily be abused. Follow best practices and explicitly disable port forwarding if you are not using it.