At some point the idea of hosting your own recursive DNS was a widely talked about subject. It would add security and privacy to your connection by avoiding things like DNS hi-jacking, which has become common among ISPs.
But I mostly wanted it to fix a problem with something; despite being told this was a stupid idea.
The root of this problem is in my IPv6 connectivity. The tunnel I’m provided suffers from some major performance issues, many of which some users don’t report experiencing. The only time I notice it is when attempting to upload files to my VPS; the upload speed is absolutely horrible.
But what actually planted this seed was a change in the behavior of Windows security. For the longest time I was blocking telemetry by dummying the IPs in
windows\system32\drivers\etc\hosts, this is also where I initially added the IPv4 resolutions for a number of local resources and my VPS. But now Defender/whatevertheycallit treats modifying the hosts file as a major security breach and will gladly restore the blank version for you. You can disable this; but it also disables all the other file system security checks.
Yeah…I know…this is a really stupid reason to setup recursive DNS; but I’m going to do it anyway. Sometimes I like going “hard no” to changing something and finding a solution around it. So, I started looking at how I could make this happen. I read in to various examples for self-hosted recursive DNS and tried to find explanations on how I might do specific tasks with those setups. It was during all of this that, on a tangent, I found out you could run Pi-Hole on a standard Ubuntu install.
Pi-Hole on Xen
I thought about just running this on the network VM that’s doing all the other network stuff; but because of how Pi-Hole wanted to screw with the network settings, I decided to isolate it entirely. I gave it a small vm:
- 4 vCPU
- 512MB ram
- 5GB storage
The only key minimum is the amount of RAM. I’ve been trying to thin out my bloated RAM assignments for some VMs that don’t do much; so I wanted to start this with the absolute minimum. 4 cores are probably overkill, but that’s fine. None of the VMs are very busy, so the overprovisioning of my own systems if fine by me.
Pi-Hole Installation Was Stupidly Simple
wget -O basic-install.sh https://install.pi-hole.net sudo bash basic-install.sh
These two commands taken from the pi-hole install page are all that’s required on a fresh system after a
dist-upgrade. I found no reasons to deviate from all the defaults during install, so it was largely just pressing enter a bunch of times. I had already set the VM’s DHCP static assignment, but it does warn you about choosing to use the existing DHCP assignment as a static IP causing possible problems in the future. Of course, the IP assigned by DHCP was outside of it’s standard pool anyway; but only I knew that. Make sure you copy the admin password that’s presented on the screen. I usually log in to the admin panel so my browser can save it, but I’m also lazy.
One of the nice things about the blocked domain lists that gravity uses is it’s in the same format as a normal hosts file. I had this crazy idea, because I forgot to check a forum posting date, that anything I put in /etc/hosts on the pi-hole OS would be returned through pi-hole; that was how it used to work and things were different now. Gravity does quite literally use the same format, so making an “adlist” of my telemetry blocks was pretty simple. So this lead me back around to my original idea…how about running recursive DNS?
Unbound On Your Pi-Hole
In a move that would apparently make my life a bit easier, there’s actual documentation from Pi-Hole on running it as an “All-around DNS solution” with it’s own recursive server. They went with unbound, which makes me think of way too many bad puns. But as further research would tell me, doing what I wanted to do with unbound; which was adding my own records to return; was likely easier with unbound than with bind. While more interactive than the pi-hole install; this still largely turned out to be a “copy and paste” installation procedure:
sudo apt install unbound /etc/unbound/unbound.conf.d/pi-hole.conf: 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: yes # 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. # Suggested by the unbound man page to reduce fragmentation reassembly problems edns-buffer-size: 1472 # 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
I was totally mistaken about what the “prefer-ipv6” function did; but that’s fine. But other than that, this runs it on port 5335. So save, restart, and test:
sudo systemctl restart unbound
dewdude@pihole:~$ dig pi-hole.net @127.0.0.1 -p 5335 ; <<>> DiG 9.16.1-Ubuntu <<>> pi-hole.net @127.0.0.1 -p 5335 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42772 ;; 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. 1947 IN A 22.214.171.124 ;; Query time: 4 msec ;; SERVER: 127.0.0.1#5335(127.0.0.1) ;; WHEN: Sat Sep 19 15:45:11 EDT 2020 ;; MSG SIZE rcvd: 56
The instructions also include checking DNSSEC validation, which I’m not going to include here, but did pass them.
Host Overrides In Unbound
I found a lone blog entry that explained how to do host overrides better than anything else I read; and probably because it was someone doing it for the same reason I’m doing this…in case I need it later. unbound is configured to read any .conf file in
/etc/unbound/unbound.conf.d/, so the easiest thing to do was just create an overrides.conf file in that directory (though the name can be whatever you want)
local-zone: pickmy.org transparent local-data: "pickmy.org A 126.96.36.199" local-data: "www.pickmy.org A 188.8.131.52" local-data: "img.pickmy.org A 184.108.40.206"
I’m still not 100% sure if I’ve done this correctly; for all I know my usage of local-zone is wrong. I do know it’s redundant, as it will declare local-zone as transparent by default. Doing it this way means the only thing we’ll ever get are the A records I define since “transparent” overrides all records for the domain; and there’s no NS records defined here. This isn’t a problem as I have just A and AAA records; and you’re looking at all the A records for pickmy.org I’ve defined.
For the time being I’m giving this a test on my own machine. I want to run it for a while and see if anything breaks. In a couple of weeks I’ll have the chance to enable it network-wide and test things while they’re unused for a couple of days. I do need to play around and see if eliminating the IPv6 DNS entries is a good idea; I thought v4 DNS wouldn’t return v6 addresses….but it’s happy to do so; meaning I don’t really needed dedicated v6.