Mullvad VPN, by default, hijacks DNS queries inside VPN tunnels and reroutes them to their own DNS servers. For most users, this is probably a good thing — your devices that try to ignore system DNS settings are still forced into using a more secure DNS. But, it can break things if you want to use unbound, for example.
To get around this, I’ll be using the Mullvad API directly to generate a certificate which does not do this.
See also
I got this process from Michael Schnerring: https://schnerring.net/blog/use-custom-dns-servers-with-mullvad-and-any-wireguard-client/. Thanks!
To summarize that article somewhat, drop the following in a Bash script and edit the <<INFO HERE>> points:
# Authenticate to Mullvad and store the returned access token
access_token=$( \
curl -X 'POST' 'https://api.mullvad.net/auth/v1/token' \
-H 'accept: application/json' -H 'content-type: application/json' \
-d '{ "account_number": "<<YOUR MULLVAD ACCOUNT NUMBER>>" }' \
| jq -r .access_token)
# Create a new Mullvad Device with DNS hijacking disabled
curl -X POST https://api.mullvad.net/accounts/v1/devices \
-H "Authorization: Bearer $access_token" -H 'content-type: application/json' \
-d '{"pubkey":"<<YOUR WIREGUARD PUBLIC KEY>>","hijack_dns":false}'Using this with OpenWRT
If you want to use a certificate like this for a Whole-Network VPN routing setup, the OpenWRT Wireguard interface can be confusing (or at least was to me).
- In OpenWRT LuCI ⇒ Network ⇒ Interfaces, select Add New Interface…
- Give the interface a name, and select a protocol of Wireguard VPN
- In the interface General Settings, select the following:
- Disable this interface: unchecked
- Bring up on boot: checked
- Click Generate new key pair
- Note the Public Key. This is what goes in the
<<YOUR WIREGUARD PUBLIC KEY>>in the Bash script.
- Note the Public Key. This is what goes in the
- Run the bash script, and note the following from its output:
name: note for future useipv4_address: note for future useipv6_address: note for future use
- Return to the Wireguard interface options in OpenWRT and continue configuring:
- Listen port: unset / random
- IP addresses:
- Add the
ipv4_addressvalue from the output of the API script - Add the
ipv6_addressvalue from the output of the API script
- Add the
- Go to the Advanced Settings tab on the interface and configure the following:
- Force Link: checked
- MTU: 1280
- Use custom DNS servers: IP address of the server with PiHole & Unbound
- Go to the Firewall Settings tab on the interface and select the appropriate firewall zone
- Go to https://mullvad.net/en/servers and find a server you want to use. Note the following:
- IPv4 address
- Public Key
- Go to the Peers tab on the interface, select Add Peer, and configure the following:
- Description: Name of the server from the list (
us-chi-wg-312, for example. If you want to. I think it’s helpful, but it really doesn’t matter) - Public key: the public key of the server you want to use
- Private key: blank
- Allowed IPs:
- Add
0.0.0.0/0 - Add
::0/0
- Add
- Route Allowed IPs: checked
- Endpoint Host: the IPv4 address of the server you want to use
- Endpoint Port:
51820
- Description: Name of the server from the list (
Viola — this tunnel should now be operational and you should be able to use your own DNS servers!
Appears as a leak
The Mullvad connection check will now indicate that you’re leaking DNS servers. This is expected behavior with this setup.
Preset multiple servers
You can repeat steps 8 and 9 multiple times with different servers to store a few presets of servers. This way, if one is slow or offline, you can just go switch right in your router and not have to figure this process out without internet.
Just make sure that all but one are Disabled at any given time!