top of page

Increase Privacy and Security with Pi-hole and Unbound

evanpowell

Updated: Jun 15, 2024



There are many tutorials out there to help you install and set up a Pi-hole. There are also an increasing number of tutorials focusing on using Pi-hole together with Unbound. In this guide, I'll show you how to install and set up Pi-hole and Unbound, but I'll also offer additional insights beyond the basic configurations that are often overlooked.


My homelab has a number of services that I use regularly, but the one that is undoubtedly the most used, and arguably the most important, is my Pi-hole. Since I have everything on my network routing its DNS requests through Pi-Hole, I've actually set up two Pi-holes on different network segments. To ensure that all of the devices on my network can reach the internet, with all of the privacy and security benefits of running a Pi-hole, I have the two Pi-hole instances configured as an HA pair.


Before we dig into things, let's first go over some of the basics.


Brief Overview of Domain Name System (DNS)

This isn't a meant to be a deep dive into DNS, but I'm providing a quick overview of DNS to make sure we're all on the same page. Feel free to skip the next section if you're already familiar with the basics of DNS.


With that, DNS can be thought of as an address book for the internet. All of the websites you visit (including this one) have an address that indicates where on the internet they reside. This address is called an IP address and will either be in the IPv4 or IPv6 form, and look something like 142.250.72.174 or 2607:f8b0:4003:c00::6a, respectively. It would be difficult to visit websites if you had to enter those wonky addresses. Fortunately, DNS translates (resolves) a more human-readable name, like Google to those addresses. Typing the domain Google.com into your browser will make a request to a DNS server, lookup the address associated with Google, and then direct your browser to that address. No need to remember all of those crazy IP address to navigate the internet. It's a bit more complicated than that, however that's the basic premise and sufficient enough to understand what we're trying to accomplish with Pi-hole and Unbound. I recommend checking out this resource by Nipum Thennakoon if you'd like a slightly deeper dive.


What is Pi-hole?

In short, Pi-hole a self-hosted DNS sinkhole that you can use to track and filter DNS requests from devices on your home network. Each time a device on your network reaches out to the internet and makes a DNS request, the request is checked against your blocklist (or blocklists). The DNS request is then translated (resolved) to an IP address if the domain isn't part of the blocklist, which tells your device where the website lives on the internet.


This level of control over the DNS requests on your network allows you to block, or at least track, malicious domains, trackers, or otherwise suspicious DNS requests on a per-device basis. This information can be used to identify if a system on your networks has been compromised, or if a piece of software you're using is calling out to known tracker domains, among other things. Think of Pi-hole as a very basic firewall, but strictly for DNS requests.


Another great thing about Pi-hole is that it can be configured to log all of the DNS request activity, which can then be view in various graphs and tables via its nicely designed admin portal.


Pi-hole admin portal
Pi-hole admin portal

What is Unbound?

As noted above, DNS translates, or resolves, a website's human-readable domain name into an IP address. The IP address is what tells your browser or other piece of software where that website resides on in the internet. Typically, Pi-hole will capture your DNS request, check if that website is on one of your blocklists, and then forward the DNS request to a DNS resolver - the thing that actually resolves the domain name to an IP address. Pi-hole is configured out of the box to send that DNS request to upstream DNS servers. These are third-party resolvers that are hosted and manged outside of your control. This is where Unbound comes in.


Rather than sending DNS request to an upstream DNS server (hosted by Google, Quad9, Cloudflare, etc.) for IP resolution, you can host your own "upstream" DNS resolver using Unbound.


This has many benefits, but here is what I believe are the big three:


  • DNSSEC is supported and can verify that DNS queries are unaltered

  • DNS queries are no longer shared with third parties

  • DNS queries will generally be quicker since they are locally cached


Setting Up Pi-hole and Unbound

In this section I'll walk through the steps of how I set up Pi-hole and Unbound in my environment.


Select a device and OS

Pi-hole is often set up on a Raspberry Pi, hence the name Pi-hole, but using a Raspberry Pi isn't actually a requirement. In fact, I have one Pi-hole instance set up in a VM on my Proxmox cluster, and a secondary Pi-hole instance on an old Raspberry Pi B+. You can install Pi-hole on just about any Linux device as long as it has at least 2GB of free space and 512MB of RAM. That being said, here are the operating systems that are officially supported at the time of this writing.

Distribution (OS)

Release

Architecture

Raspberry Pi OS

Buster / Bullseye

ARM

Armbian OS

