Aug 03, 2018

DNS Privacy with TLS

Fend yourself against pervasive monitoring, response manipulation1 and at best ISPs selling your nameserver usage data. As the Let's Encrypt programme, the DnsPrivacy initiative offers to fill a gaping hole. There are different approaches that will probably coexist and each have pros and cons. One of the DoT developers gives an excellent introduction in her talk(slides) from IETF99. It also discusses rfc 7626, a short document outlining the privacy risks associated with DNS traffic. The DoH outlook is an interesting read and somebody said "Resolverless DNS"2. To follow the DPRIVE ietf working group will keep you updated. As it is only a simple change in a mostly vanilla OpenWrt, I opted for DNS-over-TLS.

In a first step we tell dnsmasq to not query the ISP nameservers anymore. Instead it will relay to stubby and its getdns utility, forwarding the query to one of the configured nameservers capable of transport encryption3.

Somebody did the work and stubby for OpenWrt (even with uci, thanks!). Consult the readme and decide what options you want to set, balancing privacy and speed.

opkg install stubby
opkg install getdns
opkg install ca-certificates

Depending if you already use libopenssl and ca-certificates, this will use 400 - 1600 KB.

in /etc/config/dhcp add the stubby port you will forward to and edit the resolvfile, then restart dnsmasq. Make sure to have only 127.0.0.1 in the given resolvfile or use option noresolv '1'.

config dnsmasq
       ...
       option resolvfile '/etc/resolv.conf' 
       list server '127.0.0.1#5453'

in /etc/config/stubby check tls_authentication to be required with no transport fallback to anything else than tls.

config stubby 'global'
       list dns_transport 'GETDNS_TRANSPORT_TLS'
       option tls_authentication '1' 

DNSSEC (optional)

If you want to use this feature, you have to install dnsmasq-full to do validation. The proxy-dnssec config flag can reply to validating clients downstream without doing it itself though. Remember: your dnsclient (in every system) has to do validation too, or the BOGUS reply by a validating forwarder upstream will be taken for a proper return. I don't conceptionally understand yet why I can't configure stubby or dnsmasq return an error to dnssec-incapable clients when the validation fails.

config stubby 'global'
       option dnssec_return_status '1'
       option dnssec_trust_anchors '/var/lib/stubby/getdns-root.key'

Current getdns (1.2+) will fetch the trust anchor4 by itself initially if missing. dnsmasq-full enables the dnssec options in /etc/config/dhcp and has the anchor present implicitly.

config dnsmasq
       option dnssec '1'
       option dnsseccheckunsigned '1' # some perf penalty on this

I'm not sure if a small router CPUs openssl speed is a major parameter in latency, or if it's just the additional network packets.

Upstream nameserverss

To use DoT servers close to you, check the network path and latency with mtr. It won't say how quickly your queries will be answered though on average. The servers are listed online and in /etc/stubby/stubby.yml.default. Globally, Cloudflare is often close and would be an easy config, but as nonprofit and EU-based nameservers are within 25-35 ms for me, I went with dot.securedns.eu (privately run) and getdnsapi.net (NLlabs). Of the nameservers that are closer, digitalcourage.de has DoT planned for rollout soon, as250.net has it on their roadmap and from dnscache.berlin.ccc.de I haven't heard back yet. Randomization of queries is another concern, but for now I checked latency only. When I couldn't explain some slow queries I even disabled roundrobin temporarily until I can benchmark this better (because of TLS negotiation? the nameserver didn't have the domain cached? DNSSEC validation?). With roundrobin disabled, stubby will use a nameserver until it gets unavailable.

config stubby 'global'
       option round_robin_upstreams '1'
# check on tls pubkey pinset:
# echo | openssl s_client -connect $ip:$port -servername $domain 2>/dev/null \|
#        openssl x509 -pubkey -noout | openssl pkey -pubin -outform der \|
#        openssl dgst -sha256 -binary | openssl enc -base64
# DIGITALCOURAGE
config resolver
       option address '46.182.19.48'
       option tls_auth_name 'dns2.digitalcourage.de'
       #list spki 'sha256/v7rm6OtQQD3x/wbsdHDZjiDg+utMZvnoX3jq3Vi8tGU='

# SECUREDNS
config resolver
       option address '146.185.167.43'
       option tls_auth_name 'dot.securedns.eu'
       #list spki 'sha256/h3mufC43MEqRD6uE4lz6gAgULZ5/riqH/E+U+jE3H8g='

# GETDNSAPI
config resolver
       option address '185.49.141.37'
       option tls_auth_name 'getdnsapi.net'
       #list spki 'sha256/foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q='

Reload stubby and dnsmasq

/etc/init.d/stubby reload
/etc/init.d/dnsmasq reload

