Skip to main content
RunBook Academy

OPNsenseXXIII · VPN TroubleshootingDNS after tunnel up

VPN DNS after tunnel up — split-horizon DNS, resolver selection, and the surprises

Advanced⏱ ~14 mindrilltcpdumpunbound-controlswanctlwghost

What you'll learn

  • Explain the DNS choices a VPN client has to make after the tunnel comes up
  • Configure split-horizon DNS so internal names resolve through the tunnel
  • Recognise when the LAN DNS resolver setting is wrong for the tunnel
  • Add Unbound access list entries for the remote subnets so DNS works through the tunnel
  • Identify the DNS failures that produce name resolves to the public address issues

Prerequisites

Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-15

Not yet marked complete on this device.

A VPN tunnel can be perfectly negotiated, routing can be correct, MTU can be configured just right, and the user still cannot resolve internal hostnames. DNS — the layer above the network plumbing — has its own rules that interact with the tunnel in surprising ways. This lesson covers what DNS behaviour the operator should expect when a tunnel is up, the split-horizon configurations that make cross-tunnel DNS work, the Unbound access-list issue that produces silent failures, and the diagnostic drill pattern that distinguishes “DNS is broken” from “DNS is fine, the application is wrong”.

The DNS behaviour after tunnel up

When a client behind an OPNsense firewall joins a VPN tunnel, the DNS behaviour depends on what DNS server the client is configured to use:

  • Clients using the firewall’s resolver (Unbound). All DNS queries go to the firewall, which then resolves them using Unbound’s recursion and forwarding logic. Internal names (those that should resolve to RFC 1918 addresses) resolve through the DNS infrastructure; public names resolve to public addresses.
  • Clients using a third-party DNS server (Google, Cloudflare, ISP DNS). All DNS queries go directly to the public Internet. Internal names fail to resolve because the public DNS does not know about them.

For an OPNsense firewall with a working tunnel, the typical configuration is the first — clients use Unbound, and Unbound knows how to resolve both internal and external names. The “VPN DNS surprises” come from configurations that don’t match this pattern.

Split-horizon DNS through the tunnel

The most common DNS-on-VPN pattern:

  • The remote site has its own DNS server (e.g., 10.1.0.10) that resolves names in the remote site (e.g., file-server.partner.example10.1.0.20).
  • The local firewall needs to forward queries for the partner’s domain to the remote DNS server, through the tunnel.
  • Public queries (e.g., www.example.com) continue to recurse normally.

The OPNsense configuration is under Services → Unbound DNS → Query Forwarding (or, on older configurations, the equivalent under DNS Forwarder). Each entry forwards queries for a specific domain to a specific upstream DNS resolver:

Forwarding entryForwards queries forTo DNS server
Partner’s domainpartner.example10.1.0.10 (reachable through the tunnel)
Internal infrastructureinternal.example10.0.0.53 (local DNS)

The forwarding works because Unbound sends the query to 10.1.0.10 through the tunnel (because that subnet is reachable through the VTI), the partner’s DNS server responds, and Unbound caches the answer. Subsequent queries from any LAN client get the cached answer without re-traversing the tunnel.

A common OPNsense misconfiguration: the forwarding entry is correct but the remote DNS server (10.1.0.10) is unreachable through the tunnel. The result is a forwarding entry that produces SERVFAIL or timeouts for every query. The fix is at the routing layer (Layer 3) — the operator confirms the tunnel route reaches 10.1.0.0/24, not at the DNS layer.

Unbound access lists for remote subnets

The single most common silent failure for DNS across a VPN tunnel is the Unbound access list. By default, Unbound accepts queries only from the firewall’s local subnets. A client on the remote side of a tunnel that queries the firewall’s Unbound gets a REFUSED:

Read-only / SafeREFUSED from Unbound
$ drill @10.0.0.1 partner.example
;; ->>HEADER<<- opcode: QUERY, rcode: REFUSED, id: 42387
;; flags: qr rd ; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; partner.example. IN A

Illustrative output

The fix: under Services → Unbound DNS → Access Lists, add an entry that permits 10.1.0.0/24 (or whatever the remote subnet is) to query Unbound. The access list default is “local subnets only”; tunnels are not local subnets by default.

When the LAN clients have a hardcoded DNS server

A common production misconfiguration: the LAN’s DHCP scope delivers Google DNS (8.8.8.8 and 8.8.4.4) instead of the firewall’s resolver (10.0.0.1 or the LAN’s interface IP). The result:

  • DNS queries from LAN clients go directly to Google — they bypass Unbound entirely.
  • Internal names fail to resolve (Google doesn’t know internal.example).
  • Forwarding entries on Unbound are not used because the clients never query Unbound.

The discipline: ensure the DHCP scope delivers the firewall’s resolver as the primary DNS. For sites that explicitly require a third-party DNS as a backup, use the third-party DNS only as a secondary and only for clients that need it (typically laptops that travel outside the corporate network). The router-supplied DNS as primary, with the third-party DNS as secondary, is the right balance for most estates.

