October 2008

tunnelopen - script for opening tunnels on OS X

tunnelopen is a script that lets you open certain resource URLs through an ssh tunnel, similarly to just using the open command. The script sets up an ssh tunnel and redirects the URL through that tunnel. Currently this only works for afp, vnc, http, and https URLs. (For a bit of background information, read my article about tunneling afp over ssh.)

In the simplest case, you use tunnelopen the way you would use open:

tunnelopen afp://remotehost/
The script finds a free local port, sets up a tunnel from that port to the service on the remote host, and then opens a URL to the local port. After the connection for the service is done, the ssh tunnel exits automatically (unless you specify otherwise).

Alternate usernames

Sometimes the remote account is different than your local account. Use the "--login" (or "-l") option:
tunnelopen --login user afp://remotehost/

Three party tunnels

Normally when you use tunnelopen, it sets up an ssh tunnel where the ssh logs into the same machine where the tunnel is going. But often it's necessary to set up the ssh tunnel so that it logs into one machine, and tunnels to another. This is generally because you can't ssh to the machine with the resource. Reasons for this include:

To accomplish this you use the same "--login" option, but also specify a hostname:
tunnelopen -l user@thirdhost afp://remotehost/
Note that you have to specify a hostname in this case, even if it's the same.

Tunneling and NATs

If you are crossing a NAT into a private network (e.g. logging into your home network from the internet), the hostnames on the other side of the NAT won't match. This can cause some confusion if you don't understand exactly what happens.

In the simplest case, tunnelopen lets you do exactly what you normally do:

tunnelopen afp://pool99.myisp.net/
But when you do this, there is really no such machine as pool99.myisp.net. For this to work, you must have configured your home router to forward incoming ssh to a particular machine on your home network. The above command will let you access afp on that machine, even though it is not actually pool99.myisp.net. It's probably something like 192.168.1.100 (and maybe you named it too).

The reason this works is that if you are not using three-party tunneling, then tunnelopen creates an ssh tunnel that forwards to "localhost" on the remote machine. "localhost" should always point a machine at itself, no matter what it's address is.

If you need to access a resource on some other machine behind your NAT, you could configure other ports to forward ssh to other machines. But actually you can still use three party tunnels without this. But in this case you DO have to use the private address for the resource you want:

tunnelopen -l user@pool99.myisp.net afp://192.168.1.101/
This works, because the address in the URL is only used on the remote end of the ssh tunnel, where the address is correct. If you have a nameserver set up on your home network (many home routers do this automatically) then you can also use a local hostname, even though it doesn't exist on the outside network:
tunnelopen -l user@pool99.myisp.net vnc://whale/
As a general rule, the hostname inside the URL is the private name of the resource, and the hostname in the --login option is the public name for it. The exception is that tunnelopen let's you use the public name in the URL, and skip the private name altogether, in the case where there is no actual three-party tunneling.

Re-Usable Tunnels

tunnelopen sets up tunnels as one-time use. As soon as the first connection exits, so does the tunnel. But you may want a reusable tunnel. For example, if you are tunneling access to a private web server, you may want to get more than one web page through that tunnel. The "--daemon" or "-d" option sets this up.

tunnelopen --daemon http://internal.webserver.com/path/you/want.html
When this is used, the ssh tunnel never dies. If you want to get rid of it, you'll have to hunt for the process ("ps -x | grep 'ssh -L'), and kill it by hand. It will also let you know what port is being used, so you can figure out how to connect to the tunnel.

URLs will show up pointing to the local tunnel, e.g. "http://localhost:12345/". How well this will work depends in part on how the remote web page is written. You'll only be able to click through liks that do not specify the full hostname. For instance if a link is specified as "../foo.html", then your browser will figure out the server name from your current server, "http://localhost:12345/". If the web page has full URLs to internal resources, you won't be able to click through them.

This feature can also be accomplished with a timeout. Normally, tunnelopen sets up tunnels with a 30 second timeout to give time for the connection to set up (this really means that all tunnels are re-usable for 30 seconds). You can specify an alternate timeout with the "--timeout" or "-t" option. Time is normally in seconds, but can be followed by an m, h, or d to specify minutes, hours, or days. If the timeout is 60 seconds or longer, tunnelopen will report the port being used, just as it does with the -d option.

Shared Tunnels

Suppose you're at home, and you set up a tunnel that you want all the machines at home to be able to access. This is possible. Use the "--shared" or "-s" option.

But you have to know what you are doing! If you set up a shared tunnel on a public network, the entire internet can use this tunnel to get past the firewall that your tunnel is bypassing.

tunnelopen --shared --daemon http://internal.webserver.com/
Once you've set up the tunnel, you'll have to tell others how to access the resource, since they won't have it opened automatically. In this example, you'll have to look at the URL that you get, and tell other people on your local network to use that. It might typically be "http://192.168.1.100:12345/", or "http://whale.local:12345/"

This option relies on having hostnames correctly set to values that your network will understand.

Other Options

There's also "--port" or "-p" to specify a non-standard ssh port.

That's about it really, except for the "--help" or "-h" option:

host$ tunnelopen -h
Usage: tunnelopen [options] URL
  URL  - currently this can only be vnc, afp, http, or https
  Options are:
  -s/--shared             sets up a connection that all local computers
                          can access (may be VERY insecure if your
			  local network is not private)
  -d/--daemon             keeps the tunnel running after the first connection
			  exits (a good idea if --shared is used).
                          Can't be used with --timeout.
  -t/--timeout            keeps the tunnel running for at least timeout
                          seconds.  Defaults to 30, to leave time for
                          connection setup.  Can be set to longer values
                          to simulate --daemon, but still automatically
                          exit eventually.  May also be specified in
                          minutes, hours or days, by adding an m, h, or d
                          at the end of the number, e.g. "-t 3h"
                          Can't be used with --daemon.
  -l/--login user[@host]  specifies the username for login to the remote system
			  and if a host is also provided, you will ssh
			  into this host, even if that is not where the
			  tunnel is going.  This third party connection
			  gets around a Leopard afp bug.  If login is
			  not specified it uses your current login
			  name.
  -p/--port               specifies an alternate ssh port, if your ssh
                          server doesn't listen on the default port (22).
  -h/--help               show this message

Download

Just download this script, and put it wheverver you want (e.g. $HOME/bin, or /usr/local/bin, or /usr/bin). Make it executable (chmod +x tunnelopen). It is only for OS X!*
Download tunnelopen version 0.8

* It would be possible to get this script to work on other platforms, but it would need additional information on how to start up the appropriate applications for various services. This script benefits from OS X's great "open" command, which let me sidestep all of that.


Reader Comments (Experimental. Moderated, expect delays. Posts may be edited or ignored. I reserve the right to remove any or all comments, at any time.)

No comments

Add a comment


More Mac OS X Stuff


Tom Fine's Home Send Me Email