Setting up a local Unbound ad-blocking DNS resolver
Table of contents
I started by making a new LXC container on my hypervisor.
$ lxc launch ubuntu:20.04 unbound -p lan $ lxc exec unbound -- /bin/bash
This creates a new Ubuntu 20.04 container,
unbound, on my LAN and connects to it with bash.
Then I did was I always do on new containers;
- Delete the
- Create my user and set a password
- Configure the timezone
- Update, upgrade and auto remove APT packages
- Create a
.sshfolder in my home directory
- Exit the container
# userdel -r ubuntu # useradd -m -G sudo -s /bin/bash hebron # passwd hebron # dpkg-reconfigure tzdata # apt update && apt upgrade && apt autoremove # su hebron $ cd $ mkdir ~/.ssh $ exit # exit
Next I pushed the
authorized_keys file on my hypervisor to the newly created container, this allows me to log in with the same SSH key that I am using to connect to the hypervisor. I set the container to auto start and SSHed into it.
$ lxc file push .ssh/authorized_keys unbound/home/hebron/.ssh/ $ lxc config set unbound boot.autostart true $ ssh unbound
Now to the good stuff, getting Unbound up and running.
First we need to stop and disable
systemd-resolved.service, because it uses port 53, and we want Unbound on that port.
$ sudo systemctl disable systemd-resolved.service $ sudo systemctl stop systemd-resolved
Then I deleted
/etc/resolv.conf and made a new one:
nameserver 127.0.0.1 search lan.uctrl.net options trust-ad
$ sudo apt install unbound
include: "/etc/unbound/unbound.conf.d/*.conf" # Allow unbound-control remote-control: control-enable: yes server: use-syslog: yes # Refresh expiring cache entries, if less than 10% of their TTL remains prefetch: yes # Serve stale data if not older than 1 day serve-expired: yes serve-expired-ttl: 86400 # one day, in seconds # Timeout request and serve stable data if it takes longer than 1.8 seconds serve-expired-client-timeout: 1800 # 1.8 seconds, in milliseconds # Some logging to see what is going on verbosity: 2 log-queries: yes interface: 0.0.0.0 interface: ::0 # Who can use our Unbound server access-control: 127.0.0.1/8 allow access-control: ::1/64 allow access-control: 192.168.0.0/16 allow do-ip4: yes #do-ip6: yes do-udp: yes do-tcp: yes # Certificates tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt # Our local domain can fail DNSSEC validation domain-insecure: "lan.uctrl.net." # Allow plex.direct to return local addresses, otherwise Plex will fail private-domain: "plex.direct" # Ad-blocking, leave this commented out for now... #include: /etc/unbound/adservers.conf # Local zone entries should be transparent local-zone: "lan.uctrl.net." transparent local-zone: "168.192.in-addr.arpa." transparent #include: /etc/unbound/host_entries.conf # All queries to the local zone can go to the Edgerouter instead forward-zone: name: "lan.uctrl.net." forward-addr: 192.168.1.1 forward-zone: name: "168.192.in-addr.arpa." forward-addr: 192.168.1.1 # Forward all queries to Quad9 and CloudFlare, with DNS over TLS forward-zone: name: "." forward-tls-upstream: yes # Quad9 #forward-addr: 2620:fe::fe@853#dns.quad9.net forward-addr: 220.127.116.11@853#dns.quad9.net #forward-addr: 2620:fe::9@853#dns.quad9.net forward-addr: 18.104.22.168@853#dns.quad9.net # Cloudflare DNS #forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com forward-addr: 22.214.171.124@853#cloudflare-dns.com #forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com forward-addr: 126.96.36.199@853#cloudflare-dns.com
When I tried to start Unbound with these settings — it failed… It didn’t understand the
serve-expired, I learned that this option was added in 1.11.0, and I had 1.9.4. Time to upgrade!
I upgraded Ubuntu to 20.10:
$ sudo apt update $ sudo apt upgrade $ sudo apt dist-upgrade $ sudo apt autoremove
To upgrade to a non-lts version, we need to make a configuration change:
$ sudo vim /etc/update-manager/release-upgrades
Now we can upgrade:
$ sudo do-release-upgrade
I now had Unbound 1.11.0, success!
And my configuration is no longer failing:
$ unbound-checkconf unbound-checkconf: no errors in /etc/unbound/unbound.conf
I put this script in
#!/bin/bash USEIP=127.0.0.1 set -e curl -sS -L --compressed \ "http://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext&useip=$USEIP" \ | tee /etc/unbound/adservers.conf unbound-control reload
unbound-control to work, we first have to set it up:
$ sudo unbound-control-setup
It is important that the cron script has the right owner and permissions:
$ sudo chown root:root /etc/cron.weekly/update_ads.sh $ sudo chmod +x /etc/cron.weekly/update_ads.sh $ ls -l /etc/cron.weekly/ -rwxr-xr-x 1 root root 234 Feb 28 23:33 update_ads.sh
Looks good, let’s run it:
$ sudo /etc/cron.weekly/update_ads.sh
Now we can uncomment the following line in
And restart Unbound:
$ sudo systemctl restart unbound
You now have an ad filtering, DNSSEC validating, TLS encrypted local DNS resolver. Congratulations!
To test the DNSSEC validation you can try:
$ dig www.dnssec-failed.org
This should fail.
To test the ad blocking, try a lookup on a blocked domain:
$ dig adservice.google.com
This should return 127.0.0.1, instead of the real IP address.
All DHCP clients have my Edgerouter defined as the DNS server, and it forwards non-local queries to the Unbound container.
Internet --- Edgerouter --- Client | | Unbound