SSH, Subversion through SOCKS proxy on Mac OS X

UPDATE Apr 2, 2012
Due to the complete lack of updates for tsocks, I recommend the use of proxychains over tsocks. It accomplishes the same thing but works out of the box.

One persistent problem that I run into is that I need to access certain network resources through a SOCKS proxy server. This is all well and good if they are web resources — Safari, Firefox, etc. support SOCKS proxies quite well. However, I also need, for example, SSH and Subversion access to some resources. SOCKS support is woefully inadequate or nonexistent in these tools.

In the case of SSH, even if you google for this, you’ll run through thousands of examples of using ssh as a SOCKS server, but not through one as a SOCKS client. There are some convoluted solutions, but none of them I can use directly on an OS X 10.5 machine.

TSocks: the solution…if it were that easy

Now, tsocks is a nifty little tool to transparently divert network calls through a SOCKS 4 or SOCKS 5 proxy. This allows even non-SOCKS-aware applications to function through a SOCKS server.

Unfortunately it is very old, unmaintained code (1.8 beta 5 was released in 2002). It doesn’t compile cleanly on OS X due to this, nor will it compile under GCC 4.x. Further, it won’t work out of the box either if you do manage to compile it. The problem is that it relies on the Linux-only LD_PRELOAD functionality to use a shared library to hijack network system calls. This mechanism is called DYLD_INSERT_LIBRARIES on OS X and only works if DYLD_FORCE_FLAT_NAMESPACES is active.

Getting a working tsocks: MacPorts

There is an easy way to get tsocks. MacPorts ships a ported tsocks package. If you use MacPorts, sudo port install tsocks should do it.

Unfortunately on several machines I don’t use MacPorts, and don’t want to pull down an entire third-party package manager with its own library tree on each of these boxes. So I have do to this the hard way.

Getting a working tsocks: rolling my own

First to notice is that there are two tsocks distributions. One is the original tsocks 1.8b5, last updated in the first half of this decade. To make it work, follow the instructions provided by Marc Abramowitz in 2006. Note that his patch is actually located at his new domain address instead of the old, linked one.

The MacPorts distribution, on the other hand, is based on R. Garcia’s patched tsocks distribution, incorporating some modernization and new features by the Tor team. This distribution is numbered 1.8.x, with the last being 1.8.4. Unfortunately it is also no longer maintained, as the Tor devs forked this into a custom version to use with the Tor network only. Unfortunate, but for now, it still compiles, and works a bit better than the 2002 original.

To roll your own tsocks via source out of the MacPorts distribution, you will want the patches from the MacPorts repository. An outline of the compilation procedure:

  1. Download tsocks 1.8.4 from the author’s page
  2. Download all the patches from the MacPorts repository
  3. Concatenate all of the patches together:
    cat patch-* > tsocks.osx.patch
  4. Put the concatenated tsocks.osx.patch file into the tsocks source directory. Apply the patches:
    patch -p0 < tsocks.osx.patch
  5. Regenerate the configure script:
    autoreconf
  6. Configure the package:
    ./configure --prefix=/usr/local --bindir=/usr/local/bin --mandir=/usr/local/man --sysconfdir=/etc --libdir=/usr/local/lib
  7. Install the library and binaries:
    sudo make install
  8. Install the conf file:
    sudo cp ./tsocks.conf.complex.example /etc/tsocks.conf
  9. Edit the conf file. Make sure that if you’re not using tor, that you write in the conf file
    tordns_enable = false

Configuring tsocks

The complex configuration file example should have explained all of the features to be set. For my configuration:

Some important settings:

  • local – this setting, in the format of IP/netmask can be repeated several times, each time to exclude a set of IPs from being diverted to the SOCKS server. For obvious reasons, your SOCKS server will have to exist in one of these excluded IP ranges – otherwise you will never even reach your proxy server.
  • server and server_port – these should point to the IP address and port of your SOCKS server
  • server_typetsocks defaults to SOCKS4 mode. You may wish to set it to 5 for SOCKS5 usage.
  • tordns_enable – this needs to be set as false if you don’t use Tor.

Using tsocks

Once this is set up, simply prefixing the network command you want to run with tsocks will force a diversion through the proxy connection. For example:

tsocks ssh example.com

The same can be applied to Subversion.

tsocks svn update

will force the svn client to act through the proxy set in tsocks.conf.

SOCKS on localhost

Note that SOCKS services on 127.0.0.1 has a minor gotcha. Sometimes, you are able to SSH into a remote machine, and use that connection as your SOCKS server. This is described in my post about using SSH as a pseudo-VPN, which describes the -D switch. My use case here is that once you do this, all further local SSH connections to other machines should be diverted through the first SSH. For example, I’d like to do:

my-machine$ ssh -D 40000 gateway.example.com # establish a SOCKS server on localhost:40000 to the gateway host

and then:

my-machine$ ssh lan-1.example.com # access the protected lan-1 machine through the SOCKS, which will see me as gateway.example.com 

This is very doable in the tsocks setup if you set tsocks.conf:

server = 127.0.0.1/255.255.255.255
server_port = 40000

and then:

my-machine$ ssh -D 40000 gateway.example.com
my-machine$ tsocks ssh lan-1.example.com

This is the gotcha: make sure the netmask is set correctly to 255.255.255.255. Otherwise tsocks will die with a cryptic:

IP (127.0.0.1) & SUBNET (0.0.0.0) != IP on line 22 in configuration file, ignored

It is apparently fairly sensitive about the subnet mask setup to conform to exact standards.

With this tsocks setup, you won’t have to create special VPNs to lock a LAN machine behind a gateway. As long as you can SSH into the gateway machine from your local machine, you can access the resources behind it with any application on your local machine via tsocks. Nifty, huh?

13 Replies to “SSH, Subversion through SOCKS proxy on Mac OS X”

  1. I’m having difficulty getting this to work. I’m using the setup you described above with the ssh -D switch but when I turn on debugging via the TSOCKS_DEBUG variable I receive an error similar to the following: 04:23:39 (89730): do_resolve: error connecting to SOCKS server. My server is running (firefox can connect to it) and I believe my ip/port/type are correct. Any ideas?

    1. Hm. I haven’t run into this before, so I don’t have practical advice. Looking at the source code, that particular error is in fact issued by tsocks when it tries to connect to the SOCKS server you specified but can’t. It’s kind of a catch-all error, so it’s hard to tell what it’s specifically doing wrong. But error message basically does mean that it’s not reaching the SOCKS server you specified in the conf file — maybe because that server is somehow inaccessible, or you have configured the wrong one, or something else is preventing it specifically from accessing the SOCKS server.

      I do need a bit more context, but I’ll make some assumptions here. If I understand your context correctly, you’re trying to SSH through the gateway server G to a second, target host H, using SOCKS server S. If you’re using ssh -D, I’m also assuming that you’re trying to use SSH as a SOCKS server (meaning that your SOCKS server S actually sits locally on your localhost 127.0.0.1, you’re using the network through G to go to H, and that you can reach G without using any proxies). If these assumptions are not true, please let me know.

      Here’s a checklist of configuration-related things (pardon me if some of these are obvious — just trying to cover the context):
      – when you ran ssh -D, you connected to G and the SOCKS server is up. You said Firefox worked — this should mean that you used Firefox’s SOCKS5 feature to connect to your local SOCKS server at 127.0.0.1, and appear to other hosts on the Web as G)
      – tsocks.conf: server=127.0.0.1
      – tsocks.conf: port= (whatever number you set after ssh -D)
      – tsocks.conf: local=127.0.0.1/255.255.255.255
      – that you’re using tsocks when making the second SSH connection – tsocks ssh H.

      If all of these are right, you might try something simple, like telnet. If you are able to tsocks telnet your way out, then the problem might lie in SSH. Also, make sure that you do have DNS resolution, either via your ordinary DNS server, or your SOCKS server.

      If none of these help, you might try asking someone who works on tsocks or (more likely) a tsocks-spinoff project. Sorry I can’t be of more help.

      1. Thanks for the help, I had my local mask incorrect so my server was not in the masked range. Now I’m running into an issue where it appears that tsocks is not using remote DNS. For better context here is my setup:

        OSX 10.5
        tsocks from port

        tsocks.conf:
        local = 127.0.0.1/255.255.255.255
        server = 127.0.0.1
        server_port = 1080
        server_type = 5
        tordns_enable = false

        ssh -D 1080 user@server

        I’m actually trying to use this with git-svn so I type something like:

        git svn rebase

        It chugs away (debug output seems to provide nothing too useful, lots of “Call to close(n)” then it says “Host not found”. Similarly if I say tsocks ping remote-server same thing. However, if I ssh user@server I can ping the server that I’m trying to reach so tsocks should be able to get there. Any ideas?

      2. That’s pretty odd. I actually have ran into DNS issues before with tsocks, but only when the final target hostname is not globally resolvable. If you must resolve your final hostname via SOCKS, then things break. Apparently when they patched tsocks to work with Tor, they also semi-broke the original “resolve DNS hosts via SOCKS 5” functionality. There’s a compile-time switch to disable the Tor-based DNS resolution entirely and force it to use the original SOCKS-based DNS resolution, but I had some trouble compiling the package using that switch.

        Is your end host’s DNS name resolvable only through the “gateway” server, or should it be globally resolvable? If it’s globally resolvable, it should try to use your usual ISP DNS server (or whatever DNS you have configured in Network preference pane). If it’s a private hostname that only the gateway knows, then you’ll probably have to find some way to get DNS resolution working with tsocks.

  2. Excellent blog. There’s not much information out there about SOCKS support for Macs. I like you have scoured the net for an SSH client with SOCKS support. I did chance upon ZOC, and am trying out a trial version, but in the meantime I’ve got tsocks from macports installed and was wondering if you’ve ever hit this problem ? I can only get it to work if I specify a default SOCKS proxy, without which it segmentation faults ( no debug messages 🙁 ), and I can’t seem to get tsocks to use the path blocks to specify different SOCKS proxies for various networks.

    So using the config below, I can ‘tsocks ssh user@150.60.198.12‘ without any problem only if a default server is specified correctly. The path stanza just seems to be ignored. And if I leave out the default server entry all together, tsocks seg faults.

    local = 192.168.1.0/255.255.255.0
    local = 129.39.109.0/255.255.255.0
    path {
    reaches = 150.60.198.0/255.255.255.0
    server = 129.39.109.202
    server_type = 4
    server_port = 1080
    }
    server = 129.39.109.202

    1. Hi Dennis,

      I used your script (with IPs modified for my setup), removed the “server” line at the end, and used it to set up a path without a default server. I was not able to reproduce the segfault. That setup worked for me just fine, which means I can’t help you debug this directly, but also means that tsocks is supposed to handle this use case. It is likely that our setups still differ significantly (I’m using SOCKS 5, for example) that I’m not triggering the code path that has the bug in it.

      If you know the C language, you can try to instruct MacPorts to compile with the -g flag. Then, you can run tsocks under GDB, which will give you the exact backtrace of which function failed at what line of source code. At that point, we can try to see if we can fix that bug ourselves. I’m very curious as to why it doesn’t seem to work for you when it works on my setup — there must be a relatively simple fix.

    2. Try to add “tordns_enable = false”.
      I had the same problem with “path” block, but following works now:

      local = 127.0.0.1/255.255.255.255
      tordns_enable = false
      path {
      server = 127.0.0.1
      server_port = 10000
      server_type = 5
      reaches = 10.20.4.0/255.255.255.0
      }

  3. hi,I’m a new mac user ,so I couldn’t understand the process ,
    will you simplify it for me?!
    tell me what should I do first !?
    thank you,

Leave a Reply

Your email address will not be published. Required fields are marked *