Troubleshooting Unbound and NSD Integration: SERVFAIL When Resolving Local LAN DNS Queries


2 views

When setting up a local DNS infrastructure with Unbound as the recursive resolver and NSD as the authoritative server for local domains, I encountered a puzzling situation where direct queries to NSD worked perfectly, but queries through Unbound resulted in SERVFAIL responses.

// Working direct query to NSD
dig @127.0.0.1 data2.datanet.home -p 53530

// Failing query through Unbound
dig @127.0.0.1 data2.datanet.home

The Unbound logs revealed a critical clue:

Jun 15 06:12:39 pizza.yoderdev.com unbound[1947]: [1947:0] debug: skip addr on the donotquery list ip4 127.0.0.1 port 53530

This indicates that Unbound is refusing to query its own NSD instance due to the address being on its internal "donotquery" list.

The current Unbound configuration shows:

server:
  interface: 127.0.0.1
  interface: 192.168.1.50
  # ... other settings ...

stub-zone:
  name: "datanet.home"
  stub-addr: 127.0.0.1@53530

Unbound has a security feature that prevents queries to local addresses (including 127.0.0.1) by default. This is controlled by the do-not-query-localhost option, which defaults to "yes". When Unbound tries to use the stub zone configuration to query NSD running on the same machine, this security feature blocks the query.

There are two approaches to resolve this:

// Option 1: Disable the localhost query restriction
server:
  do-not-query-localhost: no

// Option 2: Use a different IP address for NSD
// In nsd.conf:
ip-address: 192.168.1.50@53530
// Then update unbound stub-zone:
stub-addr: 192.168.1.50@53530

Option 2 is generally preferred as it maintains better security while still allowing the integration to work.

After making these changes, verify with:

# Check Unbound configuration
unbound-checkconf

# Restart services
systemctl restart nsd unbound

# Test resolution
dig +short @127.0.0.1 data2.datanet.home

If you still encounter problems, consider these additional checks:

  • Firewall rules allowing traffic between Unbound and NSD
  • SELinux context if running on RHEL/CentOS
  • Proper zone configuration in NSD
  • Forward and reverse zone consistency

Remember that both services should run with proper permissions and have access to their respective configuration and zone files.


I recently set up a local DNS infrastructure using Unbound as my recursive resolver and NSD as an authoritative server for my LAN domains. While NSD works perfectly when queried directly, Unbound fails to resolve these local domains, returning SERVFAIL errors.

Direct query to NSD works flawlessly:

$ dig @127.0.0.1 data2.datanet.home -p 53530
;; ANSWER SECTION:
data2.datanet.home.     600     IN      A       192.168.1.62

But through Unbound, it fails:

$ dig @127.0.0.1 data2.datanet.home
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 47645

The most revealing log message from Unbound (with verbosity 4) shows:

skip addr on the donotquery list ip4 127.0.0.1 port 53530

Here's the relevant part of my unbound.conf:

server:
  interface: 127.0.0.1
  interface: 192.168.1.50
  access-control: 192.168.1.0/24 allow

stub-zone:
  name: "datanet.home"
  stub-addr: 127.0.0.1@53530

Unbound has a built-in protection mechanism that prevents queries to localhost by default. This is controlled by the do-not-query-localhost option which defaults to "yes". When this is enabled, Unbound will refuse to send queries to any server on localhost, including our NSD instance.

Add this to your unbound.conf:

server:
  do-not-query-localhost: no

After making this change and restarting Unbound, local resolution should work:

$ sudo systemctl restart unbound
$ dig @127.0.0.1 data2.datanet.home
;; ANSWER SECTION:
data2.datanet.home.     600     IN      A       192.168.1.62

If you prefer not to disable localhost query restrictions entirely, you can:

  1. Run NSD on a different IP address (like 192.168.1.50)
  2. Or use the following more granular approach:
server:
  do-not-query-localhost: no
  access-control: 127.0.0.1/32 refuse
  access-control: ::1/128 refuse
  access-control: 192.168.1.0/24 allow

Check your Unbound logs after the change. You should now see successful queries to your NSD server:

info: DelegationPoint: 0 names, 1 addrs
debug:    ip4 127.0.0.1 port 53530
info: processQueryTargets: sending to target 127.0.0.1 port 53530

While this solution works, be aware that:

  • Disabling localhost query restrictions could potentially allow cache poisoning if other services on localhost are vulnerable
  • Make sure your NSD configuration is properly secured
  • Consider using TSIG for authentication between Unbound and NSD

Here's a complete working configuration example:

server:
  interface: 127.0.0.1
  interface: 192.168.1.50
  do-not-query-localhost: no
  access-control: 192.168.1.0/24 allow
  verbosity: 2

stub-zone:
  name: "datanet.home"
  stub-addr: 127.0.0.1@53530
  stub-first: yes

stub-zone:
  name: "1.168.192.in-addr.arpa"
  stub-addr: 127.0.0.1@53530