The “name resolves to the wrong address” failure

The confusing failure mode: host file-server returns a public IP (the address a public DNS server knows about for that hostname), not the internal IP. The tunnel is up; traffic to the public IP does not cross the tunnel.

The cause: Unbound’s recursion returned a public-IP answer. The internal authoritative DNS is not being queried.

The diagnosis:

  1. drill @10.0.0.1 file-server.partner.example from a known-allowed source. If the answer is the public IP, Unbound is querying the public DNS, not the partner’s authoritative server.

  2. Check the forwarding entries. The partner.example forwarding entry should be present under Services → Unbound DNS → Query Forwarding. If it is missing, the entry has to be added.

  3. Check the forwarding entry’s destination. If the entry forwards partner.example to 8.8.8.8, the public address is what comes back. The fix: change the entry to forward to the partner’s authoritative DNS server (reachable through the tunnel).

  4. Check the access list. If the partner DNS server is reachable through the tunnel but Unbound cannot reach it (ACL restricts outbound from the firewall), the forwarding entry times out.

Resolver-on-tunnel: when the tunnel is the only path

For WireGuard and IPsec remote-access tunnels, the client is the only device that crosses the tunnel — the firewall does not pass traffic through the tunnel for the client (the tunnel runs on the client). The DNS configuration is different:

  • The client queries its DNS server (often the remote site’s DNS, configured in the WireGuard or IPsec client).
  • Sometimes the client uses the firewall’s resolver as a forwarder, with a “full-tunnel” configuration.

The failure mode here is the client’s DNS routing. A WireGuard AllowedIPs that includes a public DNS server (8.8.8.8) captures all DNS queries from the client; queries for internal.example never reach the partner DNS server. The fix is either an AllowedIPs that includes only the necessary IPs, or a DNS routing rule on the client that sends queries for internal.example to the partner DNS server.

The DNS diagnostic workflow

For “tunnel is up but names don’t resolve correctly”:

1. Confirm the tunnel is up
   → swanctl --list-sas ESTABLISHED, wg show recent handshake

2. Confirm routing
   → route -n get <remote-dns-ip> egress interface is the tunnel

3. Test from the firewall itself
   → drill @10.0.0.1 file-server.partner.example
   → If public IP: forwarding entry is missing or wrong
   → If internal IP: the firewall can resolve correctly

4. Test from a LAN client
   → drill @10.0.0.1 file-server.partner.example
   → If public IP: client DNS bypasses Unbound (DHCP or hardcoded)
   → If REFUSED: Unbound access list denies the source
   → If internal IP: DNS is fine; the issue is elsewhere

5. Check the Unbound access list
   → Services → Unbound DNS → Access Lists
   → Add the LAN / remote subnets permitted to query

6. Check the forwarding entries
   → Services → Unbound DNS → Query Forwarding
   → Add entries for partner.example → 10.1.0.10 (or the right DNS server)

The split-horizon DNS rules

A few summary rules:

  1. LAN clients should use Unbound as primary DNS. Delivered through DHCP or configured as static. The firewall’s resolver is the entry point for every DNS query.
  2. Internal domains are forwarded to the partner’s DNS server when the partner DNS is reachable through the tunnel.
  3. Internal domains that the firewall can already resolve (local overrides under DNS Host Overrides) do not need forwarding entries — Unbound returns the override directly.
  4. Public domains use recursion or forwarding to public DNS. No partner-specific forwarding entry needed.
  5. The Unbound access list must permit the remote subnet. Without it, queries from the remote side get REFUSED.

Summary

  • The firewall’s resolver (Unbound) is the DNS entry point for LAN clients; forwarders handle internal partner domains.
  • The Unbound access list defaults to “local subnets only”; tunnels are not local, and queries from the remote side get REFUSED unless the operator adds the remote subnets.
  • Hardcoded client DNS (8.8.8.8) bypasses Unbound entirely; the cure is DHCP-managed DNS delivery.
  • The forwarding entry for a partner domain is what makes internal names resolve through the tunnel; missing or wrong forwarding entries produce the “name resolves to public address” failure.
  • The diagnostic starts at the firewall itself: drill @firewall shows whether the firewall can resolve correctly; client-side tests show whether the client is reaching the firewall.

Knowledge check · 4 questions

  1. Q1. A tunnel is up. From a LAN host, drill @10.0.0.1 file-server.partner.example returns the public IP, not the internal IP. drill from the firewall itself returns the internal IP. What is the cause?

  2. Q2. By default, Unbound on OPNsense refuses DNS queries from sources outside the firewall local subnets; queries from a remote subnet across an IPsec tunnel will get REFUSED.

  3. Q3. Which of the following are valid DNS configurations for clients behind an OPNsense firewall with a working partner VPN tunnel? Select all that apply.

  4. Q4. A new IPsec tunnel is up. drill from a remote-side host (10.1.0.50) to the firewall (10.0.0.1) returns REFUSED for every query. drill from the LAN side returns answers. What is the fix?

Passing score: 75%. Answers are checked in this browser.