DNSSEC, pfSense & Mullvad WireGuard

DNSSEC, pfSense & Mullvad WireGuard

(Why recursive resolution works but validation doesn’t, and how we can’t fix it)


TL;DR

Running Unbound in recursive mode through a Mullvad WireGuard tunnel will resolve names but strip the AD flag because Mullvad blocks outbound TCP 53 (and large UDP 53) to anywhere except its own DNS, even when the dns hijack bit is set to false! Mullvad suggests we swap to openVPN on ports 1400/1401, however they also plan to stop OpenVPN support in 2026.

End result is A. Forwarding mode with DNSSEC. B. Recursive mode without DNSSEC.

Or ditch MullVAD entirely!


What we want

DOH and DOT are great technologies and really any sane person should use them and not go any further, however, what this does is concentrate your DNS enquires into a tunnel at one endpoint. Essentially you give the DNS providor, such as google, your complete browsing history. It’s shieldd from your ISP but, if used widely, concentrates our traffic onto these tech companies who we hope will not do anything nefarious. The tunnel also links your DNS history with you directly no matter how you shield it with further VPN connections.

Another way we could shield our traffic is pipe cleartext DNS entries out of a VPN endpoint such as MullVAD. This anonymises the traffic with all the other requests, tracing any one request back to you would be almost impossible and it would be shielded from your ISP. But what if we want to go one step further and run a fully recursive unbound resolver that communicates via a VPN connection. This would probably not provide anymore anonymity but it would be a super fun project to do.

I already have my MullVAD vpn endpoints set up in pfSense so I set my dns reolver to recursive mode, DNSSEC to hardened and only let it use the VPN gateway. This broke all DNS in my house, making my ever suffering wife slightly upset and left me scratching my head. Here’s the deep dive into whats going on.

I had already read a great guide about turning off DNS hijack in MullVAD tunnels, see references below, so I did this for my setup just in case.

Test set‑up

Component Version / Note
pfSense 2.8.0‑RELEASE
Interface tun_wg1 (Mullvad WG endpoint) - DNS Hijack off
Unbound DNSSEC hardened, edns-buffer-size: 1232

Client on LAN 192.168.1.0/24 uses pfSense as resolver (192.168.1.1).


Symptoms

$ dig +dnssec cloudflare.com @192.168.1.1 | grep flags
;; flags: qr rd ra; QUERY: 1, ANSWER: 3 …    # <- **no  ad  flag**

Yet the same host’s public resolver does validate:

$ dig +dnssec cloudflare.com @8.8.8.8 | grep flags
;; flags: qr rd ra ad; …                      # <- validation works outside WG

Ask a root server for its DNSKEY RRset

# UDP  → truncated
dig . DNSKEY @198.41.0.4 +dnssec +norecurse +time=2 +tries=1 -b ${wgip}
;; Truncated, retrying in TCP mode.
;; Connection to 198.41.0.4#53(198.41.0.4) for . failed: permission denied.

Raw TCP handshake test

nc -vz -s ${wgip} 198.41.0.4 53
nc: connect to 198.41.0.4 port 53 (tcp) failed: Permission denied

Finding: the WG exit drops / RSTs every outbound connection with destination TCP 53 (and fragments > 1232 B), so Unbound cannot fetch the 1.4 KB root DNSKEY it needs to build the trust chain.

Essentially what is happening is that even though smaller UDP packets are allowed through to external servers once we request our root DNSKEY it doesn’t fit, fragments, and is re-tried via TCP. Heres the crucial finding, even though dns hijack is set to off MullVAD in their infinite wisdown still block TCP on port 53 and we never get the key and DNSSEC just never gets off the ground.


Why resolution still works

  • Ordinary A/AAAA look‑ups fit in one small UDP packet (< 300 B) → allowed through.
  • DNSSEC validation must eventually download large DNSKEY/RRSIG sets. The first 512 bytes arrive, but the reply has the TC bit set → Unbound retries over TCP 53 → blocked.
  • Unbound therefore answers without the ad flag and logs:
    failed to prime trust anchor -- could not fetch DNSKEY rrset . DNSKEY IN
    

Solutions

Well, annoyingly, as in the TL-DR, there are not any good solutions. OpenVPN support is being deprecated and MullVAD is targeting peple who don’t know what they are doing, hence they try really really hard not to get DNS leaks. This is fine I suppose, it’s their market and arguably makes the world more private by defualting like this.

I just wish they would provide a way to turn it off for power users, making it only accessible via an API would limit people doing this by accident.

I had wanted to try airVPN so I can forward ports, hosting this blog for instance behind a VPN could be fun. So thats the next project! For now I have reverted to just using MullVAD’s resolvers through my two VPN gateways in forwarding mode.


References & further reading

  1. Mullvad VPS / Port‑filtering FAQ – explains limited DNS on WireGuard exits.
  2. Netgate Forum – “Unbound DNSSEC fails over Mullvad WG” (2024‑2025 threads).
  3. Michael Schnerring, “Use Custom DNS Servers With Mullvad WireGuard” (31 Oct 2021, updated 30 Jul 2023) – describes the hijack_dns API flag.