Cover V14, i10

Article

oct2005.tar

Chain Gang: Using SSH Chains to Traverse Firewalls

Ralf Hack

Imagine that you're working at home and you need access through the corporate firewall, but your document server, intranet Web server, and the development systems are protected by a managed shared firewall. Additionally, routing restrictions require multiple hops to gain access to your LAN. Even with a comprehensive VPN solution, you may experience difficulties accessing your normal work environment. Network restriction and network segmentation designed to protect your environment may hinder usability. Other than disregarding change control processes or maintenance windows, there is often no fast and sure way to open a port on short notice.

Fortunately, it is possibly to use SSH port forwarding as generic TCP proxy for individual services. This article demonstrates how port forwarding using the popular OpenSSH can help. By chaining together multiple sessions, you can, under certain circumstances, traverse multiple firewalls and deal with routing restrictions.

Before implementing this method, please ensure that you are in compliance with all relevant security policies of the provider of the corporate firewall. The connection method itself is based on trusted tools such as OpenSSH and OpenSSL. However, making a protected service available outside the secure environment has obvious security implications. This method is only as secure as the systems you are using. Good housekeeping, patching, and careful monitoring for any security threads on all systems involved are essential. This particularly includes the originating host and the final system to which you plan to connect.

Note that this method also can bypass well-intentioned but badly implemented security restrictions. It is based on TCP port forwarding and can be implemented in a few lines of Perl code. One of the manual pages for OpenSSH points out that "... disabling TCP forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders" [1]. Because TCP forwarders can be created with a variety of tools, only host security restrictions can really prevent their usage [2].

A Quick Introduction to SSH and Port Forwarding

SSH is a widely known and readily available tool for secure shell access. Most current Unix-like operating systems already have the binaries preinstalled. I recommend using the distributed option for your system [3]. Typically, the package maintainer puts extra effort into making the tool secure and compatible for you. To compile the source code from scratch, installation instructions for a secure standard installation are available from:

http://www.openssh.org
OpenSSH is a typical "configure, make install" installation for Unix and requires a working installation of OpenSSL and zlib. Unfortunately, the details for a secure server installation exceed the scope of this article. We assume password authentication where applicable in the following.

Typically, you would run the SSH client on the site that originates the connection, such as your laptop. The server is installed on the remote site listening on a public port. Each connection spawns a dedicated SSH server instance running with your user privileges. Once the connection is established, and both client and server are authenticated, the principle of operation is straightforward.

The client collects all data from the standard input and any forwarded ports you choose. This data gets bundled, encrypted, and sent as a single datastream to your SSH server instance. The server instance in turn decrypts the datastream into the individual channels and either submits the data as a command to your server-side shell or forwards the data to subsequent connections.

By default, both client and server attach their listening ports to the loopback device on address 127.0.0.1. This effectively permits only local processes access to the listening port and should not be opened arbitrarily. Also, if you do not require a server-side shell process, then you can prevent its creation by appending -N (do not execute a remote command) and -f (go into background after authentication) when starting the client.

Simple Port Forwarding in Practice

The following example establishes a connection to the server called "thefirewall" and starts a server-side shell. The command will also open a TCP listening socket on the loopback device of your laptop and send all requests to the host "myremotewww":

laptop$ ssh -L 3000:myremotewww:80 thefirewall
The client now collects the standard input from the local shell, the TCP handshake, and any traffic on port 3000. The encrypted data is sent to the SSH server instance on "thefirewall". Your SSH server instance will forward any shell input to the server-side shell. At the same time, any TCP handshake and data for port 3000 will cause "thefirewall" to open a corresponding connection to "myremotewww" port 80 and forward the subsequent data on this port.

Note that if "myremotewww" is a virtual host on a Web server running HTTP 1.1, then you must add an alias to your "/etc/hosts" file or the equivalent for your operating system:

127.0.0.1        myremotewww
By directing your local browser to "http://myremotewww:3000/", you access the desired virtual host "myremotewww" via the loopback device 127.0.0.1. Do not forget to remove this entry afterwards, or you will not be able to connect to "remotewww" without SSH tunnel. To connect to the HTTP 1.0 host, browse to "http://localhost:3000".

Simple Reverse Port Forwarding

Less frequently, you may be required to do the reverse: make your local resources available for services behind the firewall, such as your local Wiki, NFS [4], Samba, or MySQL database. To accommodate this, SSH provides a reverse tunnel flag -R that establishes a remote listening socket that connects to your local site:

laptop$ ssh -R 3000:localhost:80 thefirewall
As before, the client will send the standard input to the shell on host "thefirewall". This time, not the SSH client, but your SSH server instance on "thefirewall" will listen to port 3000 and forward any handshake to the SSH client session on "laptop". The client on "laptop" will establish the connection to port 80. By running lynx on "thefirewall", for example, you can connect to your local laptop even if the laptop's firewall blocks port 80:

thefirewall$ lynx http://localhost:3000
Multi-Hop SSH Sessions

As mentioned above, SSH is a generic TCP proxy. Hence, it is possible to forward SSH itself and/or to chain two tunnels together. This becomes quite helpful when you are faced with two firewalls: your home firewall and the corporate firewall. The security policy on the corporate firewall and the intranet routing may by default block direct access to your host. Using two or more SSH tunnels allows you to create an indirect tunnel.

