PolarSPARC



Bhaskar S 11/08/2020


Overview

In networking, a Tunnel is used to encapsulate a communication protocol that is not supported by the network inside a protocol that is supported by the network.

The following are some of terms used in this article:

Term Description
SSH short for Secure SHell is a protocol that sets up an encrypted connection between two nodes over an unsecured network using a Client-Server architecture
SSH Server a server that listens on the TCP port 22 for incoming SSH client requests, then authenticates those client requests, and provides a command prompt
SSH Client a client used to connect to the remote SSH Server on a specific network node
SSH Tunnel a method of encapsulating and transmitting arbitrary networking data over an encrypted SSH connection between a client node and a server node
SSH Port Forwarding another name for SSH Tunnel

So, why do we need tunneling ??? The following are some of the reasons:

There are 3 types of SSH Tunnel options as listed below:

  1. Local Port Forwarding

  2. Remote Port Forwarding

  3. Dynamic Port Forwarding

We will discuss and demonstrate each of the above options in the following sections.

Setup

The setup will be on a Ubuntu 20.04 LTS based Linux desktop. For the demonstrations, we will create an environment with 3 virtual machines running on the hypervisor VirtualBox.

The following diagram illustrates the environment setup:

Environment
Environment

The following are some of the highlights of the 3 virtual machines:

Open a Terminal window in each of the 3 virtual machines vm-1 thru vm-3 and install Python Flask, Net Tools, and SSH Server by executing the following command:

$ sudo apt install python3-flask net-tools openssh-server -y

The following would be a typical output:

Output.1

Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  javascript-common libjs-jquery ncurses-term openssh-sftp-server python3-itsdangerous python3-jinja2 python3-markupsafe
  python3-openssl python3-pyinotify python3-werkzeug ssh-import-id
Suggested packages:
  apache2 | lighttpd | httpd molly-guard monkeysphere ssh-askpass python-flask-doc python-jinja2-doc python-openssl-doc
  python3-openssl-dbg python-pyinotify-doc ipython3 python-werkzeug-doc python3-lxml python3-termcolor python3-watchdog
The following NEW packages will be installed:
  javascript-common libjs-jquery ncurses-term net-tools openssh-server openssh-sftp-server python3-flask python3-itsdangerous
  python3-jinja2 python3-markupsafe python3-openssl python3-pyinotify python3-werkzeug ssh-import-id
