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