Alex L. Demidov

DevOps/SRE consultant

How to Make a Keenetic Router a Tailscale Exit Node.

Tailscale software is available for Keenetic routers as an OpenWRT package (OPKG). OPKG support is optional and needs to be enabled (See the documentation). It is available for routers with USB ports that support USB flash drives (the supported routers are listed in the documentation).

External USB storage is preferred, as internal router memory is limited to a few hundred MB and Tailscale binaries are about 50 MB.

Prepare USB drive

The first step is to prepare a USB drive with an ext4 file system. It can be a partition or the entire USB can be formatted as an ext4 file system. It needs to be done on a separate Linux system. (See the documentation on how to format a USB drive on different systems)

1
2
3
4
5
6
# check which devices are the USB drives (match TRAN=usb)
lsblk -o NAME,MODEL,TRAN,TYPE,SIZE,MOUNTPOINT

# format the entire USB as an ext4 file system and assign label `USB128GB`
# replace sdX with the device name from `lsblk` output
mkfs.ext4 -L 'USB128GB' /dev/sdX

The next step is to download the Entware installer and save it to the USB drive. The installer should match the router architecture (mipsel, mips or aarch64). See the documentation section 3 to identify which router model requires a specific architecture. The example below uses mipsel architecture.

1
2
3
4
5
6
7
8
9
10
11
12
13
# mount the file system
mkdir /mnt/usb # create a mount point
mount -t ext4 /dev/sdX /mnt/usb # replace sdX with the device name

# create install directory
mkdir /mnt/usb/install

# download the Entware installer for mipsel architecture
curl -O --output-dir /mnt/usb/install \
  https://bin.entware.net/mipselsf-k3.4/installer/EN_mipsel-installer.tar.gz

# unmount the USB drive
umount /mnt/usb

Install Tailscale on the router

After umount /mnt/usb, it is safe to remove the USB drive and plug it into the router USB port. The drive should appear on the router System Dashboard section USB Drives and printers and in the Applications page under the USB Devices section. Make sure that it shows the correct label set before, in our example, USB128GB. See the documentation for example screenshots.

Go to the OPKG page and select appropriate device in the Drive dropdown. Click the Save button.

Go to the Diagnostics page and check System Log for the "Entware" installed! message and the default ssh login and password.

1
2
Dec 7 11:50:02 ndm Opkg::Manager: /opt/etc/init.d/doinstall: Log on to start an SSH session using login - root, password - keenetic.
Dec 7 11:50:02 ndm Opkg::Manager: /opt/etc/init.d/doinstall: [5/5] "Entware" installed!

Add a section to .ssh/config

1
2
3
4
5
6
7
cat >> ~/.ssh/config <<'EOF'

Host keenetic
  Hostname 192.168.1.1
  User root
  Port 222
EOF

Optionally, put your ssh public keys into /opt/etc/dropbear/authorized_keys for passwordless login.

1
scp -O ~/.ssh/id_ed25519.pub keenetic:/opt/etc/dropbear/authorized_keys

Log in with ssh using the default password (see the System log above):

1
ssh keenetic

Change the default password:

1
passwd

Update the OPKG packages:

1
opkg update

Install Tailscale packages:

1
opkg install iptables tailscale

Bring the Tailscale node up

  • The tailscaled won’t be able to modify the system resolv.conf, so use --accept-dns=false.
  • The system iptables rules are periodically reset by the router software, so we can’t rely on tailscaled to maintain them and need to turn them off: --netfilter-mode=off.
  • We want to be able to ssh into the router through the Tailnet, so use --ssh (it works only through the Tailscale web admin panel).
  • We want to access the subnet connected to the router through the Tailnet, so use --advertise-routes 192.168.1.0/24 (replace with your own subnet address).
  • We want to use the route as an exit node, so use --advertise-exit-node.
1
2
tailscale up --accept-dns=false --netfilter-mode=off --ssh \
  --advertise-routes 192.168.1.0/24 --advertise-exit-node

Configure netfilter rules

Without netfilter rules, the exit node and advertised routes won’t work. We need to set up custom hooks for Keentic to configure netfilter rules. We need to create two files in the hook subdirectory /opt/etc/ndm/netfilter.d (See the documentation).

/opt/etc/ndm/netfilter.d/tailscale-filter.sh to configure the default filter table:

Set the variable ROUTER_TAILSCALE_IP to the IP returned by the tailscale ip -4 command on the router or look up in tailscale status output on another Tailscale host.

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
#!/bin/sh

[ "$type" = "ip6tables" ] && exit 0   # check the protocol type in backward-compatible way
[ "$table" != "filter" ] && exit 0   # check the table name

ROUTER_TAILSCALE_IP=100.X.X.X/32 # Tailscale IP address assigned to this router

# Create chain only once; skip if it already exists
if iptables -w -N ts-forward >/dev/null 2>&1; then
  iptables -w -A ts-forward -i tailscale0 -j MARK --set-xmark 0x40000/0xff0000
  iptables -w -A ts-forward -m mark --mark 0x40000/0xff0000 -j ACCEPT
  iptables -w -A ts-forward -s 100.64.0.0/10 -o tailscale0 -j DROP
  iptables -w -A ts-forward -o tailscale0 -m conntrack ! --ctstate RELATED,ESTABLISHED -j DROP
  iptables -w -A ts-forward -o tailscale0 -j ACCEPT
  iptables -w -I FORWARD 1 -j ts-forward
fi

if iptables -w -N ts-input >/dev/null 2>&1; then
  iptables -w -A ts-input -s $ROUTER_TAILSCALE_IP -i lo -j ACCEPT
  iptables -w -A ts-input -s 100.115.92.0/23 ! -i tailscale0 -j RETURN
  iptables -w -A ts-input -s 100.64.0.0/10 ! -i tailscale0 -j DROP
  iptables -w -A ts-input -i tailscale0 -j ACCEPT
  iptables -w -A ts-input -p udp -m udp --dport 41641 -j ACCEPT
  iptables -w -I INPUT 1 -j ts-input
fi

/opt/etc/ndm/netfilter.d/tailscale-nat.sh to configure nat table:

1
2
3
4
5
6
7
8
9
#!/bin/sh

[ "$type" = "ip6tables" ] && exit 0   # check the protocol type in backward-compatible way
[ "$table" != "nat" ] && exit 0   # check the table name

if iptables -w -t nat -N ts-postrouting >/dev/null 2>&1; then
  iptables -w -t nat -A ts-postrouting -m mark --mark 0x40000/0xff0000 -j MASQUERADE
  iptables -w -t nat -I POSTROUTING 1 -j ts-postrouting
fi

The router software resets iptables rules frequently, and it can do this even while these hooks are running, so it is normal to see iptables errors in the router logs. Verify that the rules are applied properly with ssh keenetic iptables-save |grep ts-.

The rules are created by running tailscale with --netfilter-mode=on and saving the rules with iptables-save.

Comments