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. |