Unbound prioritizes external answers over dnsmasq's answers

Hi all

I have so far been running dnsmasq as it comes out of the box.
To enjoy various DNS security protocols I installed unbound and the LUCI interface for unbound.
I configured it to use dnsmasq for DHCP. (The DoT resolution work like a charm - but that is not the topic here. If you are interested in the setup, you may still well ask.)

I need this because I have configured a substantial number of internal hostnames in dnsmasq. More detailed explanation further down.

I seems that unbound first asks any servers configured in unbound, and if it does not find answer there, it will ask dnsmasq. This means, if there is a hostname that exists on an external NS and also in my dnsmasq hostnames, unbound will answer with the IP resolved from the external NS.

So you ask - what the .... why is this a problem?

There is one use case that makes me depend on this:
For all mobile clients I have configured a global name for all my own services that I consume from my home LAN and also when on the road coming from WAN. However, these services are not running on OpenWRT, but in my DMZ on a dedicated server. If I just provide a DDNS IP for the service, it will be reachable from the WAN, so when I am on the road. But from my home LAN, this does not work, because the packets would need to be routed to the WAN interface and from there would need to be forwarded back to the DMZ server. OpenWRT doesn't do this, and it would also be inefficient.

To solve this, I also provided the same global names - which I registered with DDNS providers - also in my dnsmasq hostnames, but assign the internal DMZ IP addresses there - not the external IP.

So far this has worked very well, because dnsmasq was responding with the local hostnames first, before it was asking external NSes.

Since I use unbound, this order as turned around, and the IPs from the external DDNSes are returned before the internal dnsmasq is asked.

This way, I cannot reach my DMZ services using the global names any more from LAN.

Long story short - the question is:
Is there a way i can make unbound asks the local dnsmasq BEFORE it asks other NSes?
(or is this overall a stupid idea and you have a better solution how I can reach my DMZ services from LAN and from WAN using the same FQDN?)

Any help is highly appreciated!

Look into unbound views, (type-)transparent zones, and stub resolvers. That's how I provide local overrides on a subnet-by-subnet basis for "global" DNS entries.

(I have no idea how to configure with UCI, as I use file-based config.)

Hi jeff, thanks for the pointers. i actually read up on those concepts in the manpage. Since I a totally new to unbound I have not clue what the config file blocks would have to look like for what you are proposing. Do you mind giving a sample setup and sample excerpts from your config that implement the overrides?

I know this is some work. It would be of great help and appreciated, I am still stuck..

Working "from the inside out" (super stripped-down example)

Define the local overrides

$ cat zones/external. 
local-data: "imaps.example.com.			A"
local-data: "mail.example.com.			A"

and their reverse

$ cat zones/192.168.1.rev 
local-data-ptr: "			mail.example.com"

Now define a view for hosts on the subnet

$ cat 40-view-data.conf 
	view-first: yes

	include: /var/unbound/conf.d/zones/external.

Define the zone itself

$ cat 30-global-data.conf 
local-zone: "example.com." typetransparent

and give it to the clients

$ cat 20-access-control.conf 
	interface:  	::0

	access-control: allow

	access-control-view:	""

Make sure that can be returned

$ cat 10-server.conf 
# See ideas at
# https://calomel.org/unbound_dns.html



So, with this, a host on the subnet makes a request for something in example.com

  • is allowed by 20-access-control.conf
  • is assigned to the view that I chose to name "" (a helpful name that just happens to be the subnet)
  • Looks at the view-first
  • If the data is there (mail.example.com), it gets it (locally accessible "inside" server)
  • If the data is not there, typetransparent sends the request for cloud.example.com to the public servers for example.com, and receives the "public" address

typetransparent means that if, for example, you're looking for MX for mail.example.com, it will go to the public servers (as the local data is only for A records).

I haven't hooked up dnsmasq, as I run kea, and just have the static IPs assigned listed in my zone files. I've personally never found much value in reverse resolving to DHCP-generated names. I've thought it through in the past. As I recall, another layer with setting dnsmasq as a stub resolver was the approach I would have taken. I checked my notes and unfortunately didn't find any details.

