01 Nov 2009 SSH Tunnels – part 2
In a previous post I described how and why one might set up a very simple ssh tunnel in order to access a web server or a remote desktop server on a machine with a restrictive firewall. This post goes one step deeper, showing how to chain tunnels together.
With this added complexity, we have hit the point where there are many, many ways to skin this particular cat. My aim in these posts is to show how I do things, demonstrating the underlying tools along the way. Hopefully, this will let you build up your own set of techniques in a way that makes sense to you.
A very common practice among the network security folks I meet is to ask “where will you be logging in from?” They then make an exception in their firewall rules for traffic from my particular machine. This makes a lot of sense, but it limits my flexiblity. If I give them the address of my home internet connection, then I won’t be able to connect while I’m on the road, and so on. In addition, if I need to let my colleagues see what I’m dealing with in order to get their help debugging something, we need a way to share that hole in the firewall.
My solution is to give an IP address of a server in Bioteam’s co-location facility, and then use ssh tunnels to bounce my connections through that server and into the customer’s network. This has the added social benefit of giving the customer a “bioteam.net” IP address rather than my home. I think that this looks a bit more professional.
My server is called “demo.bioteam.net”
I log into my server with the username “cdwan”
The customer’s server is “remote.customer.com”
I log into the customer’s server with the username “bioteam”
As before, the first thing to check is that straightforward, vanilla ssh connections work:
ssh demo.bioteam.net -l cdwan
I will be prompted for a password. This first time, I enter the password for “cdwan” on the bioteam machine. Next, within that ssh session, I connect to the customer’s machine:
ssh remote.customer.com -l bioteam
Again, the password prompt – but this time I have to enter the password for “bioteam” on the customer’s machine.
It feels a little pedantic to specify which password goes where, but I find that the second most common source of confusion about ssh tunnels is figuring out which machine is demanding a password. As you start to build up more and more complex systems, it’s key to remember where you are and where you are trying to go.
Assume that we want to take a look at the web server on the remote machine. If the security folks made a complete (all ports) exception for my host (demo.bioteam.net), then this is pretty simple. From my laptop, I do this:
ssh demo.bioteam.net -l cdwan -L 8999:remote.customer.com:80
This is exactly the same as the first example in the first post in this series, except that instead of connecting to “localhost,” I’m telling ssh to point at some external machine.
I will make a practice of changing the port number in these examples (8999 vs. 9000 in this case) to demonstrate that it doesn’t matter what number you use as long as it is not in use for anything else. If you try to open a tunnel on a port that is already in use by another process (someone else’s ssh tunnel to a different machine, for example) the command will fail.
There is a pretty safe range of ports from about 8900 to about 9100 that I tend to use for these purposes. I make a practice of picking numbers pretty much at random. You should do the same, rather than just hitting “up arrow, return” on some command cut and pasted from a blog post.
In actual practice, Bioteam sets up virtual machines – one per customer – to prevent us from stepping on each other’s connections and ssh keys. This has other benefits as well, the best being that we can give customers an account on our systems to share files – which is often simpler to do than convincing their networking team to let us into their machines.
Anyway, with the above tunnel in place, I can look at the URL below and see the remote machine:
It’s worth noting that I could do almost the same thing by opening a remote desktop (or VNC) connection to demo.bioteam.net and running a web browser in that remote session. I find this to be inferior because remote desktops invariably have less pixels and worse mouse control than my local browser.
I would use exactly the same sort of command if the customer wanted me to use their “DMZ” gateway machine to access their network, rather than my own. This happens in lots of cases. The key here is that once I have ssh access to a machine – I can point whatever application I want at anything that machine can see.
The setup above works as long as there is a clear path from demo.bioteam.net to remote.customer.com. As an additional complexity, assume that we now encounter the firewall problem from my first post. The security folks only opened port 22 (ssh) and only from demo.bioteam.net.
To overcome this, we need to chain ssh tunnels together. Here’s the example:
ssh demo.bioteam.net -l cdwan -L 8999:localhost:9000
From within that session, connect to the remote machine like this:
ssh remote.customer.com -l bioteam -L 9000:localhost:80
And then look at the same URL:
Here’s what happens: I have one tunnel leading from my laptop’s port 8999 to port 9000 on demo.bioteam.net. I have a second tunnel from port 9000 into remote.customer.com. Under the hood, ssh near-magically forwards packets along and does the right thing to tie them together.
There is absolutely no need for those tunnels to be opened in the same ssh session or from the same terminal. In fact, in the next post I will show how to open one tunnel from each side to meet in the middle.
It is possible to chain tunnels together arbitrarily. The longest series that I’ve ever built was three deep, but I’m not aware of any fundamental limitation on that – other than my own ability to keep track of accounts and passwords.