Fun with ssh tunnels

A couple months back, UMBC decided to block off-campus access to most of its internal hosts. Included in this bunch was concerto, which houses this blog as well as my house wiki. Although I could probably apply for and get a hole punched in the firewall for http and ssh, I decided not to bother. I’m not the most proactive guy in the world when it comes to keeping up with security patches, so the firewall thing is probably for the best. Of course, the down side to this is that I can’t use concerto as a free web hosting environment any more, which again, is probably mostly a blessing in disguise. However, there was one big thing I didn’t want to give up: ssh and web access to concerto from our home LAN. After all, there’s not much point in having a house wiki if I can’t get to it from my house! So the challenge became, how do I get this back, and make it as seamless as possible?

I started out with a couple of basic SSH tunnels. To use SSH tunnels, I needed SSH access to a host at UMBC that could “see” concerto. Fortunately, OIT’s public-access GL systems fit the bill. So, my first stab was to use the GL systems to tunnel SSH and HTTP traffic to concerto:

ssh linux.gl.umbc.edu -gfN -L 2222:concerto.ucs.umbc.edu:22 \
-L 8080:concerto.ucs.umbc.edu:80

The -g switch tells the tunnel to listen on all available interfaces, not just loopback, so the tunnel will be available to every host on my LAN. -f tells ssh to go into the background after prompting for password, and -N tells ssh not to start a shell on the remote host. I don’t need a remote shell, just the tunnels.

With this setup, I could ssh into concerto by doing ssh -p 2222 wacker.riddles.lan, and I could get to the web server at http://wacker.riddles.lan:8080. Good start, but still not quite what I wanted. Forwarding HTTP this way worked to a point, but anything using absolute paths would still not work. For example, if a page on concerto references an inline image with an absolute URL (<img src="http://concerto.ucs.umbc.edu/blah.jpg"/>), the image load would time out because the browser can’t do a direct connect to concerto.ucs.umbc.edu. In practice, I found that while MediaWiki ran just fine with this forwarding setup, b2evolution (my blogging engine) was all broken because it’s full of absolute URL references.

What I really needed was a way to make absolute references to concerto.ucs.umbc.edu work seamlessly, with connections to ports 22 and 80 automatically forwarded via the SSH tunnels. And with a little bit of networking and DNS trickery, I was able to get it working. Here’s what I did:

  1. Add an entry to my local DNS server that maps concerto.ucs.umbc.edu to an IP address on my local LAN. I did this by setting up a local DNS “zone” for concerto:
    Excerpt from named.conf:

    zone "concerto.ucs.umbc.edu" in {
    type "master";
       file "/etc/bind/db.concerto";
    }

    Contents of db.concerto:

    $TTL 3600
    @     IN      SOA     ucs.umbc.edu.   postmaster.riddles.lan. (
                                          2
                                          86400
                                          43200
                                          604800
                                          86400 )
          IN      NS      snorkelwacker.riddles.lan.
          IN      A       192.168.0.50

    Now, all DNS queries for concerto originating from the LAN will map to the local address 192.168.0.50 (aside: to flush the DNS cache under Windows XP, enter the command ipconfig /flushdns).

  2. Add a virtual network interface on my Linux server with the address 192.168.0.50:
    ifconfig eth0:0 192.168.0.50 netmask 255.255.255.0

    On a Debian system, this can be configured permanently in the file /etc/network/interfaces.

  3. Configure sshd to not listen on the new virtual interface. By default, sshd listens on all available interfaces, and we need to free up port 22 on the new virtual interface so we can tunnel it to the “real” concerto.

    File: /etc/ssh/sshd_config

    ListenAddress 192.168.0.2
    ListenAddress 127.0.0.1
  4. And finally, set up the new SSH tunnels to forward ports 80 and 22 appropriately. Because these are privileged ports, this needs to be done as root.
    ssh paulr@linux.gl.umbc.edu -gfN \
    -L concerto.ucs.umbc.edu:80:concerto.ucs.umbc.edu:80 \
    -L concerto.ucs.umbc.edu:22:concerto.ucs.umbc.edu:22

    A little more explanation is in order here.. In the tunnel specifications (-L switch), the first ‘concerto’ refers to the bind address on the local host, so we’re telling it here that the tunnels should only be bound to our new virtual interface. The second ‘concerto’ refers to the “real” host. This lookup is done on the remote host, linux.gl.umbc.edu, so it will resolve to concerto’s “real” IP address.

And that’s it.. with this setup, I can access concerto from home the same way I always did. I may still look for a cheap hosting provider so I can publish this blog to the rest of the world, but for now, this fills the bill.