As a good test of whether multiple forwarding is possible, you can try to SSH to a possible "neighbor" host and telnet to the port to which you require access. Because telnet is a TCP service, you should at least be able to establish a TCP session. If you're successful, then it is very likely that you can use the same SSH hosts as hops to create a tunnel.

Multiple Hops in Practice

Because it is easier to test individual legs starting from the last leg, we will build the tunnel in reverse. The following example assumes that your home firewall is a hardened Linux host with a broadband connection. To begin, establish the second leg of the tunnel from your firewall host to the remote intranet server "myremotewww":

myhomefirewall$ ssh -L 3001:myremotewww:80 mycorporatefirewall
This creates a tunnel from the loopback device on the "myhomefirewall" port 3001 to the Web server "myremotewww" port 80 behind "mycorporatefirewall". You may use -v (the verbosity flag) for debugging purposes. Try to "telnet localhost 3001" on "myhomefirewall" to verify that this tunnel works.

Next, connect your first leg to the listening port of the second leg tunnel. Do so by creating a tunnel between your laptop and the "myhomefirewall" with:

laptop$ ssh -L 80:localhost:3001 myhomefirewall
To test the connection, browse to "http://localhost" on your laptop. Your local SSH client tunnels this connection to the server on "myhomefirwall" over the encrypted connection. The respective SSH server instance then establishes a connection to port 3001 on "localhost". This is where the SSH client of the second leg is listening. The second SSH client tunnels the request to the SSH server instance on "mycorporatefirewall". Finally, the latter establishes a connection to "myremotewww" to port 80.

The result is equivalent to running:

ssh -L 80:myremotewww:80 mycorporatefirewall
using two SSH servers. However, there are differences. Unlike the direct method, the traffic is not completely encrypted all the way. The gap is on "myhomefirewall", where the SSH server decrypts the traffic and then forwards it to the second SSH client listening on port 3001 in the clear. Fortunately, on the exposed parts between your "laptop" and "myhomefirewall", as well as "myhomefirewall" and "mycorporatefirewall", it is fully encrypted. The advantage is that you can use different users and encryption methods on each of the legs. However, this setup also increases latency due to the inherent context switching and additional computational requirements.

If you require full path encryption, then you may be advised to tunnel an end-to-end encryption program, such as stunnel:

http://www.stunnel.org
Another option is port forwarding of the last SSH server in the chain. However, this will still leave the connection between the last SSH server and the target host in the clear.

Reverse and Forward Hops Combined

The corporate firewall may not allow connections to be forwarded at all. In this case, you can attempt to create a reverse tunnel from within the corporate network to your own firewall "myhomefirewall". This tunnel will be initiated from a suitable "jumphost" within the corporate environment. It will listen on a port on your "myhomefirewall". You establish the second leg using the following command:

jumphost$ ssh -R 3001:myremotewww:80 myhomefirewall
As in the previous example, you can now establish the first leg of the tunnel from your laptop by:

laptop$ ssh -L 80:localhost:3001 myhomefirewall
Test this tunnel by browsing to "http://localhost" again. The SSH client on your laptop will establish a connection to port 3001 on "myhomefirewall". Now the second instance of the ssh server is listening here. This second instance is created by the session from "jumphost". The connection request on port 3001 is then forwarded to the client on "jumphost". The latter will forward this connection attempt to the host "myremotewww" port 80.

As before, this is an alternative way to ssh -L 80:myremotewww:80 mycorporatefirewall. The reverse tunnel has the same advantages and drawbacks as the forward tunnel. The encryption gap is between the sshd server instance of the first leg and the second leg. Also, your home firewall must have a public SSH port to which you can connect. Hence, a virtually static IP address of your home firewall is required. When this address changes, you will lose the tunnel.

Limitations

Theoretically, there is no limit to the number of tunnels that can be chained together. However, every hop does increase latency. As mentioned previously, the encrypted traffic is a bundle of all traffic. Decryption must take place between tunnel legs before the data can be rerouted for the next leg.

Also, this method is only applicable to TCP network traffic. One way to tunnel UDP or other protocols is to establish an end-to-end PPP connection to create a genuine VPN. This is more complex and requires privileged access on both sides.

Conclusion

This brief introduction in multiple port forwarding has shown how you can establish a multi-hop TCP proxy service using SSH. It also detailed the methods available to overcome routing and firewall restrictions as permitted by host security. This can be achieved by connecting multi-port forwarding sessions to create a tunnel with multiple hops.

Resources

OpenSSH -- http://www.openssh.org

OpenSSL -- http://www.openssl.org

stunnel -- http://www.stunnel.org

zlib -- http://www.gzip.org/zlib

1. man sshd_config (version OpenSSH_3.4p1 Debian 1:3.4p1-1.woody.3)

2. Punching holes into firewalls -- http://sebsauvage.net/punching/

3. Installation -- http://comm.ncifcrf.gov/security/ssh_obtain.html

4. Strandboge, James. 2002. Encrypted NFS with OpenSSH and Linux, Sys Admin 11(3)30-34.

Ralf Hack, Dipl.-Ing. (FH), has spent nearly a decade in the United Kingdom working for ISPs in software development. His interest are Unix-type systems, anything from applications via operating systems to hardware and training. He lives in Luton, Bedfordshire, with his wife, Penny, the children, grandchildren, and (other) animals. He can be reached at: ralf@beetlecraft.net.