/dev/posts/

DNS aggregation over TLS

Published:

Updated:

In a previous post, I tried different solutions for tunnelling DNS over TLS. One of those solutions was using a dedicated DNS-over-UDP fake service replying to all queries with the truncate flag set: this was causing the stub resolvers to retry the query using a TCP-based virtual-circuit. This solution is interesting because it is dead simple (it fits in a few line of codes) but it is clearly a hack. Here, I am using a dedicated DNS forwarder aggregating all the incoming DNS-over-UDP requests over a single persistent TCP virtual-circuit.

Update (2017-05-17): This was written before DNS over TLS was a thing and before it was natively implemented in resolvers. See DNS Privacy for up-to-date instructions.

Summary

The differents solutions presented in the previous post on the resolver side:

The last solution is using this protocol stack:

     DNSSEC valid.   TLS Init.
         cache       verify TLS

[DNS]<->[DNS   ]<------------------------>[DNS]
[   ]   [      ]<---->[  |TLS]<---------->[TLS]
[TCP]<->[TCP   ]<---->[TCP   ]<---------->[TCP]
[IP ]<->[IP    ]<---->[IP    ]<---------->[IP ]
Stub R.  Forwarder    TLS Init. Internet   Recursive
         (unbound)    (stunnel)

However, each DNS request is using a new TCP and TLS connection between the stub resolver and unbound. Between unbound and stunnel, each each incoming TCP connection stream is encapsulated in a new TLS connection. This is very inefficient and the resulting DNS service is not very robust.

Performance considerations for DNS over TLS are summarized in the TLS for DNS: Initiation and Performance Considerations draft (emphasis mine):

Latency: Compared to UDP, DNS-over-TCP requires an additional round-trip-time […]. The TLS handshake adds another two RTTs of latency.

State: The use of connection-oriented TCP requires keeping additional state in both kernels and applications. […]

Processing: […] slightly higher CPU usage.

Number of connections: clients SHOULD minimize creation of new TCP connections. Use of a local DNS request aggregator (a particular type of forwarder) allows a single active DNS-over-TLS connection from any given client computer to its server.

DNS aggregation over a persistent TCP connection

In order to fix this problem, I wrote a prototype DNS forwarder which aggregates all the local UDP-based DNS messages over a persistent TCP stream:

This service can then be coupled with a TLS initiator (stunnel) which encapsulates the persistent DNS stream over TCP.

In the future, the tool might have an option to talk TLS natively. My excuse for not adding builtin support for TLS was that it gives you the freedom of choosing which TLS implementation you would like to use (OpenSSL with stunnel or socat, GnuTLS with gnutls-serv and a tool such as socat with faucet, NSS but I do not know a suitable tool using this library, etc).

        VC encap. DNSSEC valid.    TLS
	Mux.        cache        TLS verify

[DNS]<->[DNS    ]<->[DNS   ]<---------------------->[DNS]
                                     [TLS]<-------->[TLS]
[UDP]<->[UDP|TCP]<->[TCP   ]<---->[TCP   ]<-------->[TCP]
[IP ]<->[IP     ]<->[IP    ]<---->[IP    ]<-------->[IP ]
Client  Aggregator  Forwarder     TLS Init. Internet  Recursive
         (dnsfwd)   (unbound)    (stunnel)

The resulting DNS service is much more robust.

Warning

This software is a prototype. Use at your own risk.

References