Any

ARM / x86_64 / RISCV64

Ubuntu

20.x - 24.x

ARM / x86_64

Debian

10-12

ARM / x86_64 / i386

Fedora

39-40

ARM / x86_64

CentOS Stream

8-9

x86_64

For this walk-through I'll be using Ubuntu 22.04.4 LTS (Jammy Jellyfish), but the steps should be identical using a newer Ubuntu distro, including 24.04 (Noble Numbat), and nearly identical using a physical Raspberry Pi running Raspberry Pi OS.


The hardware I'm using is nothing incredible. I'm running an old Intel NUC with a Core i5-6500T, 16GB of RAM, and 512GB of SATA SSD storage. As noted before, I'm running Ubuntu in a VM using Proxmox as my KVM hypervisor (I'll likely post a setup guide on Proxmox later), but the steps I show here will be identical, regardless of running it in VM or on bare metal.


Install Pi-hole

The easiest way to get started is to run the following command.

sudo curl -sSL https://install.pi-hole.net | bash

This pulls and runs the installation script directly from the pi-hole repository. The end result is a ready-to-go experience that guides users through the installation and basic configuration process.


Please note that piping anything to bash goes against best practices unless you are absolutely certain about what you're about to run. Because of this, I recommend at least looking through Pi-hole's installation script before moving forward.


You may be asked to enter your password if you are not running as the root user, or if you didn't run the command with the sudo prefix. Simply enter your password to continue with the installation process.

Enter password if prompted to run script

An easy to follow installation and configuration wizard will appear, first letting you know that you're device will soon be a network-wide ad blocker. Press return/enter to continue.

Press return to acknowledge the prompt for donations (and consider donating).

The next screen will warn you about configuring your device with a static IP. This can be done in various ways, but I typically assign a static IP to a device/VM whenever I install the OS. Additionally, I configure the static IP within my router. I recommend doing it in both places to help ensure that the device and your network never attempt to obtain or assign a different IP.


If you have a static IP configured, used the arrow keys to select Continue (or press C), then press return.

You'll then be asked to select an upstream DNS provider. You can select whichever one you'd like since we'll configure Unbound to be our upstream DNS later. I generally go with Cloudflare when I'm not running my own upstream DNS.

Next is a question regarding whether or not you'd like to install StevenBlack's Unified Host List as one of your Blocklists. We'll go over Blocklists a bit more later, but this one is fine to start out with. Select Yes and continue.

We're then asked if we'd like to install the Admin Web Interface. This essentially installs a basic web server onto the machine and allows an easy, graphical UI to manage the Pi-hole. I strongly advise selecting Yes.

As noted above, the Admin Web Interface requires a basic web server to be installed. Pi-hole uses lighttpd, which requires several PHP modules to run. Select Yes.

Next, we have to choose if we want to enable logging. I recommend selecting Yes, but feel free to select No if you don't feel comfortable having your DNS queries logged and retained. Just note that you will lose the benefit of the charts and tables within the Admin Web Interface if you chose to disable logging.

Here we can select the level of privacy we'd like to have within the Admin Web Interface. Regardless of what you select here, everything is still saved to the database unless you choose Anonymous mode. With that being said, I recommend selecting Show everything unless you plan on having the charts or tables of the admin interface on display and you'd like to limit the domains or client data from being displayed.

You'll see a few checks and tasks completing before you see this installation complete screen. Take note of the web interface address and password, which you'll use to log into the Admin Web Interface.

Note: Don't worry if you missed the information on the previous wizard prompt, you will see the same information within the CLI after selecting OK.

Admin Web Interface

You can use the password that was provided during the installation process, but I recommend changing it to something a bit longer. This is easily done by running the following command.

pihole -a -p

Enter and confirm your new password when prompted. That's it!

Now let's go ahead and log into the Admin Web Interface. Simply open up your favorite browser, enter in the address provided during the installation process, and then enter your password when prompted.

Once logged in, you'll be presented the with home dashboard, which has analytics and graphs to show you key items at a glance. There won't be much to see here since we haven't directed any of our networked devices to use it yet. We'll cover that later. Let's move on to installing and configuring Unbound.


Install Unbound

It's great that we have Pi-hole installed and ready to block all of the DNS requests we'd like, but now it's time to install Unbound to take advantage of hosting our own recursive DNS server.


As with Pi-hole, initiating the install for Unbound involves running a very simple command.

sudo apt install unbound -y

