Setting Up a Linux Gateway to Defeat Geoblocking

ยท 812 words ยท 4 minute read

Watching free-to-view television in the UK is a frustrating experience. We have some excellent channels and content, but the options to watch online are fragmented and often advert-strewn. Bizarrely, the best option I’ve found is Zattoo, a Swiss streaming service which does things “right”. There’s a great selection of channels, the interface is simple to navigate, timeshifting is trivial and there’s an excellent PVR function. It is everything I’d hope for in a TV service at a fair price.

But it isn’t available in the UK. The service is geoblocked.

I subscribe to Mullvad’s VPN service. Running this, I can access Zattoo on my laptop or phone. That is fine, but I’d rather watch on the big TV.

My TV runs RokuOS. There’s no Mullvad VPN app for this. I have an Xbox Series S connected to it which could play video, but there’s no VPN app for that, either.

In the past, I’ve used a Raspberry PI 3 running RaspAP to create a wireless access point to connect such devices to a VPN. It “works”, to some extent, but I’ve found the connection to be flaky. As I’ve recently set up my newest Firebat T8 Plus with Proxmox, I decided to use this beautiful little cuboid of joy to provide a gateway to my VPN.

Setting up the container in Proxmox ๐Ÿ”—

My Proxmox configuration is quite vanilla. I have the default network bridge set up to one of my ethernet interfaces. For my container template, I used the debian-12-standard_12.2-1_amd64.tar.zst bundle. I kept most settings at default, whilst allocating 512MB RAM and 4GB disk to the container. That’s probably overkill for this job. I used the default bridge for networking and logged on as root via the console.

Configuring Debian ๐Ÿ”—

Annoyingly, networking is not set up by default on this Debian image. I edited /etc/network/interfaces and added:

auto eth0
iface eth0 inet dhcp

I started the interface with ifup eth0 before pinging google.com to confirm it was working. As this was a success, I proceeded to update the system and install the needed packages:

apt update
apt full-upgrade
apt install wireguard resolvconf iptables

Configuring the VPN ๐Ÿ”—

Mullvad offer WireGuard configurations for their service. I selected a Linux configuration with a Swiss endpoint and copied the generated text. I pasted this into a new file at /etc/wireguard/wg0.conf. I then used the superb AllowedIPs calculator to generate a list of IPs which should be passed through the VPN (using 0.0.0.0/0 in the “Allowed IPs” field and my local subnet and the Mullvad endpoint IP in the “Disallowed IPs” field). I manually edited the wg0.conf file to include these:

[Interface]
# Device: Mullvad Words
PrivateKey = REDACTED
Address = 10.66.170.166/32
DNS = 10.64.0.1

[Peer]
PublicKey = REDACTED
AllowedIPs = 0.0.0.0/2, 64.0.0.0/3, 96.0.0.0/4, 112.0.0.0/5, 120.0.0.0/6, 124.0.0.0/7, 126.0.0.0/8, 128.0.0.0/2, 192.0.0.0/9, 192.128.0.0/11, 192.160.0.0/13, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/11, 193.32.0.0/18, 193.32.64.0/19, 193.32.96.0/20, 193.32.112.0/21, 193.32.120.0/22, 193.32.124.0/23, 193.32.126.0/24, 193.32.127.0/26, 193.32.127.64/30, 193.32.127.68/32, 193.32.127.70/31, 193.32.127.72/29, 193.32.127.80/28, 193.32.127.96/27, 193.32.127.128/25, 193.32.128.0/17, 193.33.0.0/16, 193.34.0.0/15, 193.36.0.0/14, 193.40.0.0/13, 193.48.0.0/12, 193.64.0.0/10, 193.128.0.0/9, 194.0.0.0/7, 196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 224.0.0.0/3
Endpoint = 193.32.127.69:51808

I brought up the WireGuard tunnel with:

systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0

When I ran dig TXT +short o-o.myaddr.l.google.com @ns1.google.com to test my external IP address, I was rewarded with a Swiss address. Result!

Setting up forwarding for the gateway ๐Ÿ”—

Next, i needed to set up IP forwarding so the container would function as a gateway. I edited the file /etc/sysctl.d/99-sysctl.conf and uncommented the line net.ipv4.ip_forward=1. The setting was activated by running sysctl -p.

Finally, I had to add some iptables forwarding magic to the wg0.conf file, to ensure incoming packets would be sent through the VPN, and return packets to established connections would come back to the requestor. The final wg0.conf looks a bit like this:

[Interface]
# Device: Mullvad Words
PrivateKey = REDACTED
Address = 10.66.170.166/32
DNS = 10.64.0.1
PostUp = iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE; iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT; iptables -A FORWARD -i wg0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

[Peer]
PublicKey = REDACTED
AllowedIPs = 0.0.0.0/2, 64.0.0.0/3, 96.0.0.0/4, 112.0.0.0/5, 120.0.0.0/6, 124.0.0.0/7, 126.0.0.0/8, 128.0.0.0/2, 192.0.0.0/9, 192.128.0.0/11, 192.160.0.0/13, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/11, 193.32.0.0/18, 193.32.64.0/19, 193.32.96.0/20, 193.32.112.0/21, 193.32.120.0/22, 193.32.124.0/23, 193.32.126.0/24, 193.32.127.0/26, 193.32.127.64/30, 193.32.127.68/32, 193.32.127.70/31, 193.32.127.72/29, 193.32.127.80/28, 193.32.127.96/27, 193.32.127.128/25, 193.32.128.0/17, 193.33.0.0/16, 193.34.0.0/15, 193.36.0.0/14, 193.40.0.0/13, 193.48.0.0/12, 193.64.0.0/10, 193.128.0.0/9, 194.0.0.0/7, 196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 224.0.0.0/3
Endpoint = 193.32.127.69:51808

Of course, I had to reload the configuration with systemctl restart wg-quick@wg0 for the changes to take effect.

Using the VPN with the Xbox ๐Ÿ”—

Using the VPN involved accessing the “Advanced” network settings on the Xbox, selecting a Static IP and copying all of the settings from the previous dynamic allocation, except for the Gateway address which was set to the IP of the new container.

I then opened the Edge browser, navigated to Zattoo and started streaming. No problems!

A BBC1 game show on the big TV above the Xbox Series S

I’d love to hear comments and suggestions on Bluesky!