That's it, almost no5 DNS related plaintext data going through your uplink anymore. Though this moves the trust to another upstream to neither leak, log or even sell the query data, it's a conscious decision which nameservers you trust, the address is now verified, queries not easily seen and DNSSEC signature checked at your local device too. I recommend to set it up on any mobile device6. Your general network activity is still visible, only a VPN or similar can help to move the exit point of that metadata.

Benchmark

Preliminary tests with dnsdiag show a delay of 40+ ms for DNSSEC enabled TLS queries to 1.1.1.1, ~15 ms total without DNSSEC - the others are udp dns queries, no DNSSEC. Run with dnseval.py -f resolv.conf -c 100 old.reddit.com (pipenv helps with dnseval install).

server avg(ms) min(ms) max(ms) stddev(ms) lost(%) ttl
1.1.1.1 (tls+dnssec) 48 45 261 21 %0 246
1.1.1.1 (tls) 12 11 49 3 %0 88
1.1.1.1 6 5 32 2 %0 242
9.9.9.9 8 5 177 17 %0 299
8.8.8.8 21 19 24 2 %0 273

Global lists do not help too much, check your network path and make your own observations about availability.

Debug

To have a historical look at timestamps and delays, I temporarily run dnsmasq with logging when diagnosing latencies. Those can be seen via logread.

/etc/dnsmasq.conf:
log-queries=extra
log-async=20
dnssec-debug

stubby itself has a logging mode (-l), and brings along its query tool, have a look at its options (-h)

stubby -C /etc/stubby/stubby.yml  -l
getdns_query @1.1.1.1 +dnssec_return_all_statuses www.sinodun.com a

See which resolver you're currently using

dig -t txt resolver.dnscrypt.info # or
nslookup -type=TXT resolver.dnscrypt.info

if bind-dig is compiled with -DDIG_SIGCHASE it can do dnssec validation

dig +dnssec +sigchase -t a dnssec-failed.org @127.0.0.1 

test online or use their instructions, but make sure if to enable validation on your machine too

nslookup sigok.verteiltesysteme.net 127.0.0.1 # should return A record
nslookup sigfail.verteiltesysteme.net 127.0.0.1 # should return SERVFAIL

With dnsmasq log-queries=extra set, you can make dnsmasq output statistics and cache contents to syslog/logread. See this pi-hole ftl thread and what they have via their api

killall -s SIGUSR1 dnsmasq
...
cache size 150, 364/2744 cache insertions re-used unexpired cache entries.

there's a CHAOS query class? dnsmasqs manual page has it, querying for stats:

for domain in cachesize.bind insertions.bind evictions.bind misses.bind hits.bind auth.bind servers.bind;
    do echo -n "$type: "; dig +short -c CHAOS -t TXT $domain;
done

Recursive DNS

Doing your own recursive DNS querying and caching is another topic. You'll lose "hiding" behind a DNS used by many and its caching pool for quick anwers. If the TLD zone and authorative nameservers do not offer transport encryption your queries are again in the open for "Eve". But you can configure options like QNAME minimisation to avoid data leakage to nameservers before the authorative. I wonder to which degree query times increase, as without prefetching your own resolver will have an unfilled cache. Another addition could be to serve the root-zones yourself (rfc7706), but this will only spare you the first in at least three hops. An AXFR for a complete TLD zone could help skip the second too. Somehow "subscribing" to prefered domains, but this is as far as I know not on the horizon.

ObliviousDNS tries to decouple "Who is asking?" from "What is queried for?". Though keep in mind: after the name resolution, subsequent traffic will give a passive global eavesdropper metadata anyway. Using transport encryption for more protocols aims at raising costs for "Eve" as DNS today is still too cheap to mine.

  • use pi-holes FTLDNS for blocking before handing to dnsmasq, or use an adblocking DNS upstream as as250.net offers one
  • though dnsmasq can use a larger "cache-size" too, Unbound has more knobs, can do DoT and blocklists
  • beyond the scope of "DNS Privacy" but nonetheless relevant, the The DNS Camel talk laments how rising complexity in DNS makes the standards body loosing touch with the practioneers

  1. see link in HN discussion and the mentioned T-Mobile DNS hijack 

  2. ietf mailarchive resolverless-dns 

  3. feature list of the dns-privacy nameservers 

  4. on a tangent: there was the convergence.io(2011) effort, implemented as browser extension. "Do other persons get the same (SSL certificate)/answer that I do?". I think this was a interesting approach opposite hierachical trust models (CAs) and it was usable for https for quite some time 

  5. there's the unsettled case for encrypted Server Name Indication with TLS, if anybody wants to block the destination network-wide based on SNI 

  6. to get to captive portals, you might have to disable custom nameservers temporarily