Blog

EdgeOS setup with Mullvad VPN

January 10, 2022

Mullvad truly cares about the security and privacy of its users, and they have made all their VPN clients available as open source, so they are definitely my VPN provider of choice. Other than a unique 16-digit account number created during sign-up, no identifying information is required to use them.

Previously I have only used Mullvad on my laptop and mobile phone, but I wanted to extend the usage to most of my devices, including IoT devices that don't have a VPN client built-in. This protects me better against data profiling and allows the devices to bypass geoblocking at the same time.

I use a Ubiquiti EdgeRouter Lite on my home network, which has two LAN ports. I configured one of the LAN ports (eth2) to forward all the traffic through a VPN tunnel to Mullvad, while the other LAN port (eth1) continues to route the traffic normally. Here is how I configured this:

1. Download a VPN configuration file from Mullvad

Unfortunately, Wireguard support is not included in EdgeOS, so I had to download a configuration file for OpenVPN instead. In the example below, I make use of the ca-mtr (Montreal, Canada) profile.

In the configuration file provided by Mullvad, the client will pull routes, including a default route, which causes all traffic to go through the VPN interface. Normally you would want this, but as I only wanted to redirect the traffic from one of the LAN interfaces, I had to make a small change to the OpenVPN configuration file. We also don't want resolv.conf to be updated with Mullvad's DNS server because eth1 and the router itself should continue to use the ISP's DNS server (instead, Mullvad's DNS server will be configured as a DHCP option on eth2 in the next step).

mullvad_ca_mtr.conf should look like this:

client
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
verb 3
remote-cert-tls server
route-nopull
ping 10
ping-restart 60
sndbuf 524288
rcvbuf 524288
cipher AES-256-CBC
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA
proto udp
auth-user-pass mullvad_userpass.txt
ca mullvad_ca.crt
tun-ipv6
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
fast-io
remote-random
remote ca-mtr-104.mullvad.net 1300
remote ca-mtr-102.mullvad.net 1300
remote ca-mtr-107.mullvad.net 1300
remote ca-mtr-101.mullvad.net 1300
remote ca-mtr-103.mullvad.net 1300
remote ca-mtr-106.mullvad.net 1300
remote ca-mtr-105.mullvad.net 1300
remote ca-mtr-108.mullvad.net 1300
2. Setup OpenVPN interface and configure DHCP option

First, I created a directory under /config/user-data. This is the recommended place for configuration files, as files stored under that directory won't get overwritten by EdgeOS updates. I used /config/user-data/openvpn.

I uploaded mullvad_ca.crt, mullvad_ca_mtr.conf, and mullvad_userpass.txt using scp to this folder and ran the following commands to setup the OpenVPN connection:

configure
set interfaces openvpn vtun0 config-file /config/user-data/openvpn/mullvad_ca_mtr.conf
set interfaces openvpn vtun0 description 'Mullvad VPN tunnel'

Mullvad's public DNS server is 193.138.219.228. And while configuring it manually is not the prettiest thing, it looks like it has remained unchanged since 2019. Down the road, I could probably create an up/down script that will automatically set the DHCP DNS server option, but for now setting it manually should do:

delete service dhcp-server shared-network-name LAN2 subnet 192.168.20.0/24 dns-server 192.168.20.1
set service dhcp-server shared-network-name LAN2 subnet 192.168.20.0/24 dns-server 193.138.219.228
commit
save
3. Configure NAT and firewall modify rules

At this point we should have a working OpenVPN connection, but because of the no route-pull option which was added to the configuration in the first step, no traffic will go through the VPN tunnel just yet.

To make things work, I created a NAT rule to masquerade the address range of eth2 (192.168.20.0/24) out vtun0:

configure
set service nat rule 1000 description 'Mullvad VPN clients'
set service nat rule 1000 outbound-interface vtun0
set service nat rule 1000 source address 192.168.20.0/24
set service nat rule 1000 type masquerade

Then I created another routing table which will be used by the eth2 interface to exit through vtun0 instead of eth0:

set protocols static table 1 interface-route 0.0.0.0/0 next-hop-interface vtun0

Next, I created a modify firewall rule which makes all traffic from 192.168.20.0/24 use table 1 instead of the default table 0:

set firewall modify SOURCE_ROUTE rule 10 description 'Match traffic from 192.168.20.0/24 and use table 1'
set firewall modify SOURCE_ROUTE rule 10 source address 192.168.20.0/24
set firewall modify SOURCE_ROUTE rule 10 modify table 1

Finally, I attached the modify rule to the eth2 interface.

set interfaces ethernet eth2 firewall in modify SOURCE_ROUTE
commit
save
***
VoilĂ ! We should now have a working setup where all traffic from eth2 go out via vtun0, while the remaining traffic will go out via eth0. To make sure everything is working, you can use Mullvad's "Check for leaks" tool on a device connected on eth2.