This week we thought we’d share some background on
localtunnel, a project I wrote outside of
Twilio to help deal with the challenges of developing against webhook-based
APIs (such as Twilio’s) when coding behind a NAT.

These days it’s fairly common to run a local environment for web development.
Whether you’re running Apache, Mongrel, or the App Engine SDK, we’re all
starting to see the benefits of having a production-like environment right
there on your laptop so you can iteratively code and debug your app without
deploying live, or even needing the Internet.

The Problem

With the growing popularity of HTTP callbacks, or webhooks, there are cases
where you can really only debug your app while live and on the Internet.
Webhooks aside, there are other cases where you might need to make local web
servers public, such as testing or public demos. Demos are a surprisingly
common case, especially for multi-user systems (“Man, I wish I could have you
join this chat room app I’m working on, but it’s only running on my laptop”).

The Solution?

To some, the solution is obvious: SSH tunneling! That is, use a magical set of
options with SSH on a hosted box to set up a tunnel from that machine to your
local machine. When people connect to a port on your public machine, it gets
forwarded to a local port on your machine, looking as if that port was on a
public IP.

The idea is great, but it’s a hassle to set up. It’s not just a large, unwieldy
command you have to do every time, but you have to make sure sshd is set up
properly in order to make a public tunnel on the remote machine. Otherwise you
need to set up two tunnels, one from your machine to a private port on the
remote machine, and then another on the remote machine from a public port to
the private port (that forwards to your machine).

An Easier Solution

I think it’s too much of a hassle to consider SSH tunneling as “a quick and
easy option.” Especially if you’re trying to help somebody else set up a
tunnel. This is when I started to envision a simple command and service to make
this dead simple. Here is the quick and easy way that localtunnel provides:

$ localtunnel 8080

And you’re done! With localtunnel, it’s so simple to set this up, it’s almost
fun to do. What’s more is that the publicly accessible URL has a nice hostname
and uses port 80, no matter what port it’s on locally. And it tells you what
this URL is when you start localtunnel:

This URL can now be shared with others, used for webhook callbacks, etc. We
assume the tunnel will be fairly short-lived, although we don’t actively close
long-lived tunnels. The tunnel will generally be available until you stop the
process.

Here’s another example of using localtunnel to make the built-in Python HTTP
server public for quickly sharing files over the web:

Now you’ll get a directory listing of files in the current directory at that
URL. Share the URL and now people can access those files until you close the
tunnel.

How It Works

To be clear, this is still at its core SSH tunneling, but wrapped up in a nice
package involving a simple client and server. But let’s see what all is
happening.

The localtunnel command is written in Ruby and uses an SSH library to open the
actual tunnel, but first it hits a tunnel registration API. The API is on the
same server that you tunnel through, provided by the server component.

The server component provides two services: a reverse proxy to the forwarded
port, and the tunnel registration API. You can see the registration API for
yourself, just browse to http://open.localtunnel.com. This simple API
allocates an unused port to tunnel on, and gives the localtunnel client the
information it needs to set up an SSH tunnel for you.

Of course, there’s also authentication. As a free and public service, I don’t
want to just give everybody SSH access to this machine (as it may seem).
Instead, since we’re wrapping SSH, we use public keys and per key options to
lock down access while still allowing normal SSH access for anything else on
the box.

The server runs as a user with no shell. It only has a home directory with an
authorized_keys file. The first time you use localtunnel, you have to use the
-k option specifying a public key to pass along when we register a tunnel. We
verify it’s a valid key and then add it to the authorized_keys file with a
bunch of options preventing pretty much any use of SSH other than tunneling to
a private port on the server. After that, assuming SSH can find your private
key, it just works.

We only allow private port tunneling because we don’t want arbitrary public
port forwarding from the server. Public access to this private port on the
server comes through our reverse proxy listening on port 80 for any
*.localtunnel.com requests. We keep the mapping of randomly generated
hostnames for each active port in memory and use that to determine the backend
for our reverse proxy. This backend, however, is actually your local server
thanks to the SSH tunnel.

It’s important to consider that right now there are no real privacy guarantees
and currently no HTTPS support. This is on the roadmap, but for now this means
you may not want to share highly sensitive data over localtunnel.

That’s pretty much it. You can explore further by looking at the code on
GitHub. The server component is
written in Twisted Python and is just over 100 lines of code.

What Now?

You can start using it immediately if you have Ruby and Rubygems installed. You
just need to run:

$ gem install localtunnel

Although it currently depends on Ruby, one contributor is working on a Java
client. Speaking of contributors, there are a few other things on the roadmap
if anybody wants to help: