The journey of running a Golang program over Tor – Hacker Noon
Last weekend I was interested in running an application over Tor to calculate some metrics regarding performance. Moreover, this program wasn’t open source yet.
My first thoughts were that it wouldn’t be problematic since the Tor proxy has a SOCKS5 interface, and there was a high chance that this would be good enough to make it. As you may imagine, that wasn’t the case, and I tried different approaches, from the obvious ones to others more complex.
For a personal history reminder and also to help other people that might run into the same experience, here is the journey I got into making this work.
But first, the necessary caution.
Tor isn’t a silver bullet
Running the node over Tor would make sense on programs that only require output connections to work correctly. Moreover, you should know some basics of Tor mechanics to understand that changing Tor circuits might matter depending on what kind of anonymity are you trying to achieve.
It isn’t a silver bullet for anonymity. With the right amount of effort, statistical analysis can be done considering that Tor exit nodes are public, there could be persistent data in the executable session that can be used to correlate traffic, or some fancy clock-time correlations to leak some information about the user.
Also, running over Tor might break some assumptions made by the author of the program so you can get unexpected results or security consequences. Moreover, since exit nodes are public you have to be cautious about unencrypted traffic since you’ll, with a high chance, be sniffed.
Plan #1 — Configuring SOCKS5 proxy in program
This plan is the most obvious to run an executable behind a proxy: tell the executable to do it. This plan was rapidly dismissed since currently there wasn’t any flag to configure this.
Plan #2 — torsocks
My next approach was using a process wrapper that would do the work behind the scenes, like torsocks.
Torsocks is an interceptor in the middle of the process and the libc library. Since most the executables use libc it works in most of the cases. Behind the scenes, it utilizes the LD_PRELOAD trick to override methods of shared libraries.
You can install
apt-get. This will install
$ apt-get update
$ apt-get install torsocks curl
To check that all work as expected we can check our current IP vs the Tor exit IP:
$ curl https://ifconfig.me
$ torsocks curl https://ifconfig.me
In case you’re having troubles, check that the
tor proxy is running and retry:
sudo service tor start .
Now run the program:
torsocks yourpogram arg1 arg1 ...
Unfortunately, in my case, this didn’t work. More precisely, the program worked as if it wasn’t running within the proxy. I ran the
ldd command and checked the executable was dynamically linked to
libc, so this should work.
After some minutes of research, I found out that Golang no longer depends on
libc for network, for good reasons. If you can avoid that dependency, it’s a good idea to do it. If you’re interested in the official discussion about this, you can find it here.
The executable linked to
libc maybe for other reasons or dependencies. If the program makes syscalls directly, a wrapper interceptor is useless.
Plan #3 — iptables + transparent SOCKS client
In that same discussion, a user mentions that a good solution was using
iptables in conjunction with a transparent-proxy to tunnel the traffic into the SOCKS5 proxy. In my case, it would be the SOCKS5 of my running Tor proxy.
It’s a smart solution but has challenges. Since the transparent-proxy will receive TCP packages with the destination changed by
iptables, a way of identifying the original destination is needed. However, this person is quite smart and already gives a candidate transparent-proxy to implement this solution. Here you can find his initial motivation for building it.
To configure iptables for my needs, I had to add some tricks to make this work:
sudo iptables --table nat -A OUTPUT -p tcp -j REDIRECT --to-ports 1081 -m owner --uid-owner dummytoruser-o wlo1
Some quick explanation of this configuration:
--table natrefers to adding a rule in the
-A OUTPUTadd to the
-j REDIRECT --to-ports <port>is where the magic happens.
-m owner --uid-owner <user> -o wlo1might seem weird for my purpose, but crucial.
What did I mean by magic? It instructs to change TCP packets destination to
localhost:port. In my case, 1081 is the port where
transocks transparent-proxy is listening. This proxy will get the original TCP packet destination, as explained by Hirotaka, by an undocumented SO_ORIGINAL_DST flag in getsockopt syscall.
-o flags were necessary since I was running
tor in the same machine I was trying to run the target program over Tor. I created a
dummytoruser in the computer and set the rule only to apply in processes running in that user. This way, I ran
tor in my actual user, and run the target program as
dummytoruser . The
-o flag make this rule in the interface that has internet access since makes no sense (and will fail!) to Torify to private addresses.
Claps to Hirotaka for open-sourcing his work. I had the opportunity to make a tiny PR as a sign of gratitude.
They may be other ways to solve this problem, but despite this might seem a bit complex it, may save your day.