An introduction to DNS, DNSCrypt and DNS Over HTTPS. The purpose of this article is not to explain in depth how DNS works, if you want to learn more about DNS, please check the references given in the article. I am only showing an application of what you can do with dnscrypt-proxy.

What is DNS and why does it matter?

The Domain Name System (DNS) translates human readable names into IP addresses. DNS is the reason you can type example.com in your address bar. If you want an introduction to how DNS works, you can check this colorful explanation from dnsimple.

Unfortunately, DNS is not privacy oriented by design. As it is in clear-text, anyone can see what website you are trying to access. Moreover DNS is vulnerable to spoofing, meaning anyone can fake being your DNS server. It is also vulnerable to tampering, so your ISP, government or company can censor results.

Fortunately, it is now possible to make your DNS queries as private as possible. DNSCrypt and DNS Queries Over HTTPS (DoH) are two protocols made for this purpose. DNSCrypt and DoH won’t hide the IP you are connecting to, nor will it hide the host names in HTTPS as SNI (Server Name Indication) is not encrypted. ESNI will partially solve this, but this is another story.

DNSCrypt

DNSCrypt works by authenticating and encrypting the DNS traffic. Hence preventing DNS spoofing and tampering. If you haven’t already, you should take a look the FAQ section of dnscrypt.info.

DNSCrypt works by initializing a session with a DNSCrypt-enabled resolver. Once it is done, the connection between the resolver and the client is encrypted. More information about the protocol itself are available on the DNSCrypt website. This covers everything from how the session is initialized to which encryption algorithm and certificate can be used. The specification is well written and easy to read even for a beginner.

DNS Queries Over HTTPS

DNS Queries over HTTPS (DoH), as the name implies, performs DNS resolution using HTTPS, hence having the pros and cons of HTTPS.

DoH is currently at the center of an ongoing fight between ISP and actors like Google and Mozilla supporting it. Unlike DNSCrypt, DoH is being implemented directly inside web browsers and the project of enabling it by default does not please ISPs.

Setting up DNSCrypt and DoH with dnscrypt-proxy

For this tutorial we will use dnscrypt-proxy for both the cache and everything related to DNSCrypt and DoH. This means that you won’t need dnsmasq anymore.

Once dnscrypt-proxy is installed, we can start configuring it.

First we will configure dnscrypt using the TOML file at /etc/dnscrypt-proxy/dnscrypt-proxy.toml. The default configuration is great but I’d like to add a few things:

# If you have IPv6 connectivity
ipv6_servers = true
# Use DNSSec when available
require_dnssec = true
# Always use the fallback resolver
ignore_system_dns = true

# These should be the default settings
dnscrypt_servers = true
doh_servers = true

require_nolog = true
require_nofilter = true
fallback_resolver = '9.9.9.9:53'

# If you don't want servers with DoH
doh_servers = false

# If you don't want servers with DNSCrypt
dnscrypt_servers = false

DNSCrypt can pick providers from a list maintained by Frank Denis. However, you might want specify it manually. In this case, I would recommend reading the DNSCrypt Documentation. To manually add a server, you will need to add it by given it a name and specifying its stamp in the static category. To quote the DNSCrypt website:

DNS Stamps encode all the parameters required to connect to a secure DNS server as a single string.

server_names = ['my-server']
[static]
    [static.'my-server']
    stamp = 'sdns://my-awesome-stamp'

Now that our dnscrypt-proxy is configured, let’s start it.

$ systemctl enable --now dnscrypt-proxy.service

Finally we would like our system to use dnscrypt-proxy. To do that, let’s edit the /etc/resolv.conf file and specify that we want to use 127.0.0.1:

nameserver 127.0.0.1

Your system might not be using /etc/resolv.conf, please ensure that you’re modifying the correct file. This might be resolved.conf for systemd-resolved or … anything depending on your system.

Everything works! You can verify that you are using dnscrypt-proxy by simply stopping it, blocking a website or by enabling query logging.

Setting up Forwarding

Everything is working … but one thing is missing! If you join a private network, you won’t be using its dns. This can be very annoying as you won’t be able to resolve example.internal.domain.tld. Fortunately, you won’t need to setup dnsmasq for this. dnscrypt-proxy can do forwarding using suffix matching.

Let’s see how it works. First you need to uncomment:

forwarding_rules = '/etc/dnscrypt-proxy/forwarding-rules.txt'

Now let’s say you want to resolve *.internal.domain.tld and internal.domain.tld using 192.168.0.253 and 192.168.1.253, simply add to forwarding-rules.txt:

internal.domain.tld 192.168.0.253,192.168.1.253

This is equivalent to the following configuration on dnsmasq:

server=/internal.domain.tld/192.168.0.253
server=/internal.domain.tld/192.168.1.253

While your internal dns server might not support DNSCrypt / DoH, you should be in a trusted environment when resolving through an internal resolver.

These won’t be detailed in this article but they are worth checking:

Anonymized DNSCrypt

Recently, preliminary support of Anonymized DNSCrypt was implemented on dnscrypt-proxy (since 2.0.29-beta.1). Anonymized DNSCrypt works by sending the DNS Query to a relay and encrypting it for the final server. This means that the relay is only able to forward the query without being able to take a look at the content.

This is a huge improvement as it avoids leaking the user IP.

Let’s configure Anonymized DNSCrypt on our previously installed dnscrypt-proxy. For more informations on Anonymized DNSCrypt, please read the official documentation.

As the name implies, this does not work with DoH. The first thing we would like to do is disable DoH.

doh_servers = false

Anonymized DNSCrypt configuration match servers with a list of relays than can be used to proxy your query. Let’s first define a list of servers we would like to use:

server_names = ['suami', 'scaleway-fr']

Then let’s create custom routes to those servers:

routes = [
   { server_name='suami', via=['anon-scaleway', 'anon-ibksturm'] },
   { server_name='scaleway-fr', via=['anon-suami', 'anon-ibksturm']
]

Note than while resolving using suami, I am not using the relay anon-suami. If both the relay and the server are maintained by the same entity, then this entity can match the query with the original IP making the relay useless.

Disclaimer: Please do not use this example configuration or trust me as to what servers are safe or fast in your country. Pick your own relays and servers, or trust a list made by someone you can trust.

DNS Provider

Picking the right DNS provider is essential as your provider might be (and usually is) tracking you. Hopefully, if you’re still reading at this point, I do hope, and actually believe, that your current DNS provider is not Google, your ISP or the DNS server provided by the current free wifi you are on. However, if this is still the case, you might want to check out some other providers that do respect your privacy. Worst case you loose a bit of performance while trying to get some privacy back, best case, you might even get some serious improvemennt.

While you can create your own recursive resolver, it is not helping you if you if you are the only one using it. I earlier provided you with a list maintened by Frank Denis, however, I would also recommend taking a look at this great list from privacytools.io.

dnscrypt-proxy is able to pick the fastest server so this won’t by a problem if you are travelling a lot.

Conclusion

Used properly with the right provider, DNS can be relatively private. As I said before, this won’t hide who you are connecting to and from where, but this will increase the work needed to track your every move and will avoid censoreship.

Hopefully, with time, support for ESNI will increase. All I hope is that IPV6 will not lead to one IP per domain name as, once mapped, it would clearly break the purpose of hiding which website you are connecting to.