Friday, January 18, 2013

TOR relay and transparent routing

I assume you already know about TOR, The Onion Router for anonymity to protect your privacy.

TOR is a network so it can only work if there are nodes (relays). If you have a server, you can run one so consider it. Afraid of legal issues? You do not need to run an exit node, a relay is just fine: everything is encrypted.

This post will show you how easy it is to set up a TOR relay on Debian, how to nicely monitor it and how to use it as a transparent router.


Simple: a NAT router and behind a LAN with a server and a workstation.
           ________                ________
 internet |        |      LAN     |        |
----------| (NAT)  |--------------| server |  | router |---------.    |________|
          |________|         |
                      |             |
                      | workstation |


If you are not root, use sudo -i or su to get a root shell then:
# echo 'deb squeeze main' \
  >> /etc/apt/sources.list
# gpg --recv 74A941BA219EC810
# gpg --export --armor 74A941BA219EC810 | apt-key add -
# apt-get update
# apt-get install tor

Prepare a control password

You will need a password to remotely control your TOR server:
$ tor --hash-password test


Open /etc/tor/torrc with your favourite editor and configure a few things:
# TOR SOCKS port

# Control port
HashedControlPassword 16:A908451(...the hash above...)10D9C4B12F

# TOR relay port
ORPort 9001

# Throttle traffic to 100KB/s (800Kbps) but allow bursts up to 200KB/s (1600Kbps)
RelayBandwidthRate 100 KB
RelayBandwidthBurst 200 KB

# No exits allowed, just be a relay node
ExitPolicy reject *:*

# Transparent router
AutomapHostsOnResolve 1
# invoke-rc.d tor reload


