Home Network DNS Infrastructure

Today, I stumbled upon Pi-hole, a DNS server designed for home deployments to block ads for a network. Unlike Adblock Plus or other browser-based adblockers, this applies to all devices on the network, including phones and tablets. Pi-hole also provides a web GUI with a nice dashboard to visualize your network’s use of the system (depicted below). This pet project cascaded into revisiting DNS across my home networks.

After reading up on Pi-hole a bit, I grabbed my nearest spare Raspberry Pi (which was only running an unused instance of YunoHost), reimaged it with a fresh Raspbian Lite install, and loaded Pi-hole on it. The installation was effortless; the script even runs sudo for you if you’re too lazy to execute it yourself. Note that DNSSEC is not enabled by default, but it’s a simple checkbox in the GUI.

After installing, I pointed my laptop’s DNS at it and it was answering queries, recursing via Google DNS. All worked, so I reconfigured my site’s DHCP server to hand out the Pi-hole box as the DNS server.

Unbound

Next, an article in the Pi-hole documentation caught my eye: Pi-hole as All-Around DNS Solution. The name of the article is a little misleading, but fundamentally the suggestion is to install Unbound on the same box and directly recurse the root servers, rather than using Google DNS or one of the other providers.

There are a couple of advantages to doing this. For one, there’s a privacy benefit – that 3rd-party DNS provider isn’t going to obtain data about every site users on your network visit (unless they’re the type of users who will Google search for google.com to execute a Google search for Facebook). Secondly, it enabled me to set up a system by which a host on any of 3 sites connected Pin-hole via site-to-site VPN could query for a hostname at another site. In my existing architecture, with separate DNS and DHCP servers running on the routers at each site, I could not [easily] configure this.

It is important to note that there are a few drawbacks to querying the root servers directly. First, it does not prevent your ISP from inspecting your DNS queries. It’s unlikely that they’re doing so, but they still can. If you want to prevent this, you could use DNS-over-HTTPS, but then Cloudflare will have all the queries. Overall, I’d pick the root server approach for the best privacy. Second is the “good Internet citizen” component. If everyone did this, it would defeat the hierarchical structure of DNS caching on the Internet, and the root servers would likely not be able to handle the load. Finally, it may interfere with some CDN implementations. I’ve tested and confirmed that Apple is still serving me off their local caches, but even that is likely to be dependent upon the ISP’s configuration. For example, if an ISP employed caching servers locally and only advertised their own DNS servers to them, the CDN may not know to redirect your traffic to the nearest cache. In this way, even using a non-ISP recursive server can break CDN cache selection, but, hopefully, this is generally mitigated by RFC 7871.

Following the guide, the installation and setup with Unbound to work with Pi-hole was also seamless.

Handling Local DNS

I have certain hosts on my local network that are accessible through the Internet. When on the LAN, I want to resolve their IPs to the private IPs, rather than the public IPs. This is easily accomplished, without Unbound, by editing the /etc/hosts file on the box and adding entries appropriately:

127.0.0.1	localhost
::1		localhost ip6-localhost ip6-loopback
ff02::1		ip6-allnodes
ff02::2		ip6-allrouters

#127.0.1.1	pi-hole-server
10.1.0.3	pi-hole-server

10.1.0.7        server1.domain.com www.domain.com domain.com
10.1.0.7        server2.domain.com anotherservice.domain.com

I also want to be able to reach hosts by hostname when their IPs are not static but are assigned by DHCP. There are two solutions built into Pi-hole for this – you can either run DHCP on Pi-hole or you use “Conditional Forwarding” to forward requests for a specific domain to one IP (assuming your DHCP server is still operating a DNS server). These are both configurable in the web GUI. Unfortunately, neither of these options scales to multiple domain DHCP servers and domain names. Unbound provides a solution.

My Network

I have 3 sites connected over the Internet with OpenVPN. Until now, the local router at each site has handled DHCP and DNS. DHCP functionality will remain on the routers; at this point I have moved DNS except for processing of local/private queries off of SITE1-RTR and onto Pi-hole on a Raspberry Pi connected to SITE1-SWT1. All of the routers currently are Ubiquiti ER-X-SFPs.

Configuring the Routers

Previously, the routers did not write hostnames to the /etc/hosts file to be served via their DNS servers, so even locally, they would not resolve DHCP learned hostnames. This is easy to rectify on each EdgeRouter:
using:
set service dhcp-server hostfile-update enable

Next, there needs to be a way to differentiate between sites so that Unbound knows which DNS server to query for each hostname. To do this, I assigned a domain-name via DHCP. Unfortunately, EdgeOS does not support this natively, but it does allow you to set custom options, so this can be achieved using:
set service dhcp-server global-parameters 'option domain-name "<site>.domain.com";'
Using a different <site> name for each location, I can differentiate between hosts at different sites.

Note that these two options will not take effect until the hosts obtain new DHCP leases. You can see if the information is populated via sudo cat /etc/hosts.

Finally, previously, there was no need for one site to accept DNS queries from another. We just need to enable the routers’ DNS servers to listen on the VPN tunnel interfaces:

set service dns forwarding listen-on vtun1
set service dns forwarding listen-on vtun2

Configuring Unbound

Next, Unbound needed to be configured to know what DNS server to query for which domain. I appended the following to the end of the pi-hole.conf file the guide provided me:

    private-domain: "site1.domain.com"
    private-domain: "site2.domain.com"
    private-domain: "site3.domain.com"
    local-zone: "1.10.in-addr.arpa." nodefault
    local-zone: "2.10.in-addr.arpa." nodefault
    local-zone: "3.10.in-addr.arpa." nodefault
    stub-zone:
        name: "site1.domain.com"
        stub-addr: 10.1.0.1@53
    stub-zone:
        name: "site2.domain.com"
        stub-addr: 10.2.0.1@53
    stub-zone:
        name: "site3.domain.com"
        stub-addr: 10.3.0.1@53

10.1.0.1, 10.2.0.1, and 10.3.0.1, are, of course, the router addresses at each site serving local DNS. Afterward, Unbound needs a restart: sudo service unbound restart

After taking these steps, I can query any <host>.<site>.domain.com from site1, which is currently the only site using Pi-hole.

Remaining Work

Two items remain. For one, I need to set up a Raspberry Pi at each of the remaining locations similarly.

The other item is an issue I’m still running into – Unbound is not resolving PTR records. The biggest issue with this is that the dashboard reflects only the IP address – and not the hostname – of each client. I’ve tried adding the following config to pi-hole.conf with no benefit:

    stub-zone:
        name: "1.10.in-addr.arpa"
        stub-addr: 10.1.0.1@53

If you have any suggestions on how to fix this nagging issue, please leave a comment! Otherwise, I hope you’ve enjoyed this post and perhaps make use of it!

One Reply to “Home Network DNS Infrastructure”

Leave a Reply