Deploying and using Unbound
As per the previous post I was working through a customer query about name servers and caching, and with some background reading discovered that the Linux name resolver library does not cache successful (or failed) name lookups. This can be helpful and in other cases can be problematic as it relies on a robust DNS infrastructure.
Use a local caching only server
One very obvious solution to this problem is to deploy a caching only DNS server to every system. The caching only server is then configured to use your regular DNS servers as upstream. This provides the benefit of being able to manage your DNS infrastructure and, at the same time, provide robustness to DNS client operations should a DNS server go offline.
The flip side of this coin however is that changes to DNS will take longer to propagate as you must wait for the DNS caches to expire on the clients before the updates to DNS are accurately reflected. In a large environment this can be even more frustrating as serverA could update it’s cache earlier than serverB leading to what would appear to be inconsistent results.
Taking all this into account, I decided I would deploy a caching DNS server on at least some of my client systems. The choice for caching DNS servers basically breaks down into dnsmasq and unbound. I have used dnsmasq before, and would like to think I have a reasonable understanding of how it works and the options it can provide. For this reason, I chose to use unbound.
I could easily have installed (and configured) unbound on a system by system basis. Despite what my other half thinks, I don’t have a huge number of systems and it would be relatively easy and straightforward to manually install unbound and configure it individually on each system. However, there’s not a lot of fun (or learning) to be done there. Coming as no great surprise, I’m going to use ansible to deploy the unbound service with specific configurations to my systems. This is relatively straightforward, as I already have a VM set up as an ansible ‘master’ host from which I can push out playbooks to every other system.
Fortunately, the deployment of unbound is also easy – it’s present in the standard repos for CentOS, Debian, and Fedora. As such deploying (and enabling) the unbound service is as simple as:
--- - name: Setup unbound for name resolution
- name: Install unbound
ansible_distribution == "RedHat"
- name: Install unbound
ansible_distribution == "Fedora"
- name: Install unbound
ansible_distribution == "Debian"
- name: Apply caching configuration to use non-Overplay upstream
- name: Start and enable the unbound service
Admittedly, it’s not the most exciting piece of ansible code you’ll ever see, however it does the job and deploys unbound to the required systems. It also copies over a standard configuration file before starting and enabling the service.
Each system also needs to be reconfigured to use unbound for name resolution though, so the /etc/resolv.conf needs to be updated to use 127.0.0.1. There are many ways to achieve this – another ansible playbook (or incorporated as part of the one above), manually modifying the /etc/resolv.conf, or push out 127.0.0.1 via DHCP. The vast majority of my IP addresses are delivered via DHCP – and the router uses dnsmasq as the DHCP/DNS server. The dnsmasq instance is configured in such a way that there are ‘tags’ assigned to specific groups of devices.
I created a new tag, and set the nameserver to be 127.0.0.1 for this tag, and then I have assigned the systems to this tag that I am pushing out the unbound package to. When the DHCP lease is renewed, the system should then be using the locally installed unbound package for name resolution.
Assuming this all goes well, I can assign more systems to my ‘local-dns’ tag in the router dnsmasq configuration. I also have several systems configured to use upstream DNS servers from overplay.net that are used for unlocking services such as BBC iPlayer, Netflix etc. These can be accommodated with a local unbound instance by using the overplay.net DNS servers as the upstream/forwarders. I can also choose to use my local router dnsmasq instance for my internal (LAN) domain name.
Using ansible inventories, I am able to deploy different unbound configuration files to different systems depending upon their purpose. I also have a pihole instance running to block adverts at the DNS level, and it should be possible to incorporate this into the configuration as well. Probably by configuring pihole to use overplay.net as upstream, and configure unbound to use pihole for non-local addresses.