Well then, here we are, ladies and gents. Here’s the dragon I’ve been trying to slay (read: understand) for years. Now, to get my thoughts straight, I’ll give you a very high-level overview of what it is and what the use cases are.

NOTE: I will try to avoid coital metaphors as much as possible, maybe in another tutorial ;-)

Firstly: Most tutorials I’ve seen use localhost as a vital part of their tutorials. This causes, at least for me, a shitload of confusion. This is why in this tutorial, I will ALWAYS use express IP addresses for anything, even when you’re literally typing

m4iler@192.168.0.100$ ssh 192.168.0.100

So, without further ado, here we go!

What it looks like

First of all, let us agree on what a typical reverse SSH command should look like.

m4iler@192.168.0.100$ ssh -R 1337:192.168.0.100:22 h3imdall@192.168.0.200

This is quite a load of options, but let’s take this apart:

  • ssh is the command (duh)
  • -R tells us we’ll be opening a reverse tunnel
  • 1337 is the port we’ll open on the remote server to listen to incoming connections
  • 192.168.0.100 is the machine we want the tunnel to point to.
  • 22 is the service that will be used on this machine. If you wanted HTTP access, you’d use port 80 on here
  • h3imdall@192.168.0.200 is where we are opening the entrance of the tunnel.

This gets us halfway there. We have opened the connection from an accessible server to the client. Now, how do we use this tunnel? Simple! We just SSH to the server, but instead of providing the regular port 22 (which, in this case, would get us to 192.168.0.200), but we provide the port 1337. So, to go through this tunnel, we use the following:

ssh m4iler@192.168.0.200 -p1337

NOTE: Careful about the username, see below for explanation

Alternatively, you can ssh into your server normally and do a J-turn into your reverse port:

someone_else@somewhere_else$ ssh h3imdall@192.168.0.200
[insert MOTD here, connected to 192.168.0.200]
h3imdall@192.168.0.200$ ssh m4iler@192.168.0.200 -p1337

Before you start tunnelling

There are several things, most of which are not quite intuitive. This is why the topic has been a goddamn pain in my ass for the past several years. I’m sure there are many catches I have missed, but this is only a primer.

Firstly, check /etc/ssh/sshd_config to see if AllowTcpForwarding and GatewayPorts are set to yes in the server (in our tutorial, 192.168.0.200) before you even start. If not, change them.

Many tutorials assume you have the same username on all your devices. This may be a bad thing in cases where you access your server as one user, but are a different user on the end machine. Can you see the issue in the example? Here it is again:

m4iler@192.168.0.100$ ssh -R 1337:192.168.0.100:22 h3imdall@192.168.0.200

On the .100 host, my name is m4iler, but the username I log into when I ssh into the .200 host is h3imdall (this will often happen if you’re lazy to change the name pi on your Raspberry you want to use as a server, in which case change h3imdall for pi). If you connect to your server as h3imdall and then try to run the following command:

ssh localhost -p1337

your server will know where you’re going, but not who you’re logging in as. This will result in a login prompt (as it should), but with the wrong username!

h3imdall@192.168.0.200:~# ssh 192.168.0.200 -p 1337
h3imdall@192.168.0.200's password: 

This command might look quite reasonable, we’re logging as h3imdall into the .200 machine. But wait! Port 1337 points to the .100 machine, where I’m known as m4iler! This will cause our login to fail even if you got everything else right.

What the fuck is this good for?

You may be wondering why someone would even bother building reverse tunnels when opening ports is not that difficult. Why, then, would someone want to open reverse tunnels?

Imagine the following scenario: You have been asked by your boss to set up a nice Linux server to give everyone that new file-sharing you’ve been talking about for months. That’s great! However, no one in the company knows the router password, only the ISP knows and they’re not that friendly when it comes to relinquishing power. Sure, you might call them up and ask them to open port 22 so that you could SSH into the company from the outside, but the project is due tomorrow, the ISP admin is on holiday (you may not believe it, but that stuff happens). Put simply, you cannot take the device home, need to get into it, but cannot do it from the outside.

After you taste the goodness of SSH reverse tunnels, you may do what I’ve been wanting to do for years: Make your own head-hub, some accessible server and from each of your devices on your home network, your workspace, you make a reverse tunnel connection to it. This makes what I would call a “hall-server”. Loads of little doors (ports) you can go through to different devices under your command. You can become a necromancer with your own skeleton army and finally take over the wor… sorry, you can conveniently manage all your SSH-capable devices! :-)

Local port tunneling

There is another option, for when your firewall doesn’t allow a certain port to go out.

It looks basically the same, just with two minor differences (easy to miss, easy to confuse)

The -L option is used when the server would be normally accessible, were it not for those pesky firewalls. If you have a cunt of an admin who blocks outgoing port 2033 (Civ IV multiplayer), but port 1337 works. Let’s see how to get this going:

m4iler@192.168.0.100 ssh -L 1337:192.168.0.2:2033 h3imdall@192.168.0.200

This commands starts a tunnel with the entry point being your machine (192.168.0.100) and the end being the 192.168.0.2 host (assigned between the port numbers). This number may be another computer. The last argument (h3imdall@192.168.0.200) tells us the end-point, from where we don’t need to hide our traffic anymore. Yes, that’s what we’re doing.

If we talk in normal people terms, it goes like this:

First, we open port 1337 on our local machine. The localhost (.100) listens on port 1337, and anything it receives, gets sent as SSH traffic over a non-standard port to the exit point (signified by the last argument, h3imdall@192…200). From there, it gets unmasked and goes on to its target as regular Civ IV traffic, openly. To the target, it seems that the .200 host is playing Civ IV!

Yes, the SSH tunnel, in this case, is running via 1337. This doesn’t matter to our end point.

In closing

If I could, I would’ve provided images to everything, but I got the next best thing: Tinkernut over on YouTube explains these topics rather nicely. If you’re in a meeting right now and can only read, here you are, but if you can watch videos, I’d highly recommend Tinkernut’s video on SSH tunneling

Right now, I have NO idea how many people this will help, but there are not a lot of easy-to-read articles on this topic, I wanted to put out something simple, basic, yet at least a little usable, documenting the obstacles I encountered on the way.