Homelab Pt 3 - Deploying Pi-hole & Unbound for Network-wide DNS Filtering
Introduction
In this post I will walk through setting up Pi-hole & Unbound in my home network. These two services will work together to act as the primary DNS server for my home network. Once complete, the lifecycle of a DNS request will follow this process:
Pi-hole will be the first point of contact for any DNS requests within the network. Pi-hole serves as a DNS filtering system where it has a list of domains that are used for either advertisements or tracking purposes. When it receives a DNS request that matches one of these domains, it will return 0.0.0.0 to the requester and that content will not be loaded. If the DNS request doesn’t match any domains on the deny list, by default, the request is forwarded to a 3rd party DNS server (Google, Cloudflare, etc) and the 3rd party will resolve the domain. This works great if you are only concerned about eliminating advertisements & tracking from the sites and apps your endpoints are visiting. However, if you’d like you take enhance the privacy of your DNS requests, you might use something like Unbound in addition to Pi-hole.
Unbound performs the same role as the 3rd party DNS server in the example above, but it is open-source and self-hosted. This means that DNS requests can still be resolved without needing to be sent to a 3rd party middleman that likely tracks and builds an activity profile around those requests. Here is an example of how a DNS request for a domain that is not on the deny lists or cached is handled:
Once a domain is resolved for the first time following the process above, the results are stored in Pi-hole and Pi-hole will return the IP address on the next request instead of having to go through Unbound. Because of this, initial requests to a domain will take a bit longer, but following requests will be much faster as they are only resolved locally.
Overall, this setup provides 2 main benefits, those being:
- Network-wide ad-blocking (Which in-turn blocks malware)
- DNS requests are not sent to a 3rd party middleman
Installing Pi-hole
Now let’s get this set up. Pi-hole and Unbound will both be installed on the same Proxmox VM which has ballooning memory from 512MB-4GB, 32gb of storage, and 4 cores. This VM is located in the Management VLAN with its IP being 10.0.1.53.
Starting from the VM’s shell, I’ll enter the following command to start the all-in-one install wizard from Pi-hole:
1
curl -sSL https://install.pi-hole.net | bash
After a few seconds, I am met with the install wizard! I’ll click OK to acknowledge the warning.
On the next screen I’ll click OK again.
Now I am met with a warning saying that the VM will need a static IP. I have already configured the static IP as 10.0.1.53 so no worries here.
Here we choose the upstream DNS provider that Pi-hole will forward non-blocked/cached requests to. Once we install Unbound that will be Pi-hole’s upstream DNS provider, but I will choose Google for now and change that once Unbound is configured.
Next up, I will choose to opt in for the default block list that Pi-hole will use to return 0.0.0.0 for any domains on the list. There are various different lists out there, and I will likely add some more in the future but for now the default will do.
I’ll select Yes on this prompt as I will be using Pi-hole via the web interface.
Since I opted in for the web interface, I am now prompted to install the dependencies. I’ll accept.
Now I am prompted if I’d like to enable query logging or not. If your risk tolerance does not allow for any DNS records being kept, you’ll want to select no here. Since I will be using the DNS logs for various things like troubleshooting network issues or security detections, I will enable query logging.
Because I selected to enable query logging, I am now prompted on how much information those logs should contain. For my use-case, I’ll show everything. Again, if you are concerned about logs being discovered through access to your server, you may want to choose a different option.
That’s it for the Pi-hole installation wizard. I am now given the location where I can access the web interface and the default password.
Before I access the web interface, I will change the default password to my own using the following command on the VM:
1
pihole -a -p PASSWORD
With the new password, I’ll head over to the location I was given and I can now see the web interface. When doing the installation, my PC was in the same VLAN as Pi-hole, so I didn’t need to worry about firewall rules between them.
After authenticating, I can now see the main dashboard. Since we don’t have any endpoints pointing to Pi-hole yet, there aren’t any queries to be listed.
Now that Pi-hole is installed and listening on port 53 for any DNS requests, let’s get Unbound going.
Installing Unbound
To install Unbound, I’ll run the following command on the VM:
1
sudo apt install unbound
Once installed, I’ll need to create Unbound’s configuration file. I’ll paste the default configuration file provided by Pi-hole into the config. To both create and open the config file in a text editor for changes, I’ll run:
1
sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
Unbound is now listening to only local queries from Pi-hole on port 5335 TCP/UDP. With the configuration file good to go, now I’ll need to change Pi-hole’s upstream DNS provider to Unbound. To do this, I’ll head back to the Pi-hole web interface->Settings->DNS. Here I will deselect Google and add the custom IP of 127.0.0.1#5335. I used local host as Pi-hole and Unbound are both running on the same host.
Since my network is segmented through VLANs, I’ll want to select the “Permit all origins” option on the interface settings. By default, Pi-hole will only accept requests from the LAN it is in (10.0.1.1/24 in my case). If this is not selected, the hosts in other VLANs won’t be able to resolve DNS queries as they will be thrown out by Pi-hole.
With Pi-hole now forwarding any DNS requests that aren’t blocked or cached to Unbound, everything should work properly. Now all I need to do is to point endpoints to use Pi-hole as their DNS server and we should see some activity.
Testing Ad-Blocking
To ensure everything is working properly, I will only change my PC to point to Pi-hole as its DNS server. However before doing this, I will visit a site that serves a decent amount of ads so I can get a baseline. When visiting knowyourmeme.com, there are 3 obvious ads from the jump. There is a big banner ad across the header of the page, another banner ad across the footer, and one more ad on the right side of the screen. Rather obnoxious, let’s see if we can get rid of those.
To test Pi-hole’s ad-blocking, I will manually change my PC’s settings to use Pi-hole as its DNS server.
After doing this and reloading the page, I can now see the top and bottom banner ads are completely gone and there is an empty box where the ad on the right was. Perfect!
Heading back to Pi-hole’s web interface, I can see queries have started to populate since switching my PC’s DNS to Pi-hole.
Everything appears to be in order so I will move forward with switching over my whole network to use Pi-hole as its DNS server. I’ll also switch my PC back to the default DNS server provided by DHCP (Which will be Pi-hole after this next section).
Configuring OPNsense
To switch my entire home network to use Pi-hole as the DNS server, I will need to change the DHCP settings for each VLAN to point to the Pi-hole server. Starting with the default LAN (10.0.1.53), I’ll head to OPNsense and clear out the Google and Cloudflare DNS server then add the Pi-hole server. If I would like to add a backup DNS server such as Cloudflare in my DHCP server, I could do that here as well.
Since my PC is currently on this LAN, after saving and applying these changes if I do ipconfig /renew and then ipconfig /all, I can see what DNS servers my PC has. It looks like I am being handed out the correct DNS server!
Now that the default LAN is good to go, I will do the same for all of my VLANs. I’ll just walk through one for brevity, but the process is the same for each one. Here I will go to OPNsense->Services->DHCP->APPLICATIONS where I will remove the old Google and Cloudflare entries and add Pi-hole.
After saving and applying the changes, I’ll spin up a test machine in that VLAN to ensure all is working properly. After the machine is running, if I try and nslookup google.com I get an error saying the DNS server couldn’t be reached, what gives?
Because the firewall rules for the VLANs don’t allow any inter-VLAN traffic, the host’s DNS request are being stopped by OPNsense and never actually making it to Pi-hole. Here is what the rules currently look like:
The DNS requests are be getting caught by the second rule and blocked.
To remediate this, I will add a rule to this VLAN that allows traffic from any host in the VLAN to 10.0.1.53 on TCP/UDP 53. This will only allow DNS traffic to Pi-hole, so DNS will work properly but the VLANs will still remain largely separated outside of DNS traffic. If my risk tolerance was much lower and I didn’t want to allow any inter-VLAN traffic while still resolving domains internally, I could place a DNS server in each VLAN to remove the need for this rule.
After creating the firewall rule and placing it above the rule that blocks traffic to other VLANs (OPNsense rules work top-down, so this rule will be processed first if placed above) the APPLICATIONS interface now has the following firewall rules:
Now that the traffic to Pi-hole is allowed, if I try to nslookup google.com again, the traffic is sent to Pi-hole which then forwards it to Unbound which goes and actually does the lookup.
Back on the Pi-hole web interface, I can also see this traffic from the endpoint in the 10.0.40.1/24 VLAN.
Everything is now working properly and even hosts on another VLAN can make DNS requests. I’ll go through and change the DNS servers and firewall rules for each other VLAN to allow for the same.
Closing Thoughts
Now with Pi-hole and Unbound fully setup and working, any host within my network is able to make DNS requests that will have any ads/trackers on the deny list blocked and have other legitimate traffic resolved locally without being sent to a 3rd party middleman.
Thanks for taking the time to read this post and feel free to reach out via LinkedIn if you have any questions!