For a while now, I've been wanting to set up a PF-based firewall that transparently proxies everything over Tor. I've also wanted to help the Tor project out and run a relay. I don't want to take the risk of running an exit node (and getting banned from all sorts of services I actively use). Since I love FreeBSD, I decided to try my luck setting this up on this fantastic OS. To do a test run prior to wider deployment, I set up transparent tor proxying on my netbook and created a little script to turn off and on the proxy. This article will show you how to do the same. The official wiki-based documentation will give you a basic understanding of what to do. Their example scripts don't seem to work that great on FreeBSD 9-STABLE.


When the transparent proxy is enabled, DNS requests will be forwarded over to tor. We need to be able to handle behind-the-scenes switching from regular DNS to DNS-over-tor. We'll set up named to listen on and forward DNS requests to an upstream provider (we'll use Google's DNS server,, as an example). In /etc/namedb/named.conf, you'll see these commented-out lines: forwarders {; }; Change that to be: forwarders {; };Enable and start named:

  • Enable named in /etc/rc.conf:
    • echo 'named_enable="YES"' >> /etc/rc.conf
  • Start named:
    • service named start
  • Make it so that we always use as our nameserver. This assumes you're using dhclient:
    • echo "supersede domain-name-servers;" > /etc/dhclient.conf

Network Interfaces

We'll have tor run its own DNS server on lo1, which will have an IP of Our PF rules will forward DNS requests to tor. So let's create a new loopback interface and set its IP to

echo 'cloned_interfaces="lo1"' >> /etc/rc.conf

echo 'ifconfig_lo1=""' >> /etc/rc.conf

Configuring Tor

Install security/tor via your favorite method (ports, pkg_add, pkgng). So let's now configure Tor. We'll set it up so that it creates is own virtual network (all in memory) for DNS requests. We'll configure it so that it listens for DNS on port 1053. Normally, you'd configure it so that it listens on port 53, but there are two problems with that. First, named is listening on port 53 already. Second, Tor drops root privileges prior to setting up the DNS server socket, so Tor can't bind to port 53. The official documentation doesn't address the second problem.

Here's my /usr/local/etc/tor/torrc file:


AutomapHostsOnResolve 1

TransPort 9040

DNSPort 1053

Setting Up PF

Tor will need access to the /dev/pf device. In a display of awesome stupidity, Tor attempts to access the device after dropping root privileges. So we'll add an entry to /etc/devfs.conf to set the _tor user to own /dev/pf.

To add the rule to add the entry in /etc/devfs.conf:

echo 'own pf _tor:_tor' >> /etc/devfs.conf

I placed in a file on my home directory (which I called pf.conf) the following ruleset:

int_if = "wlan0"


# Don't use tor for the local network

non_tor = "{ }"


trans_port = "9040"


scrub in


rdr pass on { lo1 $int_if } inet proto tcp to !($int_if) -> port $trans_port

rdr pass on lo0 inet proto udp to port domain -> port 1053


# Don't use tor for ssh'ing into my box

pass out quick inet proto tcp to port 22 keep state


block return out

pass quick on { lo0 lo1 } keep state


pass out quick inet proto tcp user _tor flags S/SA modulate state

pass out quick route-to lo1 inet proto udp to port 1053 keep state

pass out quick inet to $non_tor keep state

pass out route-to lo1 inet proto tcp all flags S/SA modulate state

You will want to change int_if to your ethernet device. You'll also want to change non_tor to the subnet for your local network. That's it! You can enable the tor transparent proxy by executing: pfctl -e -f /path/to/pf.conf. You can disable the tor transparent proxy by executing: pfctl -d.

You can find the script and pf configuration file I use on GitHub.