SSH tunnels are some of the most powerful utilities in the entire command line arsenal. They allow me to connect to arbitrary ports on remote machines. I use them daily to navigate the somewhat arbitrary networking requirements that I encounter from both software tools and network administrators. I have found that it is invariably easier to figure out how to construct a tunnel than it is to convince network security folks to open more ports in their firewalls. Even though I can be flippant about this, all of these techniques remain true to the spirit, if occasionally subverting the letter, of the network access requirements imposed by my customers.
This post provides a very basic intro to forward tunnels. I hope to shed some light on reverse tunnels in a future post.
Firewalls and other network devices frequently limit the network ports that I am allowed to access. A very reasonable policy might only allow access over ssh (port 22) from machines outside of a local network. That’s well and good, but in my work I often need to see web servers (port 80) and/or Apple’s Remote Desktop or VNC (port 5900).
As a first puzzle, assume that a friendly administrator gave me an ssh account on her web server. However, because I’m not working onsite, her organization’s security scheme only allows me to get to port 22. I can ssh in, but I can’t really debug some problem with the web browser because port 80 is still blocked. Assume as well that we’ve already called up the network administrator, who said “no,” there are no exceptions to this security policy.
The remote machine is called: “remote.customer.com”
I will log into that remote machine with an account called “cdwan”
I run this perfectly ordinary ssh command to log in, and it works:
ssh remote.customer.com -l cdwan
Keep in mind that if that first command doesn’t work – there’s nothing I can do to make the rest work. With that in hand, I am going to add the forward tunnel specification:
ssh remote.customer.com -l cdwan -L 9000:localhost:80
The tunnel specification is in three parts:
* First is the port on my local machine into which I peer
* The middle is the target hostname I want to connect to as it is seen on the remote machine. In this simple example, I’m going to “localhost”, which really means “remote.customer.com”. I think that this is the most frequent source of confusion with tunnels, figuring out the context in which hostnames are evaluated.
* Last is the port to connect to on that target machine. That’s where the tunnel ends.
So, I’m setting up a connection from my local machine’s port 9000 to port 80 on remote.customer.com.
Once that tunnel is in place, I should be able to open a web browser and look at the URL below to see the web server on the remove machine:
http://localhost:9000
From the point of view of the web browser, I’m looking at port 9000 on my local machine. My ssh connection is catching those requests on port 9000, forwarding them along to the remote machine, and passing them to port 80. Responses come back by the same route.
The connection will only stay open as long as the ssh connection is active. As soon as I log out, my tunnel goes away. While I’m connected, all of my traffic goes over port 22, and is encrypted in exactly the same way as the rest of my ssh traffic. It’s actually a remarkably stable and secure solution to this problem.
I’ll close this post with the fact that ssh accepts multiple instances of the “-L” flag. So, if I want to connect to both a web server (port 80) and a remote desktop server (port 5900), I can do this:
ssh remote.customer.com -l cdwan -L 9000:localhost:80 -L 9001:localhost:5900
With that in place, I can point my web browser at localhost:9000, and my remote desktop client at localhost:9001.