Routing a Roku Streaming Stick via Tailscale Exit Node
January 1, 2024
A simple guide on how to route all the traffic from a TV streaming stick or similar device via a Tailscale Exit Node, making it appear as though it's originating from somewhere else.
I've been using Tailscale for over 2 years, and one of the really cool features is Exit Nodes. By choosing a Tailscale Exit Node in your Tailscale client, you can route all traffic from your client via that Exit Node, making it appear as though your client is in that remote location.
But what about when the client you want to route traffic from doesn't support Tailscale - e.g. a TV streaming stick? That's where this guide comes in.
Prerequisites
For this guide, you'll need the following:
- A Tailscale account with one or more Exit Nodes already configured;
- A router capable of configuring the network gateway on a per-device basis - I used a GL-AR750S (aka Slate travel router);
- A way to run a simple Linux machine (virtual or physical) - I used a Debian LXC in Proxmox, but something like a Raspberry Pi would also work;
- A Roku Streaming Stick or other client.
Critically, this method does not requiring installing any software or apps on the client, nor modifying any advanced networking settings on the client. In fact, the Roku Streaming Stick I used only supports simple DHCP network configuration.
Setting Up a Linux Router
To call this device a router is really stretching the term, but fundamentally that's what it's going to do. It's going to accept inbound traffic, NAT it, and route it via the Tailscale exit node.
Configure an LXC
In my case, I created an privileged LXC in Proxmox running Debian 11 (Bullseye) with 1 CPU, 512MB of RAM and an 8GB disk. You'll also need to give it access to /dev/tun
in order to run Tailscale.
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
I think you can also change the ownership of /dev/tun
to allow this to work in an unprivileged container, but I am using a privileged container for simplicity.
You'll want to ensure the machine has a stable IP address. You could set a static IP address, but I opted to set a reserved DHCP lease via my router in Network > LAN.
Install Packages
After you log in, the first thing you'll need to do is install a couple of packages if they're not already available:
apt update
apt install -y curl iptables-persistent sudo
Enable IP Forwarding
Then, enable IP forwarding, which in my case meant running:
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
Install Tailscale
Then download and install Tailscale using the instructions from their website:
curl -fsSL https://tailscale.com/install.sh | sh
Once Tailscale is installed, log in by running tailscale login
and following the instructions to authenticate via the web browser. Once that's working, run the following command to route traffic from this client via an exit node:
tailscale up --exit-node <id_of_your_exit_node> --exit-node-allow-lan-access
Note that the --exit-node-allow-lan-access
flag is very important - without it, you won't be able to access this device from your LAN.
Confirm that it's working by running curl ip.me
and checking that the IP address returned is the external IP address of your exit node.
Configure NAT on iptables
In order to route traffic from other devices on your LAN via this one, you need to configure Network Address Translation (NAT) to map the IP addresses. Using iptables
, this is a simple one-liner:
iptables -t nat -A POSTROUTING -j MASQUERADE
This will accept and map all traffic, so you may want to tighten this rule more, for example restricting it to specific IP addresses with something like:
iptables -t nat -s <internal_ip_cidr> -A POSTROUTING -j MASQUERADE
To ensure this new rule persists across a restart, run:
iptables-save | sudo tee /etc/iptables/rules.v4
At this point, the Linux router configuration is complete.
Configure the Router
If the client you are trying to route via the Exit Node supports static IP configuration, then you can skip this setting and simply set the Gateway to the IP address of the router you configured above.
However, devices like my Roku Streaming Stick don't support static IP configuration, instead loading all the networking configuration via DHCP. So we need to push this configuration to the device.
This step will vary significantly depending on the router that you have, but I'll walk through the steps I took to make it work on my Slate travel router.
Slate Router DHCP Configuration
A fun fact about the Slate travel router is that behind the scenes, it runs on OpenWRT - a very powerful firmware which offers a ton more configuration beyond the Slate's web UI.
However, this particular configuration isn't available even via the OpenWRT UI, so we need to log in to the router via SSH.
ssh root@<ip_address_of_router>
Enter your router password when prompted (assuming you haven't previously configured key-based authentication), then run the following commands:
uci set dhcp.exitnodetag="tag"
uci set dhcp.exitnodetag.dhcp_option="3,<ip_address_of_linux_router>"
uci add dhcp host
uci set dhcp.@host[-1].name="<my_client_name>"
uci set dhcp.@host[-1].mac="<my_client_mac_address>"
uci set dhcp.@host[-1].tag="exitnodetag"
uci commit dhcp
/etc/init.d/dnsmasq restart
This creates a new tag
(you can call it anything, I chose exitnodetag
for clarity) in the DHCP configuration, and sets DHCP option 3 - the gateway IP address - to the IP address of the Linux router we configured before. Next, it creates a new DHCP host entry for your client, and tags it with your new tag.
In essence, when the router sees a client matching the MAC address specified, it will apply the tag configuration specified, including setting the gateway to your newly created Linux router.
If your client is already powered on and connected to the network, restart it to pick up the new networking configuration.
Your client should now have all its traffic routed via the Exit Node as configured on your Linux router.
Block WAN Access
I took things one step further and enabled the Block WAN
setting in the Clients section of my Slate router for my Roku Streaming Stick. This ensures that traffic can never exit via the Slate router directly, and will only ever exit via my Linux router, and hence the Tailscale Exit Node.
Conclusion
Based on my research, this setup seems elusive to many, but is actually deceptively simple. With just a handful of commands running on a lightweight Linux machine, you can teleport traffic from any client around the world thanks to Tailscale.
Next Steps
This is a very simple implementation, but one that unlocks a lot of capability - the ability to geographically teleport a dumb device such as a TV streaming stick to a anywhere in the world that you have a Tailscale Exit Node.
But there are a couple of things you could do to take it further.
Lock It Down
The process above is very simple, and has no authentication, traffic restrictions or otherwise. Unless you trust all the traffic on your network, you likely want to lock things down using iptables
or other mechanisms.
Changing Exit Node
Changing the Exit Node is as simple as running the following command on your Linux router:
tailscale up --exit-node <id_of_your_new_exit_node> --exit-node-allow-lan-access
You can also identify which exit node is currently in use by looking at the JSON output of tailscale status --json
so automating switching the Exit Node wouldn't be too hard. You could, for example, have a toggle switch in Home Assistant that triggers an SSH command behind the scenes.
Multiple Linux Routers
The Linux router is so lightweight, that it would be trivial to spin up several of them, potentially even one per Exit Node. That way all you'd have to do is point a device at whichever router necessary to route its traffic around the world.
Exit Node VLAN
Instead of configuring the gateway for a specific device, you could also create a separate VLAN, and route traffic for all devices on that VLAN via your exit node. This would let you route a large number of devices all at once.
Dedicated WiFi SSID
With an exit node VLAN, it would be a simple next step to create an SSID specifically for that VLAN, and simply connect clients to it when you wish to route their traffic via your Exit Node.
For instance, you could have WiFi networks such as "My WiFi - UK", "My WiFi - USA", ...and so on.
Want to teleport somewhere else? Simply switch WiFi network.