Making the lab transparently Tor-friendly

Why bother?

Just an interesting challenge, really. I was thinking about whether static routes could be pushed by a DHCP server and trying to think about any subnet I have that would require static routes. Then I remembered that Tor has an option to map resolved .onion addresses to private IP space, so why not?

First challenge: Forwarding Tor DNS

Per the usual Tor configuration, I set up a subnet for .onion addresses to map onto and told Tor to map when they’re resolved.

AutomapHostsOnResolve 1

That’s all well and good, but normally I’d be accessing Tor services from behind the Tor gateway, not in front of the gateway.

Normal case: 
                  [ Tor gateway @ ]
                    /    |    \
    [ Sketchy .onion sites ] [ My VM @ ]

This case:
 [ My network @ ] -- [ Tor gateway @ ]
                                      /    |    \
                        [ Sketchy .onion sites @ ]

It could be pretty neat to have transparent access to Tor hidden services from any computer with no configuration. What’s particularly neat about this is that with the combination of DNS and a static route, we’re effectively recreating the transparent DNS/Routing setup from the Tor TransparentProxy documentation except for an entire network.

Since I’ve already got a BIND DNS server on my network, it was pretty trivial to add resolution via the Tor gateway ( for .onion addresses:

zone "onion" {
        type forward;
        forward only;
        forwarders {; };

A simple forward for the .onion zone should solve it, right? Not so:

Feb 19 20:13:45 dhcp02 named[993]: NOTIMP unexpected RCODE resolving 'supersekritonion.onion/DS/IN':
Feb 19 20:13:45 dhcp02 named[993]: no valid DS resolving 'supersekritonion.onion/A/IN':

Yeah, that’s right, idiot BIND is trying to query DNSSEC for the .onion domain. What’s even more annoying is that you can’t set dnssec-validation for a single zone, it’s a server-wide option. At some point, I may try creating a view and usingĀ dnssec-must-be-secure but it’s not high on my list of priorities. I tossed dnssec-validation no; into /etc/bind/named.conf.options and away I went. A quick rndc reload and I could resolve .onion addresses from any system on my network.

Second challenge: Adding a static route to DHCP

Apparently this functionality isn’t used very often because the configuration is atrocious in isc-dhcp-server. Note the top-level declaration of the type and then the in-subnet declaration of the static route.

option rfc3442-classless-static-routes code 121 = array of integer 8;
option ms-classless-static-routes code 249 = array of integer 8;
subnet netmask {
  option rfc3442-classless-static-routes 10, 10, 192, 192, 168, 0, 50;
  option ms-classless-static-routes 10, 10, 192, 192, 168, 0, 50;

If the static route in there doesn’t make a lot of sense to you, you aren’t the only one. TL;DR: goes through The first byte is the netmask and then the size of the netmask determines how many octets you need of the target network. If you need multiple static routes, just keep comma-separating those bytes.

Also, I had to add this iptables rule on my gateway to allow LAN traffic destined for the Tor IP space to be redirected appropriately:

gateway# iptables -t nat -A PREROUTING -i eth0 -d -p tcp -m tcp -j REDIRECT --to-ports 9040

To test, I logged into a random test system and ran ifdown eth0 && ifup eth0 to renew the DHCP lease, a little bit of ip route and then:

# ip route
default via dev eth0 proto static metric 100 via dev eth0 proto dhcp metric 100

And there we have it, transparent access to .onion sites from my network thanks to DNS forwarding, DHCP static routes, and a DNS/TCP proxy on the gateway.

Update 2018-03-01

When converting my local GitLab server to DHCP reservation, I discovered that it could no longer route externally. A quick glance at the routing table showed that it had no default route. But why? The routers option was clearly specified in dhcpd.conf. Turns out that when you have classless static routes defined, the DHCP client can ignore option routers. The fix is simple: add the default route as a classless route in the configuration. In this case, 0, 192, 168, 0, 3.

Leave a Reply

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