This command uses the standard package manager, apt, for Ubuntu (or just about any Debian-based distro). This is ideal because unbound can be upgraded using the apt upgrade command. Moreover, installing via the package manager will install the necessary dependencies and the required configuration file for the the primary root servers.


If you happen to get an error similar to this one, stating that the Unbound DNS server couldn't be started, simply reboot and run the install command again to verify everything was installed and running correctly.

Configure Unbound

One part of setting up Unbound that may be slightly intimidating for some users is the fact that we need to create a configuration file.


First, start by using the built-in VIM text editor to create and edit the configuration file by running the following command.

sudo vi /etc/unbound/unbound.conf.d/pi-hole.conf

Feel free to download my configuration file, which has a few additional configurations for improved performance and security beyond the standard configuration. Copy and paste the contents into your newly created conf file in VIM.



If you'd prefer, here's the standard configuration provided by pi-hole.net. Simply copy and paste this into your configuration file.

server:
    # If no logfile is specified, syslog is used
    # logfile: "/var/log/unbound/unbound.log"
    verbosity: 0

    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes

    # May be set to yes if you have IPv6 connectivity
    do-ip6: no

    # You want to leave this to no unless you have *native* IPv6. With 6to4 and
    # Terredo tunnels your web browser should favor IPv4 for the same reasons
    prefer-ip6: no

    # Use this only when you downloaded the list of primary root servers!
    # If you use the default dns-root-data package, unbound will find it automatically
    # root-hints: "/var/lib/unbound/root.hints"

    # Trust glue only if it is within the server's authority
    harden-glue: yes

    # Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
    harden-dnssec-stripped: yes

    # Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
    # see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
    use-caps-for-id: no

    # Reduce EDNS reassembly buffer size.
    # IP fragmentation is unreliable on the Internet today, and can cause
    # transmission failures when large DNS messages are sent via UDP. Even
    # when fragmentation does work, it may not be secure; it is theoretically
    # possible to spoof parts of a fragmented DNS message, without easy
    # detection at the receiving end. Recently, there was an excellent study
    # >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
    # by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
    # in collaboration with NLnet Labs explored DNS using real world data from the
    # the RIPE Atlas probes and the researchers suggested different values for
    # IPv4 and IPv6 and in different scenarios. They advise that servers should
    # be configured to limit DNS messages sent over UDP to a size that will not
    # trigger fragmentation on typical network links. DNS servers can switch
    # from UDP to TCP when a DNS response is too big to fit in this limited
    # buffer size. This value has also been suggested in DNS Flag Day 2020.
    edns-buffer-size: 1232

    # Perform prefetching of close to expired message cache entries
    # This only applies to domains that have been frequently queried
    prefetch: yes

    # One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
    num-threads: 1

    # Ensure kernel buffer is large enough to not lose messages in traffic spikes
    so-rcvbuf: 1m

    # Ensure privacy of local IP ranges
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
    private-address: fd00::/8
    private-address: fe80::/10

It's important to restart the Unbound service in order for it to pick up the new configurations. This can be done by running the following command.

sudo service unbound restart

Now we have Unbound running with our configurations specified in the pi-hole.conf file. Before moving forward, take note of the following lines from the configuration file.

 interface: 127.0.0.1
 port: 5335

This portion of the configuration specifies that we are running Unbound on our localhost (127.0.0.1) over port 5335. The easiest way to verify that Unbound is ready for use, run a quick DNS query using the dig command.

dig evandigs.com @127.0.0.1 -p 5335

After running this command, verify that you see a status: NOERROR in the header.

You'll typically see an error if the configuration wasn't picked up correctly or if you have a port conflict. As always, if you see an error here, performing a reboot will typically solve the issue.


Configure Pi-hole to use Unbound

Going back to our Admin Web Interface for Pi-hole, click on Settings option from the left-hand menu. Then click the DNS tab.


When we configured Pi-hole during the installation wizard process, we used Cloudflare (or one of the other options) for our upstream DNS. This can be confirmed by looking at the check marks next to Cloudflare in the Upstream DNS Servers section.

As noted and tested with the Unbound config, we need Pi-hole to send DNS requests to loopback (127.0.0.1) over port 5335. Fortunately, this is very simple. All we have to do is remove those check marks in the Upstream DNS Servers section, and then add 127.0.0.1#5335 to the Custom 1 (IPv4) field. Make sure the check box is also selected for the Custom 1 field.

