-
-
Notifications
You must be signed in to change notification settings - Fork 46
Unbound DNS
Setting up Unbound DNS with a Pi-hole installation allows you to operate your own tiny, recursive DNS server instead of relying on (and sending data to) the big players like Google or Cloudflare.
Unbound is a validating, recursive, caching DNS resolver. It is designed to be fast and lean and incorporates modern features based on open standards.
To install Unbound on the Raspberry Pi:
sudo apt install unbound
Note: If you've installed Pi-hole first and then Unbound as I have here, you might see some errors during installation of the latter, specifically Job for unbound.service failed because the control process exited with error code.
After some research, I decided to continue on with the Pi-hole guide to setting up Unbound, which seemed to work fine. I'm not sure what causes the errors, but I've decided not to investigate further at this time.
Despite the error(s), you can check to see if Unbound was installed successfully (and the version that was installed) using the following command:
unbound -V
Next we'll need to download a root.hints
file to replace the built-in root server hints:
wget -O root.hints https://www.internic.net/domain/named.root
Then move the root.hints
file to the Unbound configuration directory:
sudo mv root.hints /var/lib/unbound/
Unbound includes a lot of different configuration options that you can adjust and try out. Feel free to scan the Unbound configuration file documentation for details about each option. You can also check the latest Unbound documentation for more details.
To get started, edit the Unbound configuration file for Pi-hole:
sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
and remove the contents of the file (there is likely nothing there yet) before copying and pasting the contents of the sample pi-hole.conf configuration file in this repository. When you're done, exit and save the file.
The default port for Unbound is 53
but we're changing it to 5353
here. Feel free to change it to whatever you like, but you'll need to remember it later when we tell Pi-hole where to send upstream DNS requests:
port: 5353
This points to the root.hints
file you just downloaded:
# Use this only when you downloaded the list of primary root servers!
root-hints: "/var/lib/unbound/root.hints"
Here we're refusing connections to all interfaces and then we're allowing anything from this device (your Raspberry Pi) and anything from our local subnet (be sure to change 192.168.x.0
to whatever your local subnet is):
# IPs authorized to access the DNS Server
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.1 allow
access-control: 192.168.x.0/24 allow
If your router creates a "Guest Network" with a separate SSID and DHCP range, devices connecting to that wireless network will not be able to connect to the internet unless you grant access to that subnet. Uncomment one of the lines below or add your own based on your guest network's DHCP range:
# If you have a guest network with a separate DHCP range
#access-control: 172.16.1.0/24 allow
#access-control: 10.0.0.0/24 allow
Note: Devices connected to an AirPort guest network will not be able to use Pi-hole since your Raspberry Pi is on a separate DHCP range. They also will not have access to a DNS resolver, so they won't be able to connect to much of anything unless each device is set to manually connect to an outside DNS server, such as 1.1.1.1
.
You can adjust the cache settings if you like. Instead of the default of not caching, here we set the minimum TTL (Time To Live) to 1 hour, afterwards the DNS will do another lookup of the cached data:
# Time To Live (in seconds) for DNS cache. Set cache-min-ttl to 0 remove caching (default).
# Max cache default is 86400 (1 day).
cache-min-ttl: 3600
cache-max-ttl: 86400
When Pi-hole was doing DNS, it created this custom record for http://pi.hole so we could easily reach the Web Interface without typing in the static IP address of the Raspberry Pi. Now that Unbound is our DNS, we'll need to create this custom record in the Unbound configuration file. Be sure to replace 192.168.x.x
with the static IP address of your Raspberry Pi:
# Create DNS record for Pi-hole Web Interface
private-domain: "pi.hole"
local-zone: "pi.hole" static
local-data: "pi.hole IN A 192.168.x.x"
Once the configuration file is saved, use the unbound-checkconf
command to quickly check your unbound.conf
for errors. The console should return:
unbound-checkconf: no errors in /etc/unbound/unbound.conf
Otherwise, check your configuration file and make sure there aren't any extra line breaks, missing colons, or other bad characters.
Now that we have a customized configuration file, start the Unbound DNS server in the background:
sudo service unbound start
Then use dig
(a command-line tool to query a nameserver for DNS records) to make sure the Unbound DNS is running (be sure to use the port you set above):
dig pi-hole.net @127.0.0.1 -p 5353
Which should return some information, including a QUESTION SECTION
and an ANSWER SECTION
that includes pi-hole.net
and an IP address. For example:
; <<>> DiG 9.16.37-Debian <<>> pi-hole.net @127.0.0.1 -p 5353
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47691
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1472
;; QUESTION SECTION:
;pi-hole.net. IN A
;; ANSWER SECTION:
pi-hole.net. 3600 IN A X.XX.XXX.XX
;; Query time: 91 msec
;; SERVER: 127.0.0.1#5353(127.0.0.1)
;; WHEN: Sun Feb 19 09:03:06 EST 2023
;; MSG SIZE rcvd: 56
To make sure the Unbound DNS server is secure, use the dig
command to verify DNSSEC records. Cloudflare has a set of tools for doing just that, including the brokendnssec.net domain.
To check a domain with DNSSEC enabled (Cloudflare in this case), run the following command with dig
:
dig cloudflare.com @127.0.0.1 -p 5353
Which should return a status of NOERROR and an ANSWER SECTION
:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: XXXXX
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
But if we run dig
with a domain that doesn't have DNSSEC enabled (another Cloudflare domain for testing purposes):
dig brokendnssec.net @127.0.0.1 -p 5353
This should return a status of SERVFAIL and no ANSWER SECTION
:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: XXXXX
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
For more on DNSSEC and additional testing you can do, check out Cloudflare's Troubleshooting DNSSEC information.
Now that your Unbound recursive DNS resolver is running locally, we'll force Pi-hole to use it for DNS rather than an outside source like Google (8.8.8.8) or Cloudflare (1.1.1.1) and keep all of our network traffic contained. Any traffic on your network will be sent to Pi-hole, which in turn will use Unbound for DNS resolution before blocking the appropriate domains and returning data.
Open the Pi-hole Web Interface in a web browser while connected to your local network. Then go to Settings > DNS and uncheck any third party Upstream DNS Servers you had selected during setup. Then check the Custom 1 (IPv4) box and then add the following custom DNS Server IP and port to the text input below it:
127.0.0.1#5353
Where 127.0.0.1
points the Pi-hole server (the Raspberry Pi) to itself on port 5353
. If you changed the port in your Unbound configuration file, use that port here instead.
Next, uncheck the Never forward non-FQDN A and AAAA queries and Never forward reverse lookups for private IP ranges options, as they may interfere with accessing local hostnames since Pi-hole is not going to be used as our DHCP server. Do not check Use DNSSEC as Pi-hole will be using your Unbound DNS server, which already enables DNSSEC (Domain Name System Security Extensions).
Then check Use Conditional Forwarding and adjust the following settings:
- For
Local network in CIDR notation
, follow the Classless Inter-Domain Routing method by specifying the IP addresses available on your network, which in this case is likelyxxx.xxx.xxx.1
throughxxx.xxx.xxx.255
. The notation in this case should be192.168.1.0/24
if your router's IP is192.168.1.1
- Enter your router’s IP address in (typically something like
x.x.x.1
on your subnet) in theIP address of your DHCP server (router)
text input - Optionally add your local domain name (this can sometimes be set on your router, usually under the DHCP settings, to something like
home
)
When you're done, don't forget to save your settings.
To ensure that Unbound is configured correctly, visit this DNSSEC Tools test in a web browser using a device that's currently within your network and using your Raspberry Pi as a DNS server. If you see a thumbs up image, Unbound is validating DNSSEC signatures properly.
If your Raspberry Pi setup includes WireGuard VPN functionality, the Unbound service may not start properly when you reboot your Pi. Likely this is due to WireGuard not completing its setup on restart before Unbound tries to finish its own. In order to force Unbound to wait for WireGuard, edit the unbound.service
file:
sudo nano /etc/systemd/system/multi-user.target.wants/unbound.service
And modify the After=network.target
line to read:
After=network.target [email protected]
Save the file and exit nano. Reboot your Raspberry Pi with sudo reboot
and check to make sure Unbound is running once it's back online:
dig pi-hole.net @127.0.0.1 -p 5353
Note: You can always manually start the Unbound DNS service with sudo service unbound start
.