Using Mullvad wireguard VPN + Pi-hole with Unbound

Hi everyone,

Apologies in advance for any silly questions - I'm about as beginner as you can get.

My current set up is as follows:

  • LAN: Client (macbook, etc) -> RPi4 (running the RPi4 community build of OpenWrt) -> different RPi4 (Pi-hole -> Unbound as upstream DNS server) -> WAN
  • WLAN: Client (iPhones, etc) -> TP-Link AX-1500 (WAP) -> RPi4 (OpenWrt) -> different RPi4 (Pi-hole -> Unbound) -> WAN

What I would like to accomplish is something like this:

  • LAN: Client -> RPi4 (OpenWrt) -> RPi4 (Pi-hole -> Unbound) -> RPi4 (Mullvad with Wireguard) -> WAN
  • WLAN: Client -> TP-Link AX-1500 -> RPi4 (OpenWrt) -> RPi4 (Pi-hole -> Unbound) -> RPi4 (Mullvad with Wireguard) -> WAN

In essence, I'd like all of my local traffic to be routed through Mullvad, but before entering the tunnel, be routed through a separate RPi4 with Pi-hole and Unbound as the upstream DNS server.

So far, I've managed to get to a point where some of my traffic gets properly routed Pi-hole + unbound first (DHCP advanced setting "6,pi-holeip"), then through Mullvad, but other sites never properly resolve (this is the problem I'm facing):

  • When I look at the query log on Pi-hole, a number of requests end up with a reply of "SERVFAIL" or "NXDOMAIN" with a status saying "Retried", or when there is a status saying "OK (forwarded to localhost - unbound)", the reply is "N/A." This results in me not being able to access a number of websites.

