scottmuc / infrastructure

Documentation / Automation for personal third-party infrastructure
The Unlicense
11 stars 2 forks source link

Use `dnsmasq` to handle LAN DNS #75

Closed scottmuc closed 5 months ago

scottmuc commented 5 months ago

Issue #72 resulted in a chat with my mate @stuartnelson3 about why I don't just use dnsmasq for my DNS. To answer this, I need to tell a story:

How Did I Get Here?

Using unbound to be my DNS resolver was an early decision. I initially explored using pi-hole but ultimately chose unbound and documented some of this decision process.

In order to inform the hosts on my LAN of my locally running DNS resolver, I needed a DHCP server to provide this detail to hosts that join the network. I ended up going with kea because it appeared that isc-dhcp-server was going out of support. I wrote some notes on this earlier.

During this initial setup research, I did not consider dnsmasq at all (except acknowledging that pi-hole uses a fork of it).

For a couple years, this configuration was working fine until Sept 2022 when a repave, https://github.com/scottmuc/infrastructure/issues/41#issuecomment-1250045214, had issues due to availability of the isc-dhcp6-server package being available for the raspberry PI platform architecture. This repave resulted in https://github.com/scottmuc/infrastructure/issues/42, which is how I got to running dnsmasq along with unbound.

Why Not Go 100% dnsmasq?

dnsmasq is does not do query recursion, it can only forward (see this DNS server feature matrix). My self-imposed limit with my personal infrastructure is do reduce the number of dependencies I have on big-tech services. Why inform CloudFlare or Google of my DNS queries when it's not that hard to run your own DNS?

Because of this I cannot ditch unbound and only use dnsmasq.

So Why Do I Want To Use dnsmasq for DNS Still?

In #72, I made a bunch of static DHCP host records (in dnsmasq), and static DNS records (in unbound). This works, but I wanted to explore a different option. Really, the only hosts that need predefined static IPs is my router and my DNS resolvers. I intend to use name resolution over ip addressing for hosts in my network. I cannot a technical reason as to why, except that I want to explore the option and learn. This does create an over-arching LAN dependency on name resolution of hosts in .middle-earth.internal., but again, that's part of the fun.

By using dnsmasq as the resolver for my LAN, I can use its DHCP host registration features and ditch the static IP assignment for everything but the things that truly need static IPs. Name resolution should still work, though I'll need to see if any local DNS caches cause any issues.

The Task(s) At Hand

Other noteworthy resources that came up during research:

scottmuc commented 5 months ago

Summary

Switching to dnsmasq for DNS

This item was all in https://github.com/scottmuc/infrastructure/commit/2261a7d477cd1e7694e1aee013c158a56c8d720b. The only gotcha was the bind-interfaces flag to tell dnsmasq not to a wildcard binding.

Registering DHCP clients in DNS

This also was pretty easy. When a host does a DHCP broadcast, it can provide a name. My phone, and computers were able to do this and it worked great. Now the IP of sam.middle-earth.internal among others is no longer predictable.

Other devices were getting random IPs, but their hostnames were out of my control (e.g.: the TV would get Samsung.middle-earth.internal, and the printer would get something like MFC-L2710DW.middle-earth.internal). I brought back the MAC based static IP assignment, though I probably could have just provided the name only.

unbound configuration is more coherent

Now unbound has zero knowledge of my LAN. It's only client is dnsmasq, which forwards DNS queries to it as long as the domain isn't middle-earth.internal or a bare hostname.

pippin resolution and resolving *.middle-earth.internal hosts from pippin

Since pippin's IP is statically assigned, I needed to inform dnsmasq by putting an entry in /etc/hosts:

https://github.com/scottmuc/infrastructure/blob/7219085922a6b4290bb072c641bf81733d95015d/devices/pippin/tasks/staticip.yml#L38-L47

dnsmasq will read that file to load up some static name to IP mappings.

Since unbound couldn't resolve these names anymore, I had to configure pippin to use dnsmasq as the entrypoint to name resolution:

https://github.com/scottmuc/infrastructure/blob/7219085922a6b4290bb072c641bf81733d95015d/devices/pippin/tasks/staticip.yml#L26-L35

Closing Thoughts

This was a fun exercise to get a bit more understanding of DHCP and it's awkward (to me) relationship with DNS. I'm happier with this configuration between it creates a clear line of the responsibilities of dnsmasq vs unbound. dnsmasq is focus on my LAN requirements, whereas unbound is DNS resolver with some static ad-blocking host configuration.

Having a mixture of dynamic IP and static IP assignment does feel a bit odd. Now without the need for unbound static host configuration, I'm more inclined to bring back static DHCP IP assignment since that now can live entirely in dnsmasq. The only host that could benefit from this is gandalf because pippin scrapes its metrics endpoint and mounts a CIFS share.