Tor Wifi Gateway

From Leo's Notes
Last edited on 1 September 2019, at 06:22.

Introduction[edit | edit source]

The Tor Wifi Gateway is a open wifi access point that lets users connect to the internet via Tor. The purpose of this is to create a gateway which transparently proxies traffic over to Tor (which intrinsically is useful for other projects), perhaps share internet access to neighbors, and perhaps provide access to my stash without authentication.

What Configuration
External Network Interface (to internet) eth0 (
Internal Network Interface (to wifi access point) eth1 (,

Setup[edit | edit source]

  1. Tor Setup:
  2. DHCP / DNS proxy for the wifi subnet

TOR[edit | edit source]

Edit the /etc/tor/torrc configuration to contain the following lines:

AutomapHostsOnResolve 1

TransPort 9040

DNSPort 53

I've placed the DNS server on a separate IP because I will be using dnsmasq as my 'primary' DNS server that users will be using since I wanted my own portal/splash page with my own domain name (ie: torified.wifi).

Troubleshooting[edit | edit source]

If you are having trouble starting tor as a service with it complaining about being unable to listen on a specific port, ensure you have SELinux configured (or disabled).

DHCP / DNS Server[edit | edit source]

We will be using dnsmasq as the DHCP/DNS server.



# IPTables will route this back to
# Doing this because dnsmasq refuses to listen to a local interface.




# Define hosts

For more information on DNSMasq, see Dnsmasq

Notice that the DNS server is -- this IP does not in fact exist in my network. I will use IPTables (see below) to redirect this traffic to TOR. Reason I am using this IP is because it is not on the interface since dnsmasq will complain otherwise.

dnsmasq is configured to run the DHCP script at /scripts/dnsmasq_dhcp. This script basically ensures that the client gets 'unregistered' from the network when their lease times out or when they reconnect which will force them to my splash page. The contents of the script is quite simple:


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


# 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 53

IPTables[edit | edit source]



$IPT -t nat -F

# Connections from WifiNet must go directly to the gateway.
# Nothing can go directly to the world.

# This gateway will redirect gateway traffic through TOR
$IPT -t nat -X tor_clients
$IPT -t nat -N tor_clients

$IPT -t nat -A PREROUTING -i $TOR_IF -p tcp -d --dport 80 -j ACCEPT
$IPT -t nat -A PREROUTING -i $TOR_IF -p tcp -d --dport 53 -j ACCEPT
$IPT -t nat -A PREROUTING -i $TOR_IF -p udp -d --dport 53 -j ACCEPT
$IPT -t nat -A PREROUTING -i $TOR_IF -p tcp -d --dport 443 -j ACCEPT
$IPT -t nat -A PREROUTING -i $TOR_IF -p tcp -d --dport 80 -j ACCEPT
$IPT -t nat -A PREROUTING -i $TOR_IF -j tor_clients
$IPT -t nat -A PREROUTING -i $TOR_IF -p tcp -j DNAT --to-destination

# Redirect dns traffic going to to
# because dnsmasq refuses to listen to a local interface
$IPT -t nat -A OUTPUT -p udp -d --dport 53 -j DNAT --to-destination
$IPT -t nat -A OUTPUT -p tcp -d --dport 53 -j DNAT --to-destination

# Note: To route traffic for all hosts, use:
# $IPT -t nat -A PREROUTING -i $TOR_IF -p udp --dport 53 -j REDIRECT --to-ports 53
# $IPT -t nat -A PREROUTING -i $TOR_IF -p tcp --syn -j REDIRECT --to-ports 9040

# Allow specific hosts, bypass login screen
$IPT -t nat -I PREROUTING -i $TOR_IF -s -p udp --dport 53 -j REDIRECT --to-ports 53
$IPT -t nat -I PREROUTING -i $TOR_IF -s -p tcp --syn -j REDIRECT --to-ports 9040

$IPT -t nat -I PREROUTING -i $TOR_IF -s -p udp --dport 53 -j REDIRECT --to-ports 53
$IPT -t nat -I PREROUTING -i $TOR_IF -s -p tcp --syn -j REDIRECT --to-ports 9040

# To allow a new host, add the rules below for EVERY individual host:
# $IPT -t nat -I PREROUTING -i $TOR_IF -s $HOST -p udp --dport 53 -j REDIRECT --to-ports 53
# $IPT -t nat -I PREROUTING -i $TOR_IF -s $HOST -p tcp --syn -j REDIRECT --to-ports 9040

To add additional clients that can access the TOR network, run the following lines per IP:

$IPT -t nat -I tor_clients -p tcp -s $IP --syn -j REDIRECT --to-ports 9040 
$IPT -t nat -I tor_clients -p udp -s $IP --dport 53 -j REDIRECT --to-ports 53

To remove clients from access, run:

$IPT -t nat -D tor_clients -p tcp -s $IP --syn -j REDIRECT --to-ports 9040
$IPT -t nat -D tor_clients -p udp -s $IP --dport 53 -j REDIRECT --to-ports 53

Splash Page[edit | edit source]

With the basic infrastructure set up, we will now just need to create the splash page which will show the users the TOS, before letting them 'register' onto the network (which will just add their IP to the tor_clients_chain, thereby enabling internet access). The dnsmasq lease time will cause the IPs to 'unregister' after the lease expires when the /scripts/dnsmasq_dhcp script gets executed.

I wrote a quick PHP script that does this (far from perfect, but it works). You will need to create the actual content pages, but that should be quite trivial.

// The name of your splash page
define("SITE_NAME", "TORified.wifi");

// The secret 'key' used to generate tokens
define("SECRET", "s3kr3tz");

// Path to binaries
define("SUDO_EXEC", "/usr/bin/sudo");
define("IPTABLES_EXEC", "/sbin/iptables");

$secret = isset($_POST['secret']) ? $_POST['secret'] : NULL;

include "pages/header.php";

if ($secret === NULL) {
        include "pages/welcome.php";
} else {
        if (is_valid_key($secret)) {
                if (add_client($ip)) {
                        include "pages/registered.php";
                } else {
                        include "pages/error_registered.php";
        } else {
                include "pages/error_timedout.php";

include "pages/footer.php";

// Generates a key
function generate_key() {
        return md5(SECRET . date("Hi"));

// Verifies whether the key that was submitted on the form is valid.
// Keys expires after a minute.
function is_valid_key($secret) {
        $hour = date("H");
        $minute = date("i");

        return ($secret == md5(SECRET . $hour . $minute)
                || $secret == md5(SECRET . $hour . ($minute - 1))
                || $secret == md5(SECRET . ($hour - 1) . ($minute - 1)));

// Adds a new client to the network by allowing their IP to access TOR
function add_client($IP) {
        $rules = trim(shell_exec(SUDO_EXEC . " " . IPTABLES_EXEC . " -t nat -L|grep $IP | wc -l "));

        // If we have more than 2 rules for this IP, they are probably registered already...
        if ($rules < 2) {
                shell_exec(SUDO_EXEC . " " . IPTABLES_EXEC . " -t nat -I tor_clients -p tcp -s $IP --syn -j REDIRECT --to-ports 9040 2>&1");
                shell_exec(SUDO_EXEC . " " . IPTABLES_EXEC . " -t nat -I tor_clients -p udp -s $IP --dport 53 -j REDIRECT --to-ports 53 2>&1");

                return TRUE;
        return FALSE;