Writing mostly about computers and math.


I have been asked by a few different people I work with how to access our Subversion repository through a firewall, so I thought I'd record my solution here in case I'm ever asked again.

My university has a firewall that blocks all incoming connections. There's a VPN system we can use that requires some terrible software that's a pain to set up and there is one server that we have SSH access to from outside the university. I'm sure the IT department really likes its firewall, but it's really inconvenient for those of us who have our own computer systems that are now inaccessible from the internet. My research group has a subversion repository that's located on a computer of ours behind this firewall. It's very handy if I'm on-campus, but if I'm at home or in a hotel or something, I can't get to my work if I forgot to bring it with me or if I want to check in a quick change. I've changed some of the hostnames, but this is basically what the network topology looks like:

The terrible firewall prevents me from accessing my server located behind it from outside the network.

There are a few possible solutions to this problem, but the one that seems most useful to me is to tunnel my SVN connections through the computer that I have SSH access to (spike) to get to the computer with the repository (jet).

So, setting up an SSH tunnel is pretty simple. You have to pick a port to use at the local end of the tunnel and you need to know the correct exit port for the protocol you're tunnelling. In this case, since our subversion server only accepts svn+ssh connections, I'll need to tunnel an SSH connection (which uses port 22) over SSH. Yes, an SSH connection inside of an SSH connection. It's turtles all the way down.

To create my tunnel to port 22, I'll use port 9500 locally. Any port under 1000 requires root access to use, and I know that 9500 is free on my machine. You can pick whatever you want; it doesn't really matter. I'm tunnelling through the server that's accessible from outside the network, so this is the address that I tell SSH to connect to. The command looks like this:

ssh -fN -L 9500:jet.dhcp.msu.edu:22 beardpet@spike.msu.edu

What this means is that all traffic on my computer directed to port 9500 will come out through the SSH tunnel to spike (the computer I have access to) on port 22 at jet, the computer that I can't access from outside the network. The -f tells SSH to immediately go to the background and the -N tells it not to execute any commands. The tunnel looks like this:

The SSH connection goes from port 9500 on my computer, through spike, to port 22 on jet.

That's very lovely, but the slightly more difficult part is getting the SVN connection to send all of its data through my SSH tunnel. To do this, we have to get SVN to connect to port 9500 on my local machine, which will then forward the traffic through the tunnel to port 22 on jet, the SVN server. This is accomplished by adding two lines to ~/.subversion/config

msu ssh -p 9500

This creates a new custom tunnel for subversion to use. Instead of svn+ssh://[...], now I can use svn+msu to get to my formerly inaccessible repository on jet. The only catch is that rather than using svn+msu://jet/[...], I have to connect to localhost since that's where my end of the tunnel is. My SVN checkout command, for example, now looks like this:

svn co svn+msu://beardpet@localhost/path/to/repository

Note the localhost where the server name would normally be. This means that SVN will attempt to connect to a repository on localhost using the "msu" tunnel, which is an SSH connection on port 9500. This means that the SSH connection will go through my tunnel and come out on port 22 on jet, which is exactly where a normal svn+ssh connection would end up. It looks a bit like this:

Subversion connection tunneled through SSH.

Compare this to a typical SSH connection from behind the firewall:

A typical SVN connection coming from inside the LAN.

You can see that from jet's perspective, nothing has changed; it's receiving a request to establish an svn+ssh connection on port 22, just like it expects. So, the steps to set this up are:

  1. Create an SSH tunnel from your computer to your SVN repository.
  2. Configure SVN to use your tunnel's local port.
  3. Connect to localhost using the SSH tunnel's port to send the connection through the tunnel.

The primary downside to this method is that you have to use the tunnel to interact with working copies set up this way, even if you're on the LAN. This is because they've been configured to look for a server at localhost:9500, where there is no server, only the tunnel endpoint if it's running. Otherwise, this method is great for bypassing firewalls, not just for SVN of course, but for any protocol you can tunnel over SSH.