Keywords: split-horizon DNS, hairpin NAT

1 Like

Thanks, I am trying to wrap my head around it. In my case it is just 1! global name because I just have 1 WAN IP with 1 DDNS provider. OK actually I assign the same WAN IP to several DDNS providers for failover.
Would that mean that I need for each DDNS name a separate zone (they all point to the same IP!), or would I put them all in one zone. If several zones, could I use 1 view fo all of them or would I have to create an own view for each of the zones?

Isn't using a forward zone much easier?
(stub zone doesn't work because dnsmasq doesn't act as authoritative dns server?)

Something like:

config zone
	option enabled '1'
	option fallback '0'
	option zone_type 'forward_zone'
	list zone_name 'host1.ddnsdomainname.com'
	list zone_name 'host2.ddnsdomainname.com'
	list server ''

None uci approach:
In /etc/unbound/unbound_ext.conf add:

  name: host1ddnsdomain.com
  forward-first: no
  forward-tls-upstream: no

  name: host2ddnsdomain.com
  forward-first: no
  forward-tls-upstream: no


private-domain: host1ddnsdomain.com
private-domain: host2ddnsdomain.com
domain-insecure: host1ddnsdomain.com
domain-insecure: host2ddnsdomain.com

Assuming that dnsmasq is running on port 1053.
And that dnsmasq is still configured to give out local IP address for the ddns hostname(s).

But jeffs approach is the better one :slightly_smiling_face:

1 Like

I create a view for each of my subnets. That way, for example, my phone and laptop always get mail from “imaps.example.com” on wireless, wired, VPN to home, or outside. They get an on-link IP at home, my public IP (port forwarded at my router) when away. No messy hairpin NAT games.

I tried this method as it seemed easier. Alas, it does not work. The reason seams to be that the "config zone" paragraph is not recognized in "/etc/config/unbound" and those keywords seem not to be defined (see https://github.com/openwrt/packages/blob/openwrt-18.06/net/unbound/files/README.md)

Does that actually work for you, shm0?

Appears to have been introduced in later versions, there in master and also 19.x I think.

1 Like

I apologize that I overlooked this.

I edited my post above to include a none uci approach.

1 Like

Thank you shm0, that works on OpenWT 18, indeed.

1 Like

I'm glad that it works you :slightly_smiling_face:

Actually DHCP does not seem to announce the router as dns server any more. How can I verify that (I looked into the DHCP ACK using wireshark and I found NO DNS servers), and where could I check the config accordingly?

tcpdump-mini would be a great tool to use to diagnose.

any DHCP specific tools? like dhclient?
I am looking around what the reason could be. the interface dhcp settings look normal in the UI at least.

I tried to put "6," in DHCP Options for LAN interface. Accordingly the DHCP ACK now containts "DNS Server". That was not necessary earlier when I was just using dnsmasq. This since to have broken when I installed unbound. ???? Is this a bug?

More on this topic to be found here:

Actually it makes sense that dnsmasq doesn't provide a DNS server IP in the DHCP ACK.
Because dnsmasq isn't listening on the default port anymore.
And dnsmasq can't know if there are other DNS servers around.

The uci command will also hard code the IP.
uci get network.lan.ipaddr gets parsed at runtime/when you execute the command.

For example, if your lan interface has the IP
The actual command will be:
uci add_list "dhcp.lan.dhcp_option=option:dns-server,"

You can verify this with:
uci get dhcp.lan.dhcp_option

There is also the option to replace dnsmasq with odhcpd.
But then you lose the luci configuration but dns server will be automatically distributed again.
The unbound scripts provided by openwrt can parse your dhcp config, so you are still able to use dns overrides.

  option add_extra_dns '0'
    Level. Execute traditional DNS overrides found in `/etc/config/dhcp`.
    Optional so you may use other Unbound conf or redirect to NSD instance.
    0 - Ignore `/etc/config/dhcp`
    1 - Use only 'domain' clause (host records)
    2 - Use 'domain', 'mxhost', and 'srvhost' clauses
    3 - Use all of 'domain', 'mxhost', 'srvhost', and 'cname' clauses

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.