Some potential problem areas I considered were:

  • Firewall zones (here's my current config):
    Zone -> Forwardings
    lan -> WGZ0: input (accept), output (accept), forward (accept)
    wan -> REJECT: input(reject), output (accept), forward (reject)
    WGZ0 -> lan: input (reject), output (accept), forward (reject)

  • WG zone config:
    WGZ0:

    • masquerading on
    • MSS clamping on
    • allow forward to lan
    • allow forward from lan
  • Mullvad WG interface config (configured mostly according to Mullvad documentation with the primary exceptions being not using their DNS server):

General Settings:

  • Listen Port: 51280
  • IP Addresses: IP from Mullvad
  • No Host Routes (unchecked)

Advanced Settings:

  • Use built-in IPv6-management (checked)
  • Force Link (checked)
  • Metric = 0
  • MTU = 1420

Peers:

  • Public Key: key from Mullvad servers
  • Allowed IPs: 0.0.0.0/0
  • Route allowed IPs (checked)
  • Endpoint Host (IP of Mullvad server)
  • Endpoint Port (Port from Mullvad server)
  • Persistent Keep Alive = 25

I've tried playing around with the MTU values and endpoint port to no avail.

Unfortunately, I'm a bit stumped as to where to go from here. Some sites continue to load properly through Pi-hole/unbound -> Mullvad while others never seem to make it out. It may also be worth noting that when there is no WG interface configured on OpenWrt, all traffic is properly resolved by Pi-hole and Unbound.

Please let me know if you need any further explanation of the problem (OpenWrt only allows me to post one picture in my post for some reason). Thanks!

1 Like

I am having the same exact issue on my end and have been unable to find any other posts regarding this sort of setup, so I'm glad to see its not just me.

My setup is similar, but differs slightly as follows:

  • LAN: Client -> RPi4 (Pi-hole + Unbound) -> GL-B1300 (OpenWrt w/ Mullvad VPN) -> WAN
  • WLAN: Client -> RPi4 (Pi-hole + Unbound) -> GL-B1300 (OpenWrt w/ Mullvad VPN) -> WAN

Instead of having another RPi running a WireGuard client and funneling my network traffic through it, I've installed a Mullvad VPN profile on my GL-B1300 router itself. I also have changed the DNS address within the VPN profile to the IP address of my RPi, although it doesn't seem to be making any difference.

It is worth noting that I did not follow the installation instructions provided by Mullvad for setting up a WireGuard client running on a router as my router comes preinstalled with a custom version of OpenWrt that has native support for Mullvad.

Like your setup, I also have my router serving my client devices with the IP address of my RPi as their DNS by setting DHCP-Options = 6, [RPi IP address]. This way my devices communicate directly with the RPi for DNS instead of forwarding requests first through the router and then to the RPi.

When I have Unbound set as my upstream DNS provider within the Pi-hole admin settings, visiting almost any page results in status = retried and reply = SERVFAIL, N/A, or occasionally NXDOMAIN.

When I change the upstream DNS provider to Cloudflare, this issue appears to more or less go away entirely, however I appear to begin using the DNS provided by Mullvad rather than Cloudflare despite having changed it to the IP address of my RPi in the VPN profile.

I have a fairly simplistic understanding of networking, so I'm not too sure how to continue troubleshooting at this point. Will definitely be checking back here to in the hopes that we can both get our setups up and running correctly.

I just got an email back from the Mullvad team stating that the use of a custom DNS with their VPN service is currently only possible with the latest beta of their app. Your DNS will be hijacked when connecting through any other WireGuard client.

So, it seems like this setup is currently not possible.

1 Like

Sounds like your problem is basically the same as mine except for me, it seems like I can get about 50% of my pages to properly resolve. It’s a shame Mullvad still won’t let you use custom upstream dns as I like their approach to privacy.

Before I mark your answer as the solution, here’s an interesting page I came across that may be worth giving a shot - it’s nothing to do with DNS hijacking, but rather a broader “Unbound behind VPN causing SERVFAIL”. The OP of that solves their issue by configuring some settings in Unbound.
https://forum.openwrt.org/t/vpn-with-unbound-recursive-resolution-proper-settings/56398

I’ll also give this a shot and report back with any results.

Recursive DNS doesn't appear to work well with DNS hijacking.
In addition, DNSSEC is quite tricky, so it's best to disable while troubleshooting.

@npburns224
Great news, but the solution may require some more tinkering on your end. I believe configuring your own Wireguard interface may be different than using the custom version with Mullvad. ie: Mullvad doesn't seem to be hijacking my DNS when I use the WG interface I configured. I'm not familiar with the custom version of OpenWrt on your router, but I'd assume this is probably still possible on your set up? Anyways, here goes:

I ended up 99% successfully getting set up as intended with some tweaking to Unbound (in /etc/unbound/unbound.conf.d/pi-hole.conf) - mostly following the suggestions from the link above, and a few from experimentation with DNSSEC according to @vgaetera:

  • edns-buffer-size: 512
  • harden-glue: no
  • harden-dnssec-stripped: no (I believe this one should still work if yes, but I'm still messing around with it)
  • qname-minimisation: no
  • disable-dnssec-lame-check: yes

A quick "sudo service unbound restart" and that was all I needed to get my setup going. It's should be noted one thing I noticed was that right after restarting/rebooting Pi-hole/Unbound, sites with particularly long URLs still hit with SERVFAIL, but after sitting around for a few minutes (maybe 30 min or so), those sites resolved properly without me needing to change any settings. Someone who understands this stuff a bit better may be able to explain why that is. In addition to this, one small thing I've noticed is that a very slim number of random sites throw out errors nonetheless. For example:

If anyone has any advice as to how these nitty gritty issues may be resolved, I'm all ears. That being said, the vast majority (pretty much everything else I've found so far) runs without a hitch.


In case you need to configure your interface again without using the native app on your router, here are some of the notable omissions/changes I made from Mullvad's guide:

  • skipped initial set up (I was not exactly on a clean install because I used the RPi4 community build that had pre-installed "luci-proto-wireguard." I started at the key gen step.
  • endpoint port: instead of the recommended 51820, I used the multi hop port found in the dropdown menu of my server of interest.
  • in the firewall zones, I have masquerading and mss clamping on for my wan network as well (not sure if this is needed or not)
  • skipped over DHCP and DNS settings according to guide -> we want our traffic to be routed to the pi-hole/unbound via DHCP advanced option 6
  • skipped over DNS on LAN interface section
  • didn't add a watchdog as that's not necessary for me

And that's it! All other steps were followed according to Mullvad's guide and it's working pretty much as expected. Hope this helps you get your setup going, and definitely feel free to reach out if any of this was unclear!

1 Like

Thanks for updating me with your solution!

For my setup, all I've done is change the DNS address within the WireGuard profile to the IP of my RPi and added a couple lines in the unbound.conf.d file per your post:

  • qname-minimisation: no
  • disable-dnssec-lame-check: yes

I'm more poking around at this point than intelligently tinkering with my settings, but most sites seem to be loading now and Pi-hole appears to be filtering my traffic.

Visiting a site such as this to check for DNS leaks shows the address of the Mullvad server I'm connecting to, which I guess would indicate that my DNS is still being hijacked. Changing the upstream DNS setting in the Pi-hole admin console to Cloudflare and re-running the test yields the same IP of the Mullvad server, which further confirms this.

Just for fun, I reverted my unbound.conf.d file back to its original state and installed the latest beta of the Mullvad desktop app which allows for the entry of a custom DNS. When I point at my RPi for DNS and re-run the leak test, my public IP is returned and everything works as expected.

Whatever slight differences there are between the custom firmware my router is running and the official release of OpenWrt prevent me from following the Mullvad OpenWrt setup guide as some of the options I need simply don't exist, so perhaps flashing it to the official firmware might be a way to go.

I guess at a minimum I'm able to use my Pi-hole with the Mullvad servers, but for now local DNS resolution might need some additional tinkering.

That's awesome! Poking around was how I managed to get most of these things to work :slight_smile:

In case it helps, when I use the site you linked to check for DNS leaks, I also see the address of the Mullvad server I'm connecting to. If I'm not mistaken, I think this is actually a success! We want the DNS resolution to happen locally (less DNS info is passed along to Mullvad), but each of the individual and eventually, the final queries Unbound makes to the root servers should still find their way through the tunnel. I'm not sure if this is the best way to detect it, but one quick indicator of Unbound resolving requests can be seen in the Pi-hole query log - if requests are forwarded to localhost#port, Unbound is working correctly, just behind the VPN interface.

With regard to the custom DNS using their beta app, I believe if your public IP is returned you might be exposing your requests to your ISP (we may have different use-cases, but this was something I was trying to avoid).

Wish I could provide some more authoritative help but alas, I'm also just a beginner. Feel free to reach out if you think of anything else I might be able to help with!

Oh right! I believe your thinking is correct.

I'm now just curious how those changes to the Unbound config affect its performance/security, if at all. I suppose I'll now be moving on to investigating the ins and outs of all that lol

Haha the investigation is never ending. As far as I can tell, the settings to make Unbound work are not optimal (security-wise) as DNSSEC seems like something we want. That said, I'm really not sure if this results in any significant security risk. Definitely curious to hear what you find out!