OPNsenseXIII · Port Forwarding and NAT ReflectionNAT reflection
Split DNS as an alternative to NAT reflection — Unbound host overrides
What you'll learn
- Configure split-horizon DNS using Unbound host overrides
- Recognise why split DNS is preferred over hairpin NAT
- Identify the operational failure modes of split-horizon DNS
- Plan a split DNS rollout for a port-forwarded service
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-14
Split DNS returns different answers for the same hostname depending on whether the requester is internal or external. For an internal client, the resolver returns the private IP of the service. For an external client, the resolver returns the public IP. The internal client reaches the service directly via the LAN; the external client reaches it via the WAN and the port forward.
OPNsense ships with Unbound, a validating recursive resolver that supports split-horizon DNS through host overrides and DNS overrides. This lesson covers the configuration, the operational failure modes, and the rollout patterns for a production split DNS deployment.
How Unbound host overrides work
A host override is a local A (or AAAA) record that Unbound
returns for a specific hostname, overriding the public DNS
response. When an internal client asks Unbound for
service.example.com, Unbound returns the local IP from the
override — without ever querying the public DNS.
The configuration under Services → Unbound DNS → Overrides → Add:
- Host: service (or full hostname)
- Domain: example.com
- Type: A
- Value: 192.0.2.10
- Description: Internal override for service.example.com
The override applies to all clients that use OPNsense’s Unbound as their DNS resolver. External DNS queries (from clients using a different resolver) are unaffected.
$ drill service.example.com;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: ...
;; flags: qr rd ra ad ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; service.example.com. IN A
;; ANSWER SECTION:
service.example.com. 3600 IN A 192.0.2.10
;; Query time: 0 msec
;; SERVER: 192.0.2.1#53(192.0.2.1)Illustrative output
The configuration walk-through
The setup: a service at service.example.com is exposed via
port forward on the firewall’s WAN IP 198.51.100.1. The internal
server is 192.0.2.10. The internal DNS resolver is Unbound on
OPNsense (192.0.2.1).
Step 1: enable Unbound if not already enabled. Under
Services → Unbound DNS → General, set “Enable” to on.
Step 2: configure the host override. Under
Services → Unbound DNS → Overrides → Add:
- Host: service
- Domain: example.com
- Type: A
- Value: 192.0.2.10
- Description: Internal override — split-horizon DNS
Step 3: apply the change. Unbound reloads its configuration.
Step 4: from an internal client, query Unbound for
service.example.com. The response is 192.0.2.10.
Step 5: from an external host (or using a public resolver like 8.8.8.8), query the same hostname. The response is 198.51.100.1.
Step 6: verify end-to-end. From the internal client,
curl https://service.example.com reaches the internal server
directly. The firewall is not in the path.
Why split DNS is preferred over NAT reflection
The case for split DNS over NAT reflection is strong in most production deployments:
- Performance. LAN-direct traffic is faster than hairpin. The packet does not leave the LAN.
- Security. The internal client reaches the service with its real internal IP. The service can apply internal access controls, log internal clients correctly, and differentiate internal from external requests.
- Simplicity. No NAT reflection ruleset to manage. The firewall’s NAT configuration stays focused on the actual NAT rules; the DNS configuration handles the address translation at the resolver layer.
- WAN independence. Internal access does not require the WAN link. If the WAN goes down, internal clients still reach the service.
- Audit clarity. When troubleshooting, the operator can look at DNS to determine what IP the client is using, rather than reasoning about hairpin paths and reflection rules.
The TTL problem
A subtle operational issue with split DNS: TTL caching.
When a client resolves service.example.com externally (e.g.
while on a VPN or before the override is configured), the
client’s resolver caches the public IP for the duration of the
record’s TTL. If the TTL is 3600 seconds and the client
re-resolves within that hour, the client uses the cached public
IP, not the new internal IP.
The symptom: a user reports “the service is slow from inside the office” or “the service is not reachable from my laptop”. The diagnosis: the user’s resolver cached the public IP. The fix: flush the resolver cache.
The mitigation: configure short TTLs on the public DNS record for split DNS’d hostnames. A TTL of 60-300 seconds is typical. The client re-resolves frequently and picks up the internal IP within minutes of being on the LAN.
Operational failure modes
Five failure modes occur in production split DNS deployments:
-
The override is wrong. The host override points to an outdated internal IP. The internal client resolves to the wrong host. Symptom: connection refused or timeout. Fix: update the override.
-
The client does not use the firewall as DNS. A laptop configured with a public DNS bypasses Unbound. The override is not in effect. Symptom: the client resolves to the public IP and uses hairpin (if NAT reflection is enabled) or fails (if not). Fix: enforce the firewall as DNS via DHCP and firewall rules.
-
The override is missing. A new service is exposed via port forward but the host override is not added. Internal clients resolve to the public IP. Symptom: works externally, slow or failing internally. Fix: add the override.
-
The internal IP changes. A server is migrated; its IP changes. The override is not updated. Symptom: timeouts or connection refused. Fix: update the override, audit other overrides that may reference the same IP.
-
The override conflicts with a public DNS record. The public DNS for
service.example.comreturns a CNAME toservice.cdn.example.net, which returns the public IP. The host override on Unbound returns the internal IP directly. Both answers are valid; the override wins for clients using Unbound. But if the public DNS has additional records (TXT for SPF, MX for mail), the override loses them.
The fifth failure mode is the most subtle. Host overrides in Unbound are local A/AAAA records; they do not preserve other record types from the public DNS. If the hostname has TXT, MX, or other records that the client needs, the override loses them.
$ unbound-control list_local_dataservice.example.com. 3600 IN A 192.0.2.10
internal-portal.example.com. 3600 IN A 192.0.2.20
gitlab.example.com. 3600 IN A 192.0.2.30Illustrative output
Rollout patterns
Three rollout patterns cover the common cases.
New service
The service is new. The operator configures the public DNS
record for service.example.com to return the public IP, and
configures the Unbound host override for internal clients. The
firewall’s port forward exposes the service to external clients.
Internal clients resolve via the override.
Existing service migration
The service was previously accessible only by internal IP. The operator wants to expose it to the Internet and have internal clients continue to use the internal IP. The rollout:
- Configure the public DNS record to return the public IP.
- Configure the port forward.
- Configure the Unbound host override.
- Communicate to users: external access uses the public IP; internal access continues to use the internal IP.
- Monitor for resolution issues.
Legacy service with hard-coded public IP
The service is already exposed via public IP. Internal clients hard-code the public IP. The operator wants to migrate to split DNS. The rollout:
- Configure the Unbound host override.
- Update client configurations to use the hostname instead of the hard-coded IP.
- For clients that cannot be updated (legacy, embedded), consider NAT reflection as a transitional mechanism.
- Phase out NAT reflection once all clients use DNS.
Verifying split DNS end-to-end
The verification sequence after configuring split DNS:
- Internal query. From a LAN host, query Unbound for the
hostname. Confirm the answer is the internal IP.
drill service.example.comshows 192.0.2.10. - External query. From a host using a public DNS, query
the same hostname. Confirm the answer is the public IP.
drill @8.8.8.8 service.example.comshows 198.51.100.1. - Service reachability. From the LAN host, connect to the service using the hostname. Confirm the connection works.
- Internal IP visibility. From the server, log the source IP of the connection. Confirm it is the LAN client’s IP, not the firewall’s WAN IP.
If any step fails, the override is missing, the client is using the wrong DNS resolver, or the configuration is incomplete.
Summary
- Split DNS returns the private IP to internal clients and the public IP to external clients. The firewall’s Unbound resolver supports this via host overrides.
- Configure the override under Services → Unbound DNS → Overrides; the override applies to clients using the firewall as their DNS resolver.
- Split DNS is preferred over NAT reflection for performance, security, simplicity, and WAN independence.
- TTL caching can cause clients to use stale cached answers; short TTLs mitigate this.
- The override does not preserve non-A records (TXT, MX). If the hostname has additional record types, the override loses them.
Knowledge check · 4 questions
Q1. An internal client resolves `service.example.com` via the firewall's Unbound. The host override is configured with value 192.0.2.10. The client's DNS is configured to use 8.8.8.8 instead of the firewall. What answer does the client get?
Q2. A Unbound host override for `service.example.com` to 192.0.2.10 (A record) preserves the MX and TXT records from the public DNS for the same hostname.
Q3. Which of the following are valid operational failure modes of split DNS deployments? Select all that apply.
Q4. You want internal clients to reach `service.example.com` via the internal IP. Split DNS is configured with an Unbound override. A user reports that the service is slow from their laptop. Diagnosis shows the user's DNS is 8.8.8.8 (not the firewall). What is the fix?
Passing score: 75%. Answers are checked in this browser.