In this post, we describe the differences between the two widespread protocols for DNS encryption: DNS-over-TLS (DoT) and DNS-over-HTTPS (DoH). We compare the technical aspects of those protocols as well as their implications on user privacy. We also introduce Knot Resolver’s new built-in DoH support and explain some of our design decisions behind DoH.
Protocol overhead and complexity
Before diving into the details, lets skim over the following diagram which briefly illustrates the key differences between DoT and DoH. The first thing you may notice is that DoH is actually listed twice: as DoH with HTTP/2 protocol and DoH with HTTP/1.1 protocol. Besides sharing a similar name, these protocols are completely different – HTTP/2 is a binary protocol, while HTTP/1.1 is text-oriented.
The image compares the contents of DoT and DoH protocols inside the encrypted TLS layer. The blue and violet fields represent the payload of exchanged DNS messages, the rest is just the protocol overhead.
In DoT, this overhead is absolutely minimalist: it’s just a two-byte length prefix before every DNS message.
In DoH it gets more complicated, because the entire HTTP protocol is there in addition to the existing layers. If you’re wondering what purpose it serves, compared to DoT, you’re not alone. Some of the things that come to mind are additional attack surface or fingerprinting metadata.
DNS-over-TLS (DoT)
In the recent years, solutions have been proposed to add confidentiality to DNS. One of such early attempts has been DNSCrypt, but it has never been standardized. Instead, DNS-over-TLS (DoT) has been standardized as RFC7858 in 2016. Just a few months after the initial RFC publication, its support has been added to Knot Resolver in version 1.1.0.
The transition from DNS over TCP (which has been part of DNS since its inception) towards DNS-over-TLS was natural. DoT simply takes the existing DNS protocol and wraps it inside a secure TLS session. This provides the needed confidentiality between the two endpoints by encrypting the exchanged DNS messages.
Compared to UDP, the only protocol overhead of DoT (inside the encrypted layer) is 2-byte length prefix before every DNS message. Compared to TCP, the DoT protocol is identical. The only real overhead of DoT comes entirely from the TLS layer.
Similarly to DNS over TCP, it is possible to send multiple queries over a single DoT connection. It is possible to pipeline the queries – to send them without waiting for the answers first. The server can also respond to the queries out-of-order, which means that regardless of how many parallel queries the client sends, they aren’t going to block each other – every query will get an answer from the server as soon as it is available.
It’s worth noting that using TLS introduces new privacy concerns. The IP address is the only identifying information for queries sent over UDP. It isn’t possible to distinguish between multiple clients or devices behind the same IP address (or NAT).
However, when using the TLS layer in both DoT and DoH, the client must establish a connection. All queries sent over the established connection must inevitably belong to that particular client. Additionally, TLS session resumption makes it possible to link client queries together even across subsequent connections.
DNS-over-HTTPS (DoH)
Even though the problem of DNS confidentiality was effectively solved with DoT, a new standard (RC8484) appeared in 2018 – DNS-over-HTTPS (DoH). DoH is built on top of three layers: TCP, TLS and HTTP, while DoT uses just TCP and TLS. In terms of confidentiality, both DoT and DoH are equivalent, since they both use the TLS layer for encryption.
The DoH protocol has been very ambitious in some of its promises, such as Server Push or resolverless DNS, but these are proving to be difficult to implement in practice. There are great privacy and other concerns when multiplexing DNS and HTTP over the same connection. What we’re left with instead of great new features, is the HTTP protocol overhead, increased attack surface and novel privacy concerns (such as client/device fingerprinting through the use of HTTP headers).
In addition, as illustrated in the figure at the start of the article, DoH isn’t actually a single protocol, because it depends on the HTTP version which is used. While DoH over HTTP/2 can achieve a similar performance as DoT, DoH over HTTP/1.1 can’t – and it’s a bad idea to use it for this purpose. Even RFC8484 mentions that HTTP/2 is the minimum RECOMMENDED version for DoH. One of the major issues of HTTP/1.1 that HTTP/2 solves is head-of-line blocking at the application protocol level.
Both DoT and DoH using HTTP/2 make it possible to send replies to queries independently of the order in which they arrived. In contrast, HTTP/1.1 must send the replies in same order as the queries arrived. This means that if any query takes a long time to answer, it effectively blocks all the other responses over the same connection until the blocking query is resolved.
DoH in Knot Resolver
Due to popular demand, we’ve implemented a DoH prototype back in 2019 in version 4.0.0. We’ve leveraged the modular design of Knot Resolver and our existing http module to provide the DoH service. Since we use the lua-http library for this module, we didn’t have to worry about HTTP implementation details.
However, there were multiple drawbacks: there was a separate encryption and socket layer (handled by lua-http’s dependencies), it introduced extra dependencies, the configuration was more complex etc. With some operational experience, it also became clear our http module isn’t suited for serious DoH deployments, due to many stability and performance issues.
We considered alternate deployment options, such as a separate DoH proxy that can translate client’s HTTPS requests into plain DNS. One of such possible solutions is to use dnsdist with Knot Resolver.
Ultimately, we’ve decided to provide a built-in support for DoH to achieve superior performance and much simpler configuration with just a single component – the resolver itself. The native built-in DoH support was implemented in version 5.2.0. This inevitably exposed us to the implementation details of HTTP.
Some might argue there are libraries that can handle multiple versions of HTTP protocol, such as libcurl, which hide the complexities of HTTP. They might be suitable for client applications, but they weren’t suitable for our high-performance use-case. We already have the building blocks in place to create large number of encrypted TLS sockets and handle them asynchronously.
In the end, we’ve decided to use libnghttp2. Another resolver implementation, Unbound, independently chose the same approach and library for adding DoH support in their latest release (1.12.0).
As the name suggests, libnghttp2 supports just HTTP/2. This means that going forward, Knot Resolver will support only DoH using HTTP/2. Our previous implementation is still available, but it has been deprecated and will be removed in future versions.
Because we consider removing HTTP/1.1 support a breaking change, we didn’t automatically switch users to the new DoH implementation in 5.2.0. We encourage all resolver operators to switch to the new module at their own pace.
Performance and latency
There is also another downside to using encrypted DNS. Establishing the TLS connection is expensive in both resources and latency. Our benchmarks have shown that connection re-use plays a significant role in reducing both latency and CPU load.
When it comes to server performance, the most significant factor is the algorithm used for the TLS certificate. If you’re serious about DoT or DoH deployment, make sure to use an elliptic curve certificate. Surprisingly, the overhead of HTTP/2 in DoH compared DoT isn’t as dramatic as we expected, but it is certainly measurable.
When it comes to reducing latency for the clients, various techniques have been proposed to reduce the number of round-trips required to establish a TLS session, such as TCP Fast Open or TLS Early Start. Dealing with these is difficult and their support is limited.
TCP also suffers from the head-of-line blocking problem on the transport protocol level. It seems the ultimate solution might be to eventually abandon TCP in favor of another protocol, such as QUIC. Perhaps in the future, DNS-over-QUIC will prove superior to both DoT and DoH.
Application-level DoH
DNS-over-HTTPS is often being championed as a big win for user privacy. Modern browsers provide DoH support, and you can even pick your favorite cloud DNS provider from a very short pre-configured list. Before you do, however, I invite you to consider the implications of that choice, and to think about what it does and doesn’t do.
Let’s suppose you’re on an untrusted local network, such as an internet cafe or a hotel. In this scenario, if your objective is to avoid the local network resolver and you’d prefer to encrypt your queries and send them to another resolver somewhere on the internet, both DoH and DoT do just fine to protect your queries from the local network – they give you confidentiality towards your selected resolver. However, in this scenario, you’d probably be better off using a VPN which can protect all your traffic, along with all your queries.
On the other hand, what if you’re on a trusted local network? If you have control over the local network or you trust its administrator, the confidentiality of your queries towards your local resolver probably isn’t your biggest concern. What might be concerning from the standpoint of privacy, is to send application-identifying metadata along with your DNS queries to a large cloud provider, which has both the ability and the incentive to exploit this data.
Unfortunately, this is exactly what browser support of DoH can do. In the end, opting to use application-level DoH might actually decrease your privacy.
DoT for better privacy
If privacy is your concern, then perhaps a better solution is to set up a local (or system) resolver and use DoT to forward the traffic to a resolver of your choice.
When multiple applications and devices are behind your own resolver, they not only become indistinguishable to the target resolver operator, they also share the local cache. This gives you faster responses and you send less queries to the target resolver. And most importantly, if you use DoT, you’re not leaking application identifying metadata outside your trusted network.
However, with both DoT and DoH, there’s still a question how to pick a trusted resolver. Unfortunately, the answer to this question isn’t simple, and it certainly isn’t universal. You might need to create or consider your own threat model, and decide who you want to trust.
Your decision might be affected by your geographical location as well as the political regime of the country you find yourself in. It might be best to trust your ISP, a large cloud provider, some smaller open resolver, or the authoritative servers, if you choose to run an iterating resolver yourself.
Knot Resolver provides both DoT and DoH
In this post, I tried to describe the issues and technical experiences with implementing DoH in Knot Resolver. I outlined many issues that affect DoH which don’t affect DoT.
I’ve described the extra complexity that comes with introducing the HTTP protocol into the DNS protocol stack. It inevitably has the cost of increased attack surface, lower performance and also a novel set of of potential privacy leaks from previously unseen metadata.
Both DoT and DoH (server-side) are natively supported in Knot Resolver 5.2.0 and you’re free to make your own choice which protocol(s) you’d like to offer your users.
As a final note, keep in mind that encrypted DNS protocols solve a different problem than DNSSEC. While DoT and DoH provide confidentiality, DNSSEC guarantees authenticity of the answers. These technologies are complimentary and it’s best to use both!