0 upgraded, 14 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,478 kB of archives.
After this operation, 9,096 kB of additional disk space will be used.
Get:1 http://us.archive.ubuntu.com/ubuntu focal/main amd64 javascript-common all 11 [6,066 B]
Get:2 http://us.archive.ubuntu.com/ubuntu focal/main amd64 libjs-jquery all 3.3.1~dfsg-3 [329 kB]
Get:3 http://us.archive.ubuntu.com/ubuntu focal/main amd64 ncurses-term all 6.2-0ubuntu2 [249 kB]
Get:4 http://us.archive.ubuntu.com/ubuntu focal/main amd64 net-tools amd64 1.60+git20180626.aebd88e-1ubuntu1 [196 kB]
Get:5 http://us.archive.ubuntu.com/ubuntu focal-updates/main amd64 openssh-sftp-server amd64 1:8.2p1-4ubuntu0.1 [51.5 kB]
Get:6 http://us.archive.ubuntu.com/ubuntu focal-updates/main amd64 openssh-server amd64 1:8.2p1-4ubuntu0.1 [377 kB]
Get:7 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-itsdangerous all 1.1.0-1 [14.6 kB]
Get:9 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-markupsafe amd64 1.1.0-1build2 [13.9 kB]
Get:9 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-jinja2 all 2.10.1-2 [95.5 kB]
Get:10 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-werkzeug all 0.16.1+dfsg1-2 [183 kB]
Get:11 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-flask all 1.1.1-2 [80.3 kB]
Get:12 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-openssl all 19.0.0-1build1 [43.3 kB]
Get:13 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-pyinotify all 0.9.6-1.2ubuntu1 [24.8 kB]
Get:14 http://us.archive.ubuntu.com/ubuntu focal/main amd64 ssh-import-id all 5.10-0ubuntu1 [10.0 kB]
Fetched 1,478 kB in 0s (5,095 kB/s)  
Preconfiguring packages ...
Selecting previously unselected package javascript-common.
(Reading database ... 192970 files and directories currently installed.)
Preparing to unpack .../00-javascript-common_11_all.deb ...
Unpacking javascript-common (11) ...
Selecting previously unselected package libjs-jquery.
Preparing to unpack .../01-libjs-jquery_3.3.1~dfsg-3_all.deb ...
Unpacking libjs-jquery (3.3.1~dfsg-3) ...
Selecting previously unselected package ncurses-term.
Preparing to unpack .../02-ncurses-term_6.2-0ubuntu2_all.deb ...
Unpacking ncurses-term (6.2-0ubuntu2) ...
Preparing to unpack .../net-tools_1.60+git20180626.aebd88e-1ubuntu1_amd64.deb ...
Unpacking net-tools (1.60+git20180626.aebd88e-1ubuntu1) ...
Selecting previously unselected package openssh-sftp-server.
Preparing to unpack .../03-openssh-sftp-server_1%3a8.2p1-4ubuntu0.1_amd64.deb ...
Unpacking openssh-sftp-server (1:8.2p1-4ubuntu0.1) ...
Selecting previously unselected package openssh-server.
Preparing to unpack .../04-openssh-server_1%3a8.2p1-4ubuntu0.1_amd64.deb ...
Unpacking openssh-server (1:8.2p1-4ubuntu0.1) ...
Selecting previously unselected package python3-itsdangerous.
Preparing to unpack .../05-python3-itsdangerous_1.1.0-1_all.deb ...
Unpacking python3-itsdangerous (1.1.0-1) ...
Selecting previously unselected package python3-markupsafe.
Preparing to unpack .../06-python3-markupsafe_1.1.0-1build2_amd64.deb ...
Unpacking python3-markupsafe (1.1.0-1build2) ...
Selecting previously unselected package python3-jinja2.
Preparing to unpack .../07-python3-jinja2_2.10.1-2_all.deb ...
Unpacking python3-jinja2 (2.10.1-2) ...
Selecting previously unselected package python3-werkzeug.
Preparing to unpack .../08-python3-werkzeug_0.16.1+dfsg1-2_all.deb ...
Unpacking python3-werkzeug (0.16.1+dfsg1-2) ...
Selecting previously unselected package python3-flask.
Preparing to unpack .../09-python3-flask_1.1.1-2_all.deb ...
Unpacking python3-flask (1.1.1-2) ...
Selecting previously unselected package python3-openssl.
Preparing to unpack .../10-python3-openssl_19.0.0-1build1_all.deb ...
Unpacking python3-openssl (19.0.0-1build1) ...
Selecting previously unselected package python3-pyinotify.
Preparing to unpack .../11-python3-pyinotify_0.9.6-1.2ubuntu1_all.deb ...
Unpacking python3-pyinotify (0.9.6-1.2ubuntu1) ...
Selecting previously unselected package ssh-import-id.
Preparing to unpack .../12-ssh-import-id_5.10-0ubuntu1_all.deb ...
Unpacking ssh-import-id (5.10-0ubuntu1) ...
Setting up javascript-common (11) ...
Setting up net-tools (1.60+git20180626.aebd88e-1ubuntu1) ...
Setting up openssh-sftp-server (1:8.2p1-4ubuntu0.1) ...
Setting up openssh-server (1:8.2p1-4ubuntu0.1) ...
Creating config file /etc/ssh/sshd_config with new version
Creating SSH2 RSA key; this may take some time ...
3072 SHA256:OVmIaDeM2PCBtB6O5tddPIC3q4nuZVdqfcs/7A3QM5A root@vm-3 (RSA)
Creating SSH2 ECDSA key; this may take some time ...
256 SHA256:qDrGgauXE9LwZ6S1j4fjbY0LIPyrL+YSU8iq+PbR7jM root@vm-3 (ECDSA)
Creating SSH2 ED25519 key; this may take some time ...
256 SHA256:WBk+gqOXD47VoAJrw+JeZLxQlzBWdaKFRxi5xfPAkYg root@vm-3 (ED25519)
Created symlink /etc/systemd/system/sshd.service → /lib/systemd/system/ssh.service.
Created symlink /etc/systemd/system/multi-user.target.wants/ssh.service → /lib/systemd/system/ssh.service.
rescue-ssh.target is a disabled or a static unit, not starting it.
Setting up python3-openssl (19.0.0-1build1) ...
Setting up ssh-import-id (5.10-0ubuntu1) ...
Attempting to convert /etc/ssh/ssh_import_id
Setting up python3-pyinotify (0.9.6-1.2ubuntu1) ...
Setting up python3-itsdangerous (1.1.0-1) ...
Setting up python3-markupsafe (1.1.0-1build2) ...
Setting up python3-jinja2 (2.10.1-2) ...
Setting up libjs-jquery (3.3.1~dfsg-3) ...
Setting up ncurses-term (6.2-0ubuntu2) ...
Setting up python3-werkzeug (0.16.1+dfsg1-2) ...
Setting up python3-flask (1.1.1-2) ...
Processing triggers for systemd (245.4-4ubuntu3.3) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for ufw (0.36-6) ...

