Blog

Reverse SSH Tunnelling

I have two servers. One is a rented VPS that I use to host web stuff, and the other is a server here at home that mostly holds files and downloads torrents, but also acts as a web server for development stuff. Both of them run debianĀ with lighttpd taking care of serving web content on the VPS and apache doing the same job on the home server.

Since it houses all my documents, I used to use AjaXplorer to let me access them from anywhere, with a dynamic DNS that points to my home IP and a CNAME record on my domain that hides my dyndns hostname for the sake of making things look pretty.

Before I moved into my current apartment and shaw was providing my internet this all worked great, but when we moved to where we are now 9 months ago we switched internet providers to telus because our building provides free internet and TV with them (side note: if telus isnā€™t free for you theyā€™re not worth it).

With internet from telus, everything stopped working.

A little bit of googling quickly revealed that telus block port 80 on residential internet connections to prevent their customers from running a web server. I opened up port 8080 on the firewall and ran the web server on that port instead, but for reasons I never took the time to understand itā€™s always been terrible. 90% of the time the setup doesnā€™t work, and even if it does I hate having to specify a non-standard port in the URL anyway.

My plan for a while now has been to come up with a better solution that involves using the VPS as a proxy to route traffic to the home server through some kind of tunnel. Initially my plan was to look for something similar to hamachi to take care of the tunnelling bit so that the home server could be reached without having to worry about the firewall or NAT on our home router, but after doing some reading this week it turns out thereā€™s a much simpler and easier solution! SSH can set up a reverse tunnel out of the box – meaning the home server can initiate a connection to the VPS that results in a port on the VPS being forwarded to a port on the home server. This is important because the home server isnā€™t easily accessible to the internet at large (thanks to NAT, dynamic IP addressing, and telus blocking things) but the VPS is. The VPS wouldnā€™t be able to easily initiate a connection to the home server, but the home server will always be able to initiate a connection to the VPS! Hereā€™s what I did:

  1. Set up passwordless SSH from the home server to the VPS
    For me this was already done, but itā€™s a crucial step. If the home server requires a password to connect to the VPS then itā€™s going to be impossible to set things up to work automatically. I followed a Password-less logins with OpenSSH guide that I found through a simple google search.
  2. Temporarily open the reverse tunnel by using the following command on the home server:ssh -R 8080:localhost:80 vps1.example.com

    You can search online if you want to better understand the syntax, but essentially what Iā€™m doing here is forwarding port 8080 (an arbitrary choice) on vps1.example.com (the VPS) to port 80 on localhost (the home server). Iā€™m not specifying a username because I have identical accounts set up on both servers, but if your setup requires you to specify change vps1.example.com to [email protected].

  3. Point the (sub)domain you want to use (for me itā€™s files.example.com) to the VPS, and set up the web-server on the VPS as a proxy to forward the connection to port 8080.For lighttpd I followed the relevant documentationĀ (I also had to enable the mod_proxy module) and my configuration line looks like this:

    $HTTP[ā€œhostā€] = ā€œfiles.example.comā€ { proxy.server = (ā€œā€ => ( ( ā€œhostā€ => ā€œ127.0.0.1ā€, ā€œportā€ => 8080 ) ) ) }

    Youā€™ll also need to make sure that the web-server on the home server is prepared to accept traffic for files.example.com

At this point I gave everything a quick test and it all worked great. Yay! However closing my ssh terminal connection to the home server also closes the tunnel. There are a few ways to get around that, but thatā€™s not our only problem here. The ssh tunnel may get disconnected for any number of reasons (if either server loses internet connectivity, is rebooted, general internet weirdness, the list goes on). Thereā€™s a tool available that solves all these potential pitfalls though, and itā€™s called autossh.

Autossh monitors an ssh connection and restarts it if it goes down for any reason. Itā€™s available in the debian repositories, so I installed it on the home server.

  1. To setup the same tunnel we did in step two using autossh, the command looks like this:autossh -M 29001 -f -N -R 8080:localhost:80 vps1.example.com

    Again I wonā€™t detail out all the command options, but theyā€™re to peruse on the autossh website or in the man page. The result running this command is that our tunnel is set up in a persistent way – if the connection is broken for some reason autossh recreates it. We can also log out of our terminal session on the home server without the tunnel being disconnected.

  2. The final step I took was to automate creation of the tunnel. I could have written in init.d script but a better solution is to put a script in /etc/network/if-up.d. This way whenever the network interface comes up on the home server the tunnel is created. The only additional consideration here is the user. Passwordless ssh is set up under my user account not as root, so I want the script to open the connection with my user account too. Hereā€™s my very simple script:#!/bin/sh
    su -c ā€œautossh -M 29001 -f -N -R 8080:localhost:80 vps1.example.comā€ Jason

Done! Now I have a persistent ssh tunnel forwarding port 8080 on the VPS server to port 80 on the home server, and lighttpd acting as a proxy on the VPS to forward traffic for the relevant subdomain to that port, and visiting files.example.com in a browser works again, despite telusā€™ best efforts to stop me.