The basic setup of a Pi-hole Unbound setup is complete. There are many sites out there than you can use to test if your blocklists are working. One of my favorites is by d3ward. This will provide more detail about which ads and trackers are being blocked, and provide an overall score. You can use these results to tweak your blocklists as needed.


Additional Configurations and Blocklists

Here are a few notable configurations that I added to my Pi-hole to fit my needs. I'll also include resources for various Blocklists.


Interface Settings

For my production instances of Pi-hole Unbound, I have also configure the DNS interface settings (found under Settings > DNS > Interface Settings) to use Respond Only on Interface ens18 Rather than the recommended Allow only local requests setting. This was necessary because my network is segmented and not all of my devices are on the 192.168.10.0/24 segment. You'll need to do the same thing if you have devices on different network segments.


As recommended by the GUI, I firewalled my Pi-hole by configuring my UDM Pro to drop all inter-VLAN traffic by default, then I allowed specific network segments to pass DNS traffic to the Pi-hole IPs (see screenshot below). This post won't include details on my UDM Pro configurations, but this particular firewall configuration is worth noting.

UDM Pro Firewall Rule
UDM Pro Firewall Rule

Use DNSSEC

You may have noticed that I also have a check mark next to the Use DNSSEC option (very bottom of the last Pi-hole Admin Web Interface screenshot). This is an additional option that will verify DNS records before they are received from the upstream DNS server, thus enhancing security. I recommend enabling this feature whenever possible.


Conditional Forwarding

