This article will go through the steps necessary to set up a gateway machine that transparently routes traffic through the TOR network. The goal is to have a subnet that will have all TCP traffic routed through TOR which should prevent any leaking of information by the TOR browser (for instance, due to a browser vulnerability).

Prerequisites[edit]

You must have a machine that has at least two network interfaces (one for the external network, one for the TOR network). This guide was created against a clean installation of CentOS 7.1 x86_64.

The two networks will have the following configuration:

Description Interface Addresses
External Network Interface (to internet) eno16777728 10.1.3.39/22
Internal Network Interface (to internal TOR subnet) eno33554952 192.168.192.1/24

This host will be the gateway for the internal TOR subnet.

Installation[edit]

Install the TOR package. You can find the TOR repository at https://deb.torproject.org/torproject.org/.

For CentOS 7, the packages are at https://deb.torproject.org/torproject.org/rpm/el/7/x86_64/.

Install dnsmasq. Optionally, install Apache for your internal website.

Disable SELinux.

Ensure that your network interfaces are set up correctly. Because the internal network does not have a DHCP server, you will need to set it manually:

ip addr add 192.168.192.1/24 dev eno33554952

Configuration[edit]

Create the dnsmasq.conf file:

listen-address=192.168.192.1
port=53

strict-order
no-resolv

# Remote DNS server (this should go to TOR's DNS service)
# This is set to 1.1.1.1 which is routed by IPTables to TOR's service.
# DNSMasq does not allow you to set this to an internal IP address.
server=1.1.1.1

interface=eno33554952
address=/torified.wifi/192.168.192.1

dhcp-script=/scripts/dnsmasq_dhcp
dhcp-range=192.168.192.20,192.168.192.250,2h
dhcp-option=1,255.255.255.0
dhcp-option=6,192.168.192.1
dhcp-option=3,192.168.192.1

# This is the external (non TOR) network interface.
no-dhcp-interface=eno16777728

domain=torified.wifi
log-queries
log-dhcp

Enable and start the TOR service.

systemctl enable dnsmasq
systemctl start dnsmasq


Create the torrc file containing: (/etc/tor/torrc)

Log notice file /var/log/tor/notices.log
DataDirectory /var/lib/tor
VirtualAddrNetwork 10.192.0.0/10

TransPort 9040
TransListenAddress 192.168.192.1

DNSPort 5353
DNSListenAddress  192.168.192.1
AutomapHostsOnResolve 1

As of this writing (version 0.2.6.10), the TOR package only has the old SystemV startup script and not the systemd service unit file. Create /usr/lib/systemd/system/tor.service containing:

[Unit]
Description = Anonymizing overlay network for TCP
After = syslog.target network.target nss-lookup.target tor-firewall.service

[Service]
Type = simple
User = _tor
ExecStart = /usr/bin/tor --runasdaemon 0 -f /etc/tor/torrc --quiet
ExecReload = /bin/kill -HUP ${MAINPID}
ExecStop = /bin/kill -INT ${MAINPID}
TimeoutSec = 30
Restart = on-failure
LimitNOFILE = 4096

[Install]
WantedBy = multi-user.target

Enable and start the TOR service.

systemctl enable tor.service
systemctl start tor.service


Create the firewall configuration script (firewall.sh):

#!/bin/sh

IPT=/usr/sbin/iptables
TOR_UID=998
TOR_NET=192.168.192.0/24
TOR_IF=eno33554952
OUT_IF=eno16777728

ip addr add 192.168.192.1/24 dev $TOR_IF

$IPT -F
$IPT -t nat -F

# Connections cannot be forwarded from one network to another.
$IPT -A FORWARD -i $TOR_IF -j REJECT

# Accept HTTP traffic for web service running on this host.
$IPT -t nat -A PREROUTING -i $TOR_IF -p tcp -d 192.168.192.1 --dport 80 -j ACCEPT

# Accept DNS traffic (handled by dnsmasq which goes to 1.1.1.1)
$IPT -t nat -A PREROUTING -i $TOR_IF -p udp -d 192.168.192.1 --dport 53 -j ACCEPT

# DNSMasq will send DNS queries to 1.1.1.1 which will be routed to the TOR DNS service
$IPT -t nat -A OUTPUT -p udp -d 1.1.1.1 --dport 53 -j DNAT --to-destination 192.168.192.1:5353

# Client chain. Append clients who should have access to this chain.
$IPT -t nat -X tor_clients
$IPT -t nat -N tor_clients
$IPT -t nat -A PREROUTING -i $TOR_IF -j tor_clients

# Allow all clients to connect.
# $IPT -t nat -A tor_clients -i $TOR_IF -p udp --dport 53 -j REDIRECT --to-ports 53
# $IPT -t nat -A tor_clients -i $TOR_IF -p tcp --syn -j REDIRECT --to-ports 9040

# Allow a specific client to connect.
$IPT -t nat -A tor_clients -i $TOR_IF -p udp -s 192.168.192.128 --dport 53 -j REDIRECT --to-ports 53
$IPT -t nat -A tor_clients -i $TOR_IF -p tcp -s 192.168.192.128 --syn -j REDIRECT --to-ports 9040


# Allow the 1.1.1.1 DNS redirection (UDP to the TOR server IP)
# Allow DNS to TOR 5353 service. Allow DNS from this host to the subnet.
$IPT -A OUTPUT -p udp --dport 5353 -j ACCEPT
$IPT -A OUTPUT -p udp -s 192.168.192.1 -d 192.168.192.1/24 -j ACCEPT

# Allow web service
$IPT -A OUTPUT -p tcp --sport 80 -s 192.168.192.1 -d 192.168.192.1/24 -j ACCEPT

# Allow this talking to internal network
$IPT -A OUTPUT -p tcp -s 10.1.3.39 -d 10.1.1.0/22 -j ACCEPT

# Allow TOR
$IPT -A OUTPUT -m owner --uid-owner 0 -j ACCEPT
$IPT -A OUTPUT -m owner --uid-owner $TOR_UID -j ACCEPT
$IPT -A OUTPUT -j LOG --log-prefix "drop: " --log-level 4
$IPT -A OUTPUT -j REJECT

Run the firewall script above to set up IPTables.

The TOR gateway should now be operational.

Create a systemd service unit to execute the firewall script on startup. See Systemd

/usr/lib/systemd/system/tor-firewall.service

[Unit]
Description=TOR Firewall
After=network.target
ConditionFileIsExecutable=/scripts/firewall.sh

[Service]
Type=oneshot
ExecStart=/scripts/firewall.sh
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99

[Install]
WantedBy=multi-user.target

Enable the service so that it is triggered on startup.

systemctl enable tor-firewall

Optional Services[edit]

You may want to set up a web portal to allow users to register in order to access the TOR network. Install apache and use the script in the wifi page.

You can make DNSMasq execute a script whenever there is a change in the DHCP lease. This is useful to automatically remove hosts from having access to TOR after the lease expires.

#!/bin/sh

if [ $# -ne 3 -a $# -ne 4 ] ; then
        echo "Usage: dnsmasq_dhcp <add|del> <mac> <ip> [hostname]"
        exit
fi

TOR_IF="eno33554952"
Action=$1
IP=$3

# We want to remove them when they lose their lease...
if [[ "$Action" = "del" ]] || [[ "$Action" = "old" ]] ; then
        /sbin/iptables -t nat -D tor_clients -p tcp -s $IP --syn -j REDIRECT --to-ports 9040
        /sbin/iptables -t nat -D tor_clients -p udp -s $IP --dport 53 -j REDIRECT --to-ports 5353
fi