Can 'dnsmasq' be trusted?

I'm just getting DNS resolution set up on a recently-installed LEDE box.

I'm doing packet-capture on the traffic dnsmasq is forwarding to its configured default nameserver, and this is revealing several things that seem to be broken. It could be dnsmasq itself, or it could be in the translation that seems to take place from the UCI config syntax to the dnsmasq config syntax.

Firstly I understand the UCI 'domainneeded' directive (dnsmasq: 'domain-needed') when set, should prevent queries for unqualified hostnames being passed to the default nameserver.

Instead however, they are, with the local domain name appended.

Similarly I'm seeing all queries for PTR records, included those that are successfully resolved by dnsmasq, ALSO being passed to the default nameserver, but with the local domain name appended after the 'in-addr.arpa'.

I'm considering binning dnsmasq and replacing it with something I can trust. But I'd be interested to hear of other folks experience.

1 Like

http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
https://lede-project.org/docs/user-guide/dhcp_configuration

Just give it another try with other settings.

1 Like

Hi, yes I know where the docs are. Unfortunately the docs seem to be incorrect, or the software is broken. For example, the docs state:

Options domainneeded, boguspriv, localise_queries, and expandhosts make sure that requests for these local host names (and the reverse lookup) never get forwarded to the upstream DNS servers.

In the default config I'm using, ALL these options are set, nevertheless dnsmasq is incorrectly forwarding queries for unqualified hostnames AND PTR records.

Right now, I cannot trust the docs or the software..

Have you double-checked the actual config files dnsmasq is using (/var/etc/dnsmasq.conf.*)?

My understanding (from the docs linked above) is the active dnsmasq config is all inside /etc/config/dhcp.

The file /var/etc/dnsmasq.conf contains only commented-out directives, to do with SRV records, it does not seem relevant.

I'm starting to think dnsmasq may have a bug whereby it cannot handle a DNS zone containing only the letter 'a'.

My local zone is 'a.foo.bar' (foo.bar substituted for privacy). This is configured as the default search zone in '/etc/resolv.conf', and the 'domain' and 'local' directives in '/etc/config/dhcp'.

What I'm seeing in the scenario already mentioned, is that dnsmasq is incorrectly appending 'foo.bar' (NO 'A') to queries for unqualified A records and to all PTR records (WTF ?) before passing these to its default nameserver.

Nowhere in the configuration have I given it the string 'foo.bar', it is munging the string I've given it. Very weird.

[quote="ajm, post:5, topic:2083, full:true"]
My understanding (from the docs linked above) is the active dnsmasq config is all inside /etc/config/dhcp.

The file /var/etc/dnsmasq.conf contains only commented-out directives, to do with SRV records, it does not seem relevant.
[/quote]You are wrong regarding /var/etc/dnsmasq.conf. Do not mix that to /etc/dnsmasq.conf ...

The LEDE-style uci config from /etc/config/dhcp gets translated to native dnsmasq config by the init script /etc/init.d/dnsmasq and gets stored to /var/etc/dnsmasq.conf.uniqueid that is then used to start the actual dnsmasq instance. See example from LEDE master below:

root@LEDE:~# head /var/etc/dnsmasq.conf.cfg02411c
# auto-generated config file from /etc/config/dhcp
conf-file=/etc/dnsmasq.conf
dhcp-authoritative
domain-needed
localise-queries
read-ethers
bogus-priv
expand-hosts
local-service
cache-size=1000

That file shows you the actual options given to the dnsmaq executable.

And the link above to the thekelleys site gives you the original full upstream dnsmasq options & documentation, which is a quite different thing than the Openwrt/LEDE docs that you quote.

This is not a dnsmasq issue and can be seen with other resolvers as well. This is a DHCP or Windows issue. If you are doing a hostname lookup from a Windows machine (nslookup), Windows will automatically append the so-called search domain (e.g. ".lan" or whatever you configured in your DHCP settings as your local domain name) to any hostname you are looking up. If you use a multipart domain like ".lan.mydomain.com" Windows will even go so far as to splitting that domain name and try various combinations like "hostname.lan.mydomain.com" and "hostname.mydomain.com".