I like iptables with ferm, for easy rule making. Edit /etc/ferm/ferm.conf:
domain ip {
  table filter {
    chain INPUT {
      proto tcp dport 9001 ACCEPT; # ORPort
      saddr {
        proto udp dport 53 ACCEPT;   # DNSPort
        proto tcp dport 9040 ACCEPT; # TransPort
        proto tcp dport 9050 ACCEPT; # SocksPort
        proto tcp dport 9051 ACCEPT; # ControlPort
  table nat {
    chain PREROUTING {
      interface eth0 saddr {
        # Do not redirect RFC1918, except VirtualAddrNetwork (
        daddr ( ACCEPT;
        proto tcp syn REDIRECT to-ports 9040; # TransPort
  # Make sure NOT to forward traffic (e.g. udp which does not go via TOR)
  table FORWARD policy DROP;
# invoke-rc.d ferm reload

Port forwarding

The only port you need to forward from your router to your TOR server is 9001.
If your router is also a Linux server, you can do this with ferm again. Edit /etc/ferm/ferm.conf:
domain ip {
  table nat {
    chain PREROUTING {
      daddr {
        proto tcp dport 9001 DNAT to; # ORPort
# invoke-rc.d ferm reload


I like ARM, the Anonymizing Relay Monitor in console:

Arm bandwidth graph and log

# apt-get install tor-arm
And start it:
# arm
You can also run arm remotely, by connecting on the ControlPort (9051) and using the control password.

Use TOR on the workstation

Instead of using TOR for the whole system, let's add a tor user that will pass through TOR, while other users still use the normal connection:
# adduser tor
And now it is policy routing on user.

Edit /etc/ferm/ferm.conf to apply specific rules to tor user:
  1. mark packets for later specific routing
  2. redirect DNS requests to TOR resolver instead of your DNS resolver to avoid DNS leak
@def $RESOLVERS = `awk '$1=="nameserver"{print $2}' /etc/resolv.conf`;

domain ip {
  table mangle {
    chain OUTPUT {
      mod owner uid-owner tor MARK set-mark 0x1;
  table nat {
    chain OUTPUT {
      mod owner uid-owner tor daddr $RESOLVERS
        proto (tcp udp) dport 53 DNAT to;
# invoke-rc.d ferm reload
And route packets differently based on the mark:
# ip rule add fwmark 0x1 table 100
# ip route add default via table 100
# ip route flush cache
To persist after reboot, edit /etc/network/interfaces and add 3 post-up to your network interface:
auto eth0
iface eth0 inet static

  post-up ip rule add fwmark 0x1 table 100
  post-up ip route add default via table 100
  post-up ip route flush cache
Now try:
tor$ curl
[not your IP but a TOR exit node]


  1. This is great, but it depends entirely on iptables, which is unfortunately Linux specific. Do you know if there is a similar setup for running a tor relay on FreeBSD.

  2. It must be possible on FreeBSD too but I don't know the details.

  3. What is the real sense of Tor when your net speed decrease of around 50-60%?
    Maybe just to get some INFO
    Nice INFO indeed

  4. Great post d: what about adding Privoxy to the mix.. I just installed this into a VPS not a pretty picture, I also want to add torsocks, I love to hear your take on this.

  5. @Feb 7th comment: yes TOR will be slower than your regular connection, but it gives you privacy and security. See for more information. Also, adding more relays on fast connections will help TOR network to be faster!

    @Feb 8th comment: yes if you need an HTTP proxy in addition to TOR's socks proxy, privoxy will do that for you.
    And yes torsocks is great, enables you to use TOR transparently: "usewithtor curl" (replacing connect calls).

  6. Thank you for sharing, works perfect!
    I have same network configuration as you, except one thing: on router i have lighthttpd and torrent server deluged, what can i do to send their traffic trough tor too?
    Sorry for my English, i am not native speaker.

  7. You can do it on router itself with policy routing: create user, run programs under that user, mark packets from that user in netfilter and route them separately. You can inspire from part 2 of
    Good luck!

  8. THIS is how it should be done.. :D

    root@tor:/etc# cat /etc/tor/torrc|grep -v ^#|grep -v ^$
    Log notice file /var/log/notices.log
    AutomapHostsSuffixes .onion,.exit
    AutomapHostsOnResolve 1
    ExitPolicy reject *:* # no exits allowed
    CircuitBuildTimeout 5
    KeepalivePeriod 60
    NewCircuitPeriod 15
    AvoidDiskWrites 1
    ExcludeNodes {??},{gb},{us},{fr}
    ExcludeSingleHopRelays 1
    AllowSingleHopCircuits 0
    ClientOnly 1
    ConstrainedSockets 1
    ReachableDirAddresses *:80
    ReachableORAddresses *:443
    SocksPort 9050
    DNSPort 53
    TransPort 9040
    root@tor:/etc# cat iptables-rules
    :INPUT DROP [0:0]
    :FORWARD DROP [0:0]
    :OUTPUT DROP [0:0]
    # INDPUT
    -A INPUT -i lo -m state --state ESTABLISHED,RELATED -j ACCEPT
    -A INPUT -i lo -j DROP
    -A INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
    -A INPUT -i eth0 -j DROP
    -A INPUT -i wlan0 --src --dst -p udp -m udp --dport 53 -j ACCEPT
    -A INPUT -i wlan0 --src -p tcp -m tcp --dport 9040 -j ACCEPT
    -A INPUT -i wlan0 --src -p tcp -m tcp --dport 60000 -j ACCEPT
    -A INPUT -i wlan0 -m state --state ESTABLISHED,RELATED -j ACCEPT
    -A INPUT -i wlan0 -j DROP
    -A INPUT -m state --state INVALID -j DROP
    # OUTPUT
    -A OUTPUT -o lo -m state --state ESTABLISHED,RELATED -j ACCEPT
    -A OUTPUT -o eth0 -m owner --uid-owner debian-tor -p udp -m udp --dport 53 -j ACCEPT
    -A OUTPUT -o eth0 -m owner --uid-owner debian-tor -p tcp -m tcp --dport 80 -j ACCEPT
    -A OUTPUT -o eth0 -m owner --uid-owner debian-tor -p tcp -m tcp --dport 443 -j ACCEPT
    -A OUTPUT -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
    -A OUTPUT -o eth0 -j DROP
    -A OUTPUT -o wlan0 -m state --state ESTABLISHED,RELATED -j ACCEPT
    -A OUTPUT -m state --state INVALID -j DROP
    -A FORWARD -m state --state INVALID -j DROP
    :PREROUTING ACCEPT [32:4626]
    :INPUT ACCEPT [195:15210]
    :OUTPUT ACCEPT [3:216]
    -A PREROUTING -i wlan0 --src -p tcp -m tcp --dport 22 -j REDIRECT --to-ports 60000
    -A PREROUTING -i wlan0 --src -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9040
    -A PREROUTING -i wlan0 --src -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 9040
    -A PREROUTING -i wlan0 --src -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j REDIRECT --to-ports 9040
    root@tor:/etc# cat network/interfaces
    # LOOPBACK - lo0
    auto lo
    iface lo inet loopback
    # ETH - eth0
    auto eth0
    iface eth0 inet dhcp
    pre-up ifconfig eth0 hw ether 00:40:B7:13:37:01
    # WLAN - wlan0
    auto wlan0
    iface wlan0 inet static
    pre-up ifconfig wlan0 hw ether 00:40:B7:13:37:00
    up /sbin/iptables-restore < /etc/iptables-rules