Local Port Forwarding

Assuming vm-3 is hosting a useful web application on 10.0.2.4, it is *ONLY* accessible within the 10.0.2.x network. What if a client on the vm-2 wants to access the web application ???

In this situation, one could use Local Port Forwarding SSH Tunnel option to allow the client vm-2 running on 192.168.56.104 to access the web application server running on the 10.0.2.x network.

The following is the code for the simple Python based web application:

Web.py
import sys
from datetime import datetime
from flask import Flask

app = Flask(__name__)
HOST = ''
PORT = 0

HTML = """
<html>
<head>
  <title>From %s</title>
</head>
<body>
  <h3>Hello from %s:%d</h3>
  <h3>Timestamp: %s</h3>
</body>
</html>
"""

@app.route('/')
def index():
    return HTML % (HOST, HOST, PORT, datetime.now())

if __name__ == '__main__':
    if len(sys.argv) == 3:
        HOST = sys.argv[1]
        PORT = int(sys.argv[2])
        app.run(host=HOST, port=PORT)

To start the simple Python web application on vm-3, execute the following command in the Terminal for vm-3:

$ python3 Web.py 10.0.2.4 8080

The following would be a typical output:

Output.2

* Serving Flask app "Web" (lazy loading)
* Environment: production
  WARNING: This is a development server. Do not use it in a production deployment.
  Use a production WSGI server instead.
* Debug mode: off
* Running on http://10.0.2.4:8080/ (Press CTRL+C to quit)

To list all the TCP ports listening for a connection on vm-2, execute the following command:

$ netstat -tlnp

The following would be the typical output:

Output.3