So, there are bascially two ways to go about that issue:

  1. Filter the specific domain name by telling dnsmasq not to forward any requests with that local domain name. There is a setting for that in dnsmasq (I don't rememember what it's called, as I use unbound as a DNS resolver for quite a while now, but I know it's there and I tested it before).
  2. Disable the assignment of a local search domain in your DHCP settings. This way the client computer will not append the search domain to a hostname lookup anymore. The downside is that you then have to use the fully qualified domain to connect to your various clients on the network.

Approach 1 has one more catch though: If you use a multipart domain, though, this approach isn't practical. Because, as I said, Windows will split the domain names, so if you filter ".lan.mydomain.com", a search for "hostname.mydomain.com" will still leak out. Blocking ".mydomain.com", however, isn't what you'd want either, because you might have other domain names at ".mydomain.com" (aside from .lan.mydomain.com), that you actually might want to be forwarded, such as "mywebserver.mydomain.com" for instance.

Thanks very much for the explanations there folks, plenty to have a think about.

I will probably end up running a recursive server on the same box using Unbound, so I'll try and keep the Dnsmasq config as simple as possible.

@EricLuehrsen has done a great job with UCI (and LuCI) integration for Unbound. You can have Unbound pass e.g. LAN queries to dnsmasq, see the README on Github for more info.

The setting under 1) mentioned by @silentcreek should be:

-S, --local, --server=

e.g. this part:
"Also permitted is a -S flag which gives a domain but no IP address; this tells dnsmasq that a domain is local and it may answer queries from /etc/hosts or DHCP but should never forward queries on that domain to any upstream servers. local is a synonym for server to make configuration files clearer in this case. "

It should translate to this in UCI (I guess):

option server '/mydomain.com/' or option server '/subdomain.mydomain.com/'

option local '/mydomain.com/' should also be equal to option server '/mydomain.com/'.

I only set option local in UCI and it put server=/mydomain.com/ in /var/etc/dnsmasq.conf

Using unbound alone won't help you. As I said, the queries are not generated by dnsmasq but are coming from the clients. And as you seem to be using a multipart domain name, it's also not as easy as filtering just that. I had the same issue with a mutilpart domain name as my local zone. For Windows, IIRC there is a setting to prevent the splitting of the search domain, but it has to be configured on every client, so I chose a different route. I simply removed the local domain from the DHCP configuration. So clients will not append the search domain automatically. Also dnsmasq won't automatically resolve the full domains for the DHCP clients, but I configured static domain names for those clients names for which I actually use them (which are just a handful).

Can't say I'm impressed by the Unbound UCI integration as it failed on pretty much the first thing I tried.

I changed the listen port from 53 to 533, but fails to make it though to the actual config which I understand is at: "/var/lib/unbound/unbound.conf".

Feel free to write a fix for the issues. Packages are community-maintained...

It works OK with '5353' but not '533'. Onwards..

This is probably a privilege issue. Depending on your configuration, unbound runs as un unprivileged user (i.e. without root permissions), which is good in terms of security. But on Linux only root is allowed to bind to ports lower than 1024. So, if you run unbound without root permissions, you have to use a port higher than 1024 or make sure that unbound starts as root and drops its privileges after binding to the desired port.

These are just some quick notes:

  • Unbound starts with root and drops to jailed user. That is how it gets [53] normally.
  • It is traditional to have DNS backbone and daemon connections on [1053] or [5353]. The UCI/LuCI filter "dumb thumb" mistakes that may cause grief so allow [53] or >[1024]. No RFC I can find gives a privileged port to "alternate DNS."
  • dnsmasq needs the option local and option domain settings and as long as option local '/your.domain.lan/' matches the query suffix, it should not be forwarded.
  • dnsmasq UCI also has options to prevent reverse queries of private domains and localhost, which may be part of your "leak."
  • If we are talking about Unbound, then realize you may not need dnsmasq. The Unbound UCI allows you to work with Unbound+odhcpd alone and get similar dnsmasq like function.
  • Unbound is strict about query filtering, if you use the initial option domain_type static setting.
  • Unbound HOWTO is on github: https://github.com/openwrt/packages/blob/master/net/unbound/files/README.md and don't forget luci-app-unbound if that can help you.
  • UCI doesn't (yet) help create SRV, MX, or CNAME records in Unbound. You can put your own local-data: lines in /etc/unbound/unbound_srv.conf.
  • Unbound does not reconcile CNAME records; they are just used for "IN CNAME." You need to put the alternate A and PTR records in manually.
1 Like

Thanks again folks all this info is really appreciated. TBH I feel a bit stupid as I was seeing an artefact of crappy Windows lookup/search behaviour and jumped to wrong conclusions.

Now I have Unbound running on a non-standard port and have set dnsmasq to use it for recursion, the system is behaving pretty much how I want it to.

Eric thanks for your detailed points I'm sure they may be useful, and I'll look closer at Unbound/UCI.