If you're like me, you also want to see the device names rather than just the device IPs associated with DNS requests on your network. If you're not using your Pi-hole as your DHCP server you'll need to enable the Use Conditional Forwarding feature. This can be found at Settings > DNS > Advanced Settings > Conditional forwarding. Simply check the box, enter in your local network's CIDR and the IP address of your router (or whatever you're using as your DHCP server).

Blocklists

Pi-hole really gets its functionality from the blocklists you provide. That being said, you will run into issues if you blindly use every blocklist found on the internet. This is because many of the service you want/need will be part of a domain or IP listed in one of the blocklists for one reason or another. For example, if you use YouTube and like to look at your watch history, you'll find that the domain required for this feature to work is on many blocklists. Identifying and correcting issues like this can be difficult, or at least tedious. I'll provide resources for blocklists and, equally as important, resources for safelists.


How to use Blocklists

Before going too much further about which blocklists to use, let's cover how to configure blocklists in the Admin Web Interface.


Click on Domains from the left-hand menu. The top section is where you'll add domains or lists of domains to your blocklist (blacklist) or safelist (whitelist). Here is where you can paste one of the URLs from resources lists below and add it to your blocklist/safelist.

In order to get your pi-hole to use the provided domains, navigate to Tools > Update Gravity. This will parse through all of the domains from any provided URL and add it to your Pi-hole's database.

Blocklist Resources

I have been using firebog.net for years. They provide a fairly large collection of list maintainers/sources to choose from. The nice thing about firebog is that they organize the lists by types. So, if you're only interested in blocking advertisements, you can grab the sources listed under Advertising Lists. If you wan to block known trackers, you can use the sources listed under Tracking & Telemetry Lists.


I've also pulled from various aggregators such as oisd.nl, which does a really good job at creating blocklists that rarely interfere with the things you would want/need. Most recently, I've been playing around with Hagizi's DNS Blocklist, which is very well organized and thorough. Definitely worth checking out.


Safelists Resources

As noted previously, you will absolutely break your ability to do things on the internet if you simply pull from every blocklist aggregator across the internet. It's a good idea to also maintain a safelist in order to not break the tools and services that you need. The two sources I primarily use for my safelist are from pi-hole.net and oisd.nl. I recommend reviewing and implementing your safelist items as soon as possible. Adding them before you add any blocklist would be ideal.


I was originally going to go through more specific examples of which domains and subdomains to safelist, but what I consider as a necessary item to safelist may not be important or relevant to you. I recommend going through the resources that I've provided to determine what makes the most sense for your situation. However, I am more than happy to dive into specific examples, how I was able to troubleshoot the issues, and what I ended up safelisting. Leave a comment or reach out if you're interested. I may make a separate post for something like that if I get enough positive feedback for it.


High Availability (HA)

For my HA setup, I have two Pi-holes. My primary Pi-hole is running in a VM on my Proxmox cluster, the secondary is running on a dedicated Raspberry Pi B+. They are each on a separate UPS and sit within different network segments. One of the more tedious tasks when running multiple pi-holes is keeping the blocklists and safelists in sync. That's where vmstan's gravity-sync project comes in.


Gravity-sync is essentially a bash script that will pull the configuration from your designated primary pi-hole, and sync it to a secondary Pi-hole. The script will sync the following:


  • Adlist settings with status and comments

  • Domain/RegEx blocklists and safelists, along with status and comments

  • Clients and group assignments, along with status and descriptions

  • Local DNS Records

  • Local CNAME Records Static DHCP Assignments


Note that the sync will not include any logs, so those will remain with each Pi-hole separately.


If you'd like to take advantage of what gravity-sync offers, set up a second Pi-hole in the same way that was discussed in the previous section.


Please note that gravity-sync requires a user with system-level admin permissions. That user can be the root user (not recommended) or any other user that is a member of the sudo group. This is necessary because the user that will be used with the syncing script will need to be granted password-less sudo permissions.


I've created a new user, gravitysync, on both of the Pi-hole instances. I recommend that you also create a separate user specific for the sync script.


Make sure the new user has sudo permissions by running the following command.

sudo usermod -aG sudo gravitysync

If you're like me and have disabled PasswordAuthentication in favor of only leveraging PubKeyAuthentication, then you'll need to edit the sshd_config file to allow the gravity-sync user to authenticate via password. To start, run the following to open the file in the text editor.

sudo vi /etc/ssh/sshd_config

Add the following at the end of the file

Match User gravitysync
	PasswordAuthentication yes

Edit the sudoers file

sudo vi /etc/sudoers

Add the following at the end of the sudoers file.

# Allow sudo without password
gravitysync ALL=(ALL) NOPASSWD: ALL

Now that we have our user set up, we can finally get started with gravity-sync installation process. This is as easy as running the following command.

curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh | bash

As noted previously, piping anything into bash is not recommended unless you know exactly what the script is doing. For that reason, please take some time to review the code from vmstan's github repository before blindly piping it to bash.


The script may prompt you to enter your password in order to obtain sudo permissions to run the necessary tasks. Enter your password and hit enter.

From there, you'll need to enter the ip address of the secondary Pi-hole.

Add the gravitysync username.

Type yes if/when prompted to connect to the secondary pi-hole machine.

Type the password of the gravitysync user on the secondary Pi-hole.

Once that runs successfully, you'll want to run the following command to verify that the primary Pi-Hole instance can SSH to the secondary instance.

ssh -p '22' 'gravitysync@192.168.10.13'

It will look something like this.

Run the same installation command/script on the secondary Pi-hole. Follow the same steps, but with the IP and gravitysync user password for the primary Pi-hole instance. Once that's done, then go back to your primary instance to run a couple more commands.


On the primary Pi-hole, run the following command.

gravity-sync push

This will execute various checks, backups, and updates. The two Pi-hole instances should be in sync at this point.

Finally, run the following command to set up a job to periodically sync your Pi-hole instances.

gravity-sync auto

It will look something like this.

Now both of the Pi-hole instance will remain in sync as you make changes to blocklists and other configuration items.


Mobile Management

One of the somewhat annoying things with running a Pi-hole is temporarily needing to disable blocking in order to access something that would otherwise be blocked. There are several ways to disable and re-enable blocking, but I'm currently using RocketScience IT's Pi-hole Remote. This options provides a clean, intuitive app if you have Apple products. It does cost $3.99, but if you're an iOS and MacOS user like me, you'll likely find that it's well worth cost.


Simply download the application, provide any/all of your Pi-hole's API tokens, located in the Admin Web Interface > settings > API > show API token. Providing the app with the API token will allow you to enable and disable blocking. Additionally, you can provide your Admin Web Interface password if you want to monitor CPU, RAM, and temperature. Once that information is provided you'll see your Pi-hole instances appear in the app within a few moments.


From here you can view your logs, tail live events, and disable or enable blocking, among other nice-to-have features. Overall, it's a great way to quickly manage your Pi-hole setup without having to manually log into the web interface or SSH each time.


Conclusion

Pi-hole is a great way to block those annoying ads and potentially nefarious domains. Combined with Unbound, it provides a great security and privacy tool by putting DNS queries in your own control. There are any amazing features to explore and configure. Hopefully, some of the additional items I provided can help you on your Pi-hole journey.



13 views0 comments

Recent Posts

See All

Kommentare


© Evan Digs - 2024 - Created by Evan Powell.

  • LinkedIn
bottom of page