(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp6       0      0 ::1:631                 :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -

To setup the Local Port Forwarding SSH Tunnel on vm-2, execute the following command in the Terminal for vm-2 (which will prompt the user for authentication on the remote SSH Server running on 192.168.56.103):

$ ssh -L 8080:10.0.2.4:8080 192.168.56.103

With Local Port Forwarding SSH Tunnel option, any request on a specified local port 8080 on the client 192.168.56.104 is tunneled to the remote SSH Server running on 192.168.56.103, which is then transparently forwarded to the desired target 10.0.2.4.

The general format for the -L option is: [bind-address]:local-port:target-host:target-port, where the [bind-address] is optional. If not specified (like in the above example), it uses localhost as the default.

Now, let us list all the TCP ports listening for a connection on vm-2, execute the following command:

$ netstat -tlnp

The following would be the typical output:

Output.4

(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      3247/ssh            
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp6       0      0 ::1:631                 :::*                    LISTEN      -                   
tcp6       0      0 ::1:8080                :::*                    LISTEN      3247/ssh            
tcp6       0      0 :::22                   :::*                    LISTEN      -

From the Output.4 above, we see that the SSH Tunnel is listening on the port 8080 on the client 192.168.56.104.

Launch a browser on vm-2 and open the URL http://localhost:8080/.

The following diagram illustrates the screenshot of the browser:

Local Port Forward Browser
Browser (Local Port Forward)

Terminate the SSH Tunnel on vm-2 by executing the following command:

$ exit

Refresh the browser on vm-2.

The following diagram illustrates the screenshot of the browser:

Exited Local Port Forward Browser
Browser (Local Port Forward Exited)

PERFECT !!! The setup works as expected.

Remote Port Forwarding

Assume vm-1 is hosting the same web application on 10.0.2.15. It is *ONLY* accessible within the 10.0.2.x network. What if a client on the vm-2 wants to access the web application ???

!! ATTENTION !!

vm-1 is using NAT networking which implies it can access nodes outside its network while *NOT* the other way around

In this situation, one could use Remote Port Forwarding SSH Tunnel option (the reverse of Local Port Forwarding SSH Tunnel) to allow the client vm-2 on 192.168.56.104 access to the web application running on 10.0.2.15. In other words, the SSH Tunnel will be setup from the SSH Server to the client.

Typically, Remote Port Forwarding SSH Tunnel option is used to give access to an internal service to an external user or an external service.

We will reuse the same code listed above for the simple Python based web application in this demonstration.

To start the simple Python web application on vm-1, execute the following command in the Terminal for vm-1:

$ python3 Web.py 10.0.2.15 8080

The following would be a typical output:

Output.5

* Serving Flask app "Web" (lazy loading)
* Environment: production
  WARNING: This is a development server. Do not use it in a production deployment.
  Use a production WSGI server instead.
* Debug mode: off
* Running on http://10.0.2.15:8080/ (Press CTRL+C to quit)

To list all the TCP ports listening for a connection on vm-2, execute the following command:

$ netstat -tlnp

The result should be similar to the one in Output.3 above.

To setup the Remote Port Forwarding SSH Tunnel on vm-1, execute the following command in the Terminal for vm-1 (which will prompt the user for authentication on the remote SSH Server running on 192.168.56.104):

$ ssh -R 8080:10.0.2.15:8080 192.168.56.104

With Remote Port Forwarding SSH Tunnel option, any request on a specified remote port 8080 on the client 192.168.56.104 is tunneled to the SSH Server running on 10.0.2.15, which is then transparently forwarded to the desired target on 10.0.2.15.

The general format for the -R option is: [bind-address]:remote-port:target-host:target-port, where the [bind-address] is optional. If not specified (like in the above example), it uses localhost as the default.

Now, let us list all the TCP ports listening for a connection on vm-2, execute the following command:

$ netstat -tlnp

The result should be similar to the one in Output.4 above.

Launch a browser on vm-2 and open the URL http://localhost:8080/.

The following diagram illustrates the screenshot of the browser:

Remote Port Forward Browser
Browser (Remote Port Forward)

Terminate the SSH Tunnel on vm-1 by executing the following command:

$ exit

Refresh the browser on vm-2.

The following diagram illustrates the screenshot of the browser:

Exited Remote Port Forward Browser
Browser (Remote Port Forward Exited)

AWESOME !!! The setup works as expected.

Dynamic Port Forwarding

Going back to our first demonstration, assume vm-3 is hosting the useful web application on 10.0.2.4. It is *ONLY* accessible within the 10.0.2.x network. In addition, given that vm-3 is also on the 192.168.56.x network (second interface), it can access the web application. Is there a way for the client on vm-2 access the web application (other than Local Port Forwarding SSH Tunnel option) ???

The answer is *YES* - it can using the Dynamic Port Forwarding SSH Tunnel option.

We will reuse the same code listed above for the simple Python based web application in this demonstration.

To start the simple Python web application on vm-3, execute the following command in the Terminal for vm-3:

$ python3 Web.py 10.0.2.4 8080

The result should be similar to the one in Output.2 above.

To setup the Dynamic Port Forwarding SSH Tunnel on vm-2, execute the following command in the Terminal for vm-2 (which will prompt the user for authentication on the remote SSH Server running on 192.168.56.103):

$ ssh -D 8080 192.168.56.103

The Dynamic Port Forwarding SSH Tunnel option automatically sets up a SOCKS proxy on the client vm-2. Once the browser is configured to use the SOCKS proxy, all requests are routed through the proxy to the SSH Server, which then transparently forwards the request to the desired target on 10.0.2.4.

The general format for the -D option is: [bind-address]:local-port, where the [bind-address] is optional. If not specified (like in the above example), it uses localhost as the default.

Now, let us list all the TCP ports listening for a connection on vm-2, execute the following command:

$ netstat -tlnp

The result should be similar to the one in Output.4 above.

Launch a browser on vm-2 and open the URL http://10.0.2.4:8080/.

The following diagram illustrates the screenshot of the browser:

Dynamic Port Forward Browser
Browser (Dynamic Port Forward)

This was expected as we have not yet configured the browser to use the SOCKS proxy.

Click on the three horizontal lines (top right-hand corner) and then on the Preferences option as shown in the illustration below:

Browser Preferences
Browser Preferences

Scroll down and click on the Settings button as shown in the illustration below:

Browser Network Settings
Browser Network Settings

Select the Manual proxy configuration option, enter the details for the SOCKS Host and Port, and finally click the OK button as shown in the illustration below:

Browser SOCKS Settings
Browser SOCKS Settings

Open the URL http://10.0.2.4:8080/ in a new tab of the browser.

The following diagram illustrates the screenshot of the browser:

Dynamic Port Forward Browser
Browser (Dynamic Port Forward)

WALLA !!! The setup works as expected.

References

SSH/OpenSSH/PortForwarding



© PolarSPARC