DNS queries over TLS

Following on from my previous post on Unbound, this post is going to look at encrypting DNS traffic using TLS.

Standard DNS traffic

DNS traffic is normally sent unencrypted over port 53. tcpdump can be used to demonstrate this:

$ tcpdump -nX host 1.1.1.1 or host 1.0.0.1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), capture size 262144 bytes
22:55:09.183176 IP 10.0.2.15.38783 > 1.1.1.1.domain: 58055+ A? example.com. (29)
        0x0000:  4500 0039 8545 0000 4011 e75e 0a00 020f  E..9.E..@..^....
        0x0010:  0101 0101 977f 0035 0025 0e47 e2c7 0100  .......5.%.G....
        0x0020:  0001 0000 0000 0000 0765 7861 6d70 6c65  .........example
        0x0030:  0363 6f6d 0000 0100 01                   .com.....
22:55:09.210108 IP 1.1.1.1.domain > 10.0.2.15.38783: 58055 1/0/0 A 93.184.216.34 (45)
        0x0000:  4500 0049 01bf 0000 4011 6ad5 0101 0101  E..I....@.j.....
        0x0010:  0a00 020f 0035 977f 0035 9e04 e2c7 8180  .....5...5......
        0x0020:  0001 0001 0000 0000 0765 7861 6d70 6c65  .........example
        0x0030:  0363 6f6d 0000 0100 01c0 0c00 0100 0100  .com............
        0x0040:  000d 9b00 045d b8d8 22                   .....].."

In the example above two packets are displayed, the request sent to look up example.com, followed by the response from the remote DNS server. Anyone with access to the network will be able to read both the request and response.

Encrypting traffic

In the previous post on Unbound, the following configuration was used to forward traffic to CloudFlare's DNS servers:

forward-zone:
        name: "."
        forward-addr: 1.1.1.1
        forward-addr: 1.0.0.1

CloudFlare's DNS servers can also be accessed via TLS. To do this the configuration above can be modified as follows:

forward-zone:
        name: "."
        forward-addr: 1.1.1.1@853#cloudflare-dns.com
        forward-addr: 1.0.0.1@853#cloudflare-dns.com
        forward-ssl-upstream: yes

In the configuration above @853 specifies the port and #cloudflare-dns.com specifies the hostname which should be validated when the TLS connection is established. Unfortunately name verification was only recently added (bug 658); so if you're using an earlier version of Unbound #cloudflare-dns.com will be ignored.

Once the Unbound configuration has been updated, restart the service with the following command:

systemctl restart unbound.service

Any queries made by unbound will now be sent over an encrypted TLS connection, tcpdump can be used to verify this:

10.0.2.15.36284 > 1.1.1.1.853: Flags [S], cksum 0x0e3f (incorrect -> 0x0b08), seq 574903500, win 29200, options [mss 1460,sackOK,TS val 701611 ecr 0,nop,wscale 7], length 0

Latency

From a privacy perspective encrypting DNS traffic is great, unfortunately there is a down side. Unlike DNS using UDP packets, DNS over TLS requires time to establish a TLS connection before making the query. As a result initial queries can take over a second:

$ time host example.com 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases:

example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

real    0m1.559s
user    0m0.009s
sys     0m0.004s

Compared to a fraction of a second for queries over UDP:

$ time host example.com 1.1.1.1
Using domain server:
Name: 1.1.1.1
Address: 1.1.1.1#53
Aliases:

example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

real    0m0.288s
user    0m0.007s
sys     0m0.006s