OpenWrt router with Tor, SSH as hidden service

Prerequisites

You will need a router with the open source firmware OpenWrt installed (tested with OpenWrt 18.06.1).

If you don't want to flash a router with OpenWrt yourself or don't have a compatible router, check out e.g. GL.iNet routers. Their devices are well engineered, privacy-friendly, powered by OpenWrt and come in various form factors and flavours. The devices have built-in support for the VPNs Wireguard and OpenVPN or even Tor (which support is declared as beta and didn't work with my devices).

SSH access has to be working (see: Guide for newcomers) and the router must have internet access.

Install required packages

  • tor (tested with v0.4.2.7-1)
  • tor-geoip (tested with v0.4.2.7-1)

If you have a GL-AR300M, these packages are already installed.

Via LuCi

  • Search and install the packages through System > Software.

Via CLI/SSH

opkg update
opkg install tor tor-geoip

Configure Tor

root@GL-AR300M:~# mkdir -p -m 0700 /etc/tor/data /var/lib/tor /etc/tor/hidden_service_ssh
root@GL-AR300M:~# chown tor: /etc/tor/data /var/lib/tor /etc/tor/hidden_service_ssh
root@GL-AR300M:~# cat <<EOF > /etc/tor/torrc
RunAsDaemon 1
AllowUnverifiedNodes middle,rendezvous
Log notice syslog
## Only run as a client, never a relay or exit
ClientOnly 1
DataDirectory /etc/tor/data/
HiddenServiceDir /etc/tor/hidden_service_ssh/
HiddenServicePort 22 127.0.0.1:22
User tor
SocksPort 9050
SocksPort 192.168.8.1:9050
SocksPolicy accept 127.0.0.1/32
SocksPolicy accept 192.168.8.0/24
SocksPolicy reject *
#AutomapHostsSuffixes .exit,.onion
AutomapHostsSuffixes "."
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
TransPort 192.168.8.1:9040
DNSPort 192.168.8.1:9053
TruncateLogFile 1
LogMessageDomains 1
Log [control]notice  file /var/lib/tor/control.log
StrictNodes 1
ExitNodes {IS}
EOF

Create/Replace service script /etc/init.d/tor (changes the user to tor):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2011 OpenWrt.org

START=50
STOP=50

USE_PROCD=1

SERVICE_USER=tor
SERVICE_GROUP=tor

start_service() {
    local enable=$(uci get tor.global.enable)
    [ $enable != "1" ] && exit 0

    rm -rf /var/lib/tor/*

    [ -f /var/run/tor.pid ] || {
        touch /var/run/tor.pid
        chown $SERVICE_USER:$SERVICE_GROUP /var/run/tor.pid
    }
    [ -d /var/lib/tor ] || {
        mkdir -m 0755 -p /var/lib/tor
        chmod 0700 /var/lib/tor
        chown $SERVICE_USER:$SERVICE_GROUP /var/lib/tor
    }
    [ -d /var/log/tor ] || {
        mkdir -m 0755 -p /var/log/tor
        chown $SERVICE_USER:$SERVICE_GROUP /var/log/tor
    }
    [ -f  /var/lib/tor/control.log  ] && {
        rm /var/lib/tor/control.log
    }
    procd_open_instance
    procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-0}
    procd_set_param stderr 1
    procd_set_param command /usr/sbin/tor --runasdaemon 0
    procd_close_instance
}

Ensure basic UCI Tor config in /etc/config/tor:

config global 'global'
    option country 'CH'
    option enable '1'

Ensure that /etc/config/firewall contains:

config rule 'tor_dhcp'
    option name 'Allow-Tor-DHCP'
    option src 'lan'
    option proto 'udp'
    option dest_port '67'
    option family 'ipv4'
    option target 'ACCEPT'

config rule 'tor_dns'
    option name 'Allow-Tor-DNS'
    option src 'lan'
    option proto 'udp'
    option dest_port '9053'
    option family 'ipv4'
    option target 'ACCEPT'

config rule 'tor_tras'
    option name 'Allow-Tor-Transparent'
    option src 'lan'
    option proto 'tcp'
    option dest_port '9040'
    option family 'ipv4'
    option target 'ACCEPT'

config rule 'tor_socks'
    option name 'Allow-Tor-SOCKS'
    option src 'lan'
    option proto 'tcp'
    option dest_port '9050'
    option family 'ipv4'
    option target 'ACCEPT'

config redirect 'tor_allow_http'
    option name 'Allow access http'
    option src 'lan'
    option src_ip '192.168.8.1/24'
    option src_dip '192.168.8.1'
    option src_dport '80'
    option family 'ipv4'
    option proto 'tcp'
    option target 'ACCEPT'

config redirect 'tor_allow_ssh'
    option name 'Allow access  ssh'
    option src 'lan'
    option src_ip '192.168.8.1/24'
    option src_dip '192.168.8.1'
    option src_dport '22'
    option family 'ipv4'
    option proto 'tcp'
    option target 'ACCEPT'

config redirect 'tor_allow'
    option name 'Allow access tor domain'
    option src 'lan'
    option src_dport '9050'
    option family 'ipv4'
    option proto 'tcp'
    option target 'ACCEPT'

config redirect 'dns_int'
    option name 'Intercept-DNS'
    option src 'lan'
    option src_dport '53'
    option dest_port '9053'
    option family 'ipv4'
    option proto 'udp'
    option target 'DNAT'

config redirect 'tcp_int'
    option name 'Intercept-TCP'
    option src 'lan'
    option dest_port '9040'
    option family 'ipv4'
    option proto 'tcp'
    option extra '--syn'
    option target 'DNAT'

If you have a GL.iNet router, use this slightly modified custom firewall script:

root@GL-AR300M:~# cat /etc/firewall.user

force_dns() {
    # lanip=$(ifconfig br-lan |sed -n 's/.*dr:\(.*\) Bc.*/\1/p')
    lanip=$(uci get network.lan.ipaddr)
    tor=$(ps|grep /usr/sbin/tor|grep -v grep)
    [ "$1" = "add" ] && {
        ip=$(uci get glconfig.general.ipaddr)
        [ -z "$ip" ] && ip=$(uci get network.lan.ipaddr)
        iptables -t nat -D PREROUTING -i br-+ -s 0/0 -p udp --dport 53 -j DNAT --to $ip
        iptables -t nat -D PREROUTING -i br-+ -s 0/0 -p tcp --dport 53 -j DNAT --to $ip

        uci set glconfig.general.ipaddr=$lanip
        uci commit glconfig
        iptables -t nat -C PREROUTING -i br-+ -s 0/0 -p udp --dport 53 -j DNAT --to $lanip
        [ ! "$?" = "0" ] && iptables -t nat -I PREROUTING -i br-+ -s 0/0 -p udp --dport 53 -j DNAT --to $lanip
        iptables -t nat -C PREROUTING -i br-+ -s 0/0 -p tcp --dport 53 -j DNAT --to $lanip
        [ ! "$?" = "0" ] && iptables -t nat -I PREROUTING -i br-+ -s 0/0 -p tcp --dport 53 -j DNAT --to $lanip

        if [ -n "$tor" ];then
            iptables -t nat -C PREROUTING -i br-lan -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 9053
            [ ! "$?" = "0" ] && iptables -t nat -I PREROUTING -i br-lan -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 9053
            iptables -t nat -C PREROUTING -i br-lan -p tcp -m tcp --dport 9050 -j REDIRECT --to-ports 9050

            [ ! "$?" = "0" ] && iptables -t nat -I PREROUTING -i br-lan -p tcp -m tcp --dport 9050 -j REDIRECT --to-ports 9050
            iptables -t nat -C PREROUTING -i br-lan -p udp -m udp --dport 53 -j REDIRECT --to-ports 9053
            [ ! "$?" = "0" ] && iptables -t nat -I PREROUTING -i br-lan -p udp -m udp --dport 53 -j REDIRECT --to-ports 9053
        fi
    }

    [ "$1" = "remove" ] && {
        lanip=$(uci get glconfig.general.ipaddr)
        [ -z "$lanip" ] && lanip=$(uci get network.lan.ipaddr)
        iptables -t nat -C PREROUTING -i br-+ -s 0/0 -p udp --dport 53 -j DNAT --to $lanip
        [ "$?" = "0" ] && iptables -t nat -D PREROUTING -i br-+ -s 0/0 -p udp --dport 53 -j DNAT --to $lanip
        iptables -t nat -C PREROUTING -i br-+ -s 0/0 -p tcp --dport 53 -j DNAT --to $lanip
        [ "$?" = "0" ] && iptables -t nat -D PREROUTING -i br-+ -s 0/0 -p tcp --dport 53 -j DNAT --to $lanip

        if [ -n "$tor" ];then
            iptables -t nat -D PREROUTING -i br-lan -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 9053
            iptables -t nat -D PREROUTING -i br-lan -p udp -m udp --dport 53 -j REDIRECT --to-ports 9053
            iptables -t nat -D PREROUTING -i br-lan -p tcp -m tcp --dport 9050 -j REDIRECT --to-ports 9050
        fi
    }
}

force=$(uci get glconfig.general.force_dns)
if [ -n "$force" ]; then
    force_dns add
else
    force_dns remove
fi
gl-firewall

# PPTP Passthrough
iptables -t raw -D OUTPUT -p tcp -m tcp --dport 1723 -j CT --helper pptp
iptables -t raw -A OUTPUT -p tcp -m tcp --dport 1723 -j CT --helper pptp

Start tor:

root@GL-AR300M:~# service tor start

Ensure that the service is running:

root@GL-AR300M:~# ps -w | grep /sbin/tor | grep -v grep
24645 tor      31804 S    /usr/sbin/tor --runasdaemon 0

Get hostname:

root@GL-AR300M:~# cat /etc/tor/hidden_service_ssh/hostname
<tor-address>.onion

SSH access via Tor (Linux)

  • Install tor
  • Start tor: sudo systemctl start tor
  • Ensure that tor is up and running:

    curl --socks5 127.0.0.1:9050 -s https://check.torproject.org/ | cat | grep -m 1 Congratulations | xargs
    
  • Edit ~/.ssh/config:

    Host my-router
        User root
        # Use the tor address from above
        Hostname <tor-address>.onion
        # If you are connected to the router's default Wifi and want to test it
        #ProxyCommand=nc -X 5 -x 192.168.8.1:9050 %h %p
        # Use the local tor proxy
        ProxyCommand=nc -X 5 -x 127.0.0.1:9050 %h %p
    

Resources that helped me:

Jan Beilicke

About the author

Jan Beilicke is a long-time IT professional and full-time nerd. Open source enthusiast, advocating security and privacy. Sees the cloud as other people's computers. Find him on Mastodon or Twitter.