Unbound Plus
As detailed in a previous post, I'm running Unbound as local DNS resolver. I've configured it to use DNS over TLS, and while things were a little shaky with the few available servers supporting this security protocol in the beginning, I didn't need to switch back to plain DNS for at least a year. And I'm not using the commercial providers that have decided to jump on the bandwagon, namely, Google (8.8.8.8), Cloudflare (1.1.1.1), and Quad9 (9.9.9.9). I wouldn't touch them except for testing purposes.
However, my initial motive to run a local resolver was not security or privacy, but latency, and DNS over TLS, being based on TCP instead of UDP as plain DNS, definitely doesn't help with that. In fact, unencrypted queries over UDP are generally way faster than encrypted ones over TCP, but the actual latency can strongly vary depending on the server queried.
Dnsperf is the standard tool for measuring the performance of an authoritative DNS server, but it doesn't support TCP, and the patched version is seriously outdated. Flamethrower is a brand-new alternative looking very promising, but I've got inconsistent results from it (I'm pretty sure that was entirely my fault).
The standard DNS query tools dig (part of bind) and drill (part of ldns) don't support TLS, but kdig (part of knot) supposedly does. An alternative is pydig, which I've used already two years ago to check if an authoritative server offers DNS over TLS, and which turned out to be just as helpful in determining the latency of a list of DNS servers (one IP per line). After updating ('git pull origin master'), I've fed this list (called, let's say, dns-servers.txt) to pydig using
while read p; do ./pydig @$p +dnssec +tls=auth ix.de | grep 'TLS response' | awk '{print substr($0, index($0, $10))}'; done < dns-servers.txt
with an explicit (+) requirement for DNSSEC and TLS (or without for plain DNS).
I've got a few really interesting results this way. For example, Cloudflare is invariably the fastest service available, with a latency of 9 and 60 ms for plain and encrypted UDP queries here at home, respectively. From pdes-net.org, the situation is different: Cloudflare takes 4 and 20 ms, while dnswarden returns results within 1 and 9 ms, respectively. Insanely fast!
This latter service (where the hell did it come from all of a sudden?) is also very competitive compared to Google or Quad9 here at home: all of them require about a 100 ms to answer TLS requests. That seems terribly slow, but it's not as bad as it sounds. First, I've configured Unbound to be a caching resolver, so many, if not most, requests are answered with virtually zero latency. Second, I minimize external requests by making the root zone local – which is also known as the hyperlocal concept.
Due to this added functionality, I've found it necessary to revamp the configuration. All main and auxiliary configuration files of my current Unbound installation are attached below.
Main configuration files
/etc/unbound/...
.../unbound.conf
include: "/etc/unbound/unbound.conf.d/*.conf"
.../unbound.conf.d/01_Basic.conf
server:
verbosity: 1
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
use-syslog: yes
do-daemonize: no
username: "unbound"
directory: "/etc/unbound"
root-hints: root.hints
#trust-anchor-file: trusted-key.key
auto-trust-anchor-file: trusted-key.key
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes
minimal-responses: yes
prefetch: yes
qname-minimisation: yes
rrset-roundrobin: yes
use-caps-for-id: yes
## reduce edns packet size to help big udp packets over dumb firewalls
#edns-buffer-size: 1232
#max-udp-size: 1232
cache-min-ttl: 3600
cache-max-ttl: 604800
include: /etc/unbound/adservers
.../unbound.conf.d/02_Forward.conf
server:
interface: ::0
interface: 0.0.0.0
access-control: ::1 allow
access-control: 2001:DB8:: allow
#access-control: fd00:aaaa:bbbb::/64 allow
access-control: 192.168.178.0/16 allow
verbosity: 1
ssl-upstream: yes
forward-zone:
# forward-addr format must be ip "@" port number "#" followed by the valid public
# hostname in order for unbound to use the tls-cert-bundle to validate the dns
# server certificate.
name: "."
# Servers support DNS over TLS, DNSSEC, and (partly) QNAME minimization
# see https://dnsprivacy.org/jenkins/job/dnsprivacy-monitoring/
### commercial servers for tests
#forward-addr: 1.1.1.1@853 #cloudflare-dns.com
#forward-addr: 8.8.8.8@853 #dns.google
#forward-addr: 9.9.9.9@853 #dns.quad9.net
### fully functional (ordered by performance)
forward-addr: 116.203.70.156@853 #dot1.dnswarden.com
forward-addr: 116.203.35.255@853 #dot2.dnswarden.com
#forward-addr: 185.49.141.37@853 #getdnsapi.net
#forward-addr: 185.95.218.43@853 #dns.digitale-gesellschaft.ch
#forward-addr: 146.185.167.43@853 #dot.securedns.eu
#forward-addr: 145.100.185.15@853 #dnsovertls.sinodun.com
#forward-addr: 145.100.185.16@853 #dnsovertls1.sinodun.com
#forward-addr: 46.182.19.48@853 #dns2.digitalcourage.de
#forward-addr: 80.241.218.68@853 #fdns1.dismail.de
#forward-addr: 89.233.43.71@853 #unicast.censurfridns.dk
### temporarily (2019/11/05) or permanently broken
#forward-addr: 145.100.185.17@853 #dnsovertls2.sinodun.com
#forward-addr: 145.100.185.18@853 #dnsovertls3.sinodun.com
#forward-addr: 158.64.1.29@853 #kaitain.restena.lu
#forward-addr: 199.58.81.218@853 #dns.cmrg.net
##forward-addr: 81.187.221.24@853 #dns-tls.bitwiseshift.net
##forward-addr: 94.130.110.185@853 #ns1.dnsprivacy.at
##forward-addr: 94.130.110.178@853 #ns2.dnsprivacy.at
#forward-addr: 89.234.186.112@853 #dns.neutopia.org
.../unbound.conf.d/03_Performance.conf
# https://www.unbound.net/documentation/howto_optimise.html
server:
# use all cores
num-threads: 8
# power of 2 close to num-threads
msg-cache-slabs: 8
rrset-cache-slabs: 8
infra-cache-slabs: 8
key-cache-slabs: 8
# more cache memory, rrset=msg*2
rrset-cache-size: 200m
msg-cache-size: 100m
# more outgoing connections
# depends on number of cores: 1024/cores - 50
outgoing-range: 100
# Larger socket buffer. OS may need config.
so-rcvbuf: 8m
so-sndbuf: 8m
# Faster UDP with multithreading (only on Linux).
so-reuseport: yes
.../unbound.conf.d/04_Rootzone.conf
# “Hyperlocal“ configuration. # see https://forum.turris.cz/t/undbound-rfc7706-hyperlocal-concept/8761 # furthermore # https://forum.kuketz-blog.de/viewtopic.php?f=42&t=3067 # https://tools.ietf.org/html/rfc7706#appendix-A # https://tools.ietf.org/html/rfc7706#appendix-B.1 # https://www.iana.org/domains/root/servers auth-zone: name: . for-downstream: no for-upstream: yes fallback-enabled: yes #master: 198.41.0.4 # a.root-servers.net master: 199.9.14.201 # b.root-servers.net master: 192.33.4.12 # c.root-servers.net #master: 199.7.91.13 # d.root-servers.net #master: 192.203.230.10 # e.root-servers.net master: 192.5.5.241 # f.root-servers.net master: 192.112.36.4 # g.root-servers.net #master: 198.97.190.53 # h.root-servers.net #master: 192.36.148.17 # i.root-servers.net #master: 192.58.128.30 # j.root-servers.net master: 193.0.14.129 # k.root-servers.net #master: 199.7.83.42 # l.root-servers.net #master: 202.12.27.33 # m.root-servers.net master: 192.0.47.132 # xfr.cjr.dns.icann.org master: 192.0.32.132 # xfr.lax.dns.icann.org zonefile: "root.zone"
Auxiliary configuration files
/etc/cron.weekly/...
.../adserver_updates
#!/bin/bash
# Updating Unbound resources.
# Place this into e.g. /etc/cron.weekly
###[ adservers ]###
curl -sS -L --compressed -o /etc/unbound/adservers.new "`https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext <https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext>`_"
if ` $? -eq 0 <>`_; then
mv /etc/unbound/adservers /etc/unbound/adservers.bak
mv /etc/unbound/adservers.new /etc/unbound/adservers
unbound-checkconf >/dev/null
if ` $? -eq 0 <>`_; then
rm /etc/unbound/adservers.bak
systemctl restart unbound.service
else
echo "Warning: Errors in newly downloaded adservers file probably due to incomplete download:"
unbound-checkconf
mv /etc/unbound/adservers /etc/unbound/adservers.new
mv /etc/unbound/adservers.bak /etc/unbound/adservers
fi
else
echo "Download of unbound adservers failed!"
fi
.../roothint_updates
#!/bin/bash
# Updating Unbound resources.
# Place this into e.g. /etc/cron.weekly
###[ root.hints ]###
curl -sS -L --compressed -o /etc/unbound/root.hints.new `https://www.internic.net/domain/named.cache <https://www.internic.net/domain/named.cache>`_
if ` $? -eq 0 <>`_; then
mv /etc/unbound/root.hints /etc/unbound/root.hints.bak
mv /etc/unbound/root.hints.new /etc/unbound/root.hints
unbound-checkconf >/dev/null
if ` $? -eq 0 <>`_; then
rm /etc/unbound/root.hints.bak
systemctl restart unbound.service
else
echo "Warning: Errors in newly downloaded root.hints file probably due to incomplete download:"
unbound-checkconf
mv /etc/unbound/root.hints /etc/unbound/root.hints.new
mv /etc/unbound/root.hints.bak /etc/unbound/root.hints
fi
else
echo "Download of unbound root.hints failed!"
fi
/etc/systemd/system/unbound.service.d
I've discarded my custom snippet for systemd to get the DNS anchor. Archlinux does provide the anchor automatically as a dependency of unbound (dnssec-anchors), so why complicate things. For other distributions, however, the snippet may still be useful, so here it is:
[Service] ExecStartPre=sudo -u /usr/bin/unbound-anchor -a /etc/unbound/trusted-key.key