How to: Deploy DoT and DoH with dnsdist

By on 28 Feb 2020

Category: Tech matters

Tags: , , , , ,


Blog home

DNS privacy is a major concern for many, and for good reasons. DNS requests contain fields that are considered private, which reveal sensitive information about someone’s browsing and Internet activities. To address these issues, two DNS privacy standards have grown in popularity over the last couple of years – DNS-over-TLS (RFC 7858) and DNS-over-HTTPS (RFC 8484).

DNS-over-TLS (or DoT) provides encrypted transport for DNS transactions. This is achieved by encrypting DNS traffic using TLS. DNS-over-HTTPS (DoH) provides another form of secure transport where DNS queries and responses are passed as HTTPS traffic. This also allows web applications to access DNS information using an API.

Below is a simple tutorial to implement these privacy standards. We will be using the Ubuntu 18.04 LTS (Bionic Beaver) server and the Mac OS X client. It is assumed the server already has DNS software installed doing recursive functions.

There’s a handful of tools and applications that can implement DNS privacy. We can run a TLS proxy or use a caching forwarder. In this tutorial, we will use dnsdist, a DNS-aware load balancer. It has support for both DoH and DoT and can easily be configured alongside existing DNS servers.

Part A: Configure DNS server with DoT

1. Install dnsdist

To support DoT, we will need dnsdist version 1.3 or higher. Since Ubuntu ships with an older version, update apt to include the PowerDNS repo.

vi /etc/apt/sources.list.d/pdns.list 
deb [arch=amd64] bionic-dnsdist-14 main
vi /etc/apt/preferences.d/dnsdist
Package: dnsdist*
Pin: origin
Pin-Priority: 600 
curl | sudo apt-key add - &&
sudo apt-get update &&
sudo apt-get install dnsdist

Check if the installed version supports DNS-over-TLS.

dnsdist --version
dnsdist 1.4.0 (Lua 5.1.4 [LuaJIT 2.1.0-beta3])
Enabled features: cdb dns-over-tls(gnutls openssl) dns-over-https(DOH) dnscrypt

2. Generate certificate

We will use OpenSSL to generate a self-signed certificate. For context, assume the server has the IP address and the domain

openssl req -newkey rsa:2048 -nodes -keyout -x509 -days 365 -out

Then copy the certificate and key in their respective directories. In this case, we will use /etc/ssl/certsand /etc/ssl/private/ . You may need to update the ownership and permissions accordingly.

Optional: If you use a domain that can be verified, then you may also use CertBot to get LetsEncrypt certificates.

3. Generate SPKI for the certificate

The SPKI pin must be published so that clients can use it to authenticate the server. Use this command to generate the SPKI.

echo | openssl s_client -connect 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

4. Create or update dnsdist configuration

Create or edit the dnsdist configuration file. Update to add the IP/port that dnsdist is listening to. Here it will listen to all interfaces on port 5300.

vi /etc/dnsdist/dnsdist.conf

addLocal('', {doTCP=true, reusePort=true, tcpFastOpenSize=0})
addLocal('[::]:5300', {doTCP=true, reusePort=true, tcpFastOpenSize=0})

For simplicity, we will use a very permissive ACL. You can adjust this based on your setup.


Add one or more downstream DNS servers where dnsdist will forward queries to. In this case, we are using the same server.

newServer({address="",qps=1, name="resolver1"}) 

5. Enable DNS over TLS

The option addTLSLocal adds a listening port for DoT. By default, it uses port 853.

addTLSLocal('','/etc/ssl/certs/', '/etc/ssl/private/')

The above line means the server will listen on port 853 on TCP, using the provided certificate and key for the TLS handshake.

To listen on all interfaces, add these two lines for IPv4 and IPv6 instead.

addTLSLocal('','/etc/ssl/certs/', '/etc/ssl/private/')
addTLSLocal('[::]','/etc/ssl/certs/', '/etc/ssl/private/')

6. Run the service

You may run the service using this command. It will also open the console.

dnsdist -C /etc/dnsdist/dnsdist.conf --local=

Confirm that the server is listening to the port.

Listening on for TLS
Listening on [::]:853 for TLS

7. Configure recursive DNS server

It is assumed that you already have a working DNS application running on the same server. This can be either PowerDNS, NSD or BIND. If not, you may install Bind9 as follows: 

sudo apt-get install bind9
sudo systemctl status bind9
sudo systemctl start bind9 

Part B: Configure DNS server with DoH

1. Update dnsdist configuration

DoH is supported in dnsdist version 1.4 and up. Confirm the version and check the feature is enabled.

dnsdist --version

dnsdist 1.4.0 (Lua 5.1.4 [LuaJIT 2.1.0-beta3])
Enabled features: cdb dns-over-tls(gnutls openssl) dns-over-https(DOH) dnscrypt

The option addDOHLocal adds a listening port for DoH. Open dnsdist.conf to add the following lines for IPv4 and IPv6. You can specify a specific address, but here it’s listening on all interfaces on port 443.

vi /etc/dnsdist/dnsdist.conf

addDOHLocal("", "/etc/ssl/certs/", "/etc/ssl/private/", "/dns-query", { doTCP=true, reusePort=true, tcpFastOpenSize=0 })

addDOHLocal("[::]:443", "/etc/ssl/certs/", "/etc/ssl/private/", "/dns-query", { doTCP=true, reusePort=true, tcpFastOpenSize=0 })

2. Run the service

Use the command:

dnsdist -C /etc/dnsdist/dnsdist.conf --local= 


systemctl start dnsdist
systemctl status dnsdist 

Check the output and confirm that it’s listening on the configured port:

Listening on for DoH
Listening on [::]:443 for DoH 

Part C: Configure the client

For the client, you need to have a stub resolver. Most operating systems have a built-in resolver, however, most currently don’t support DoT/DoH. As a workaround, we will install a stub resolver called Stubby. The instructions below are for a client running Mac OS X.

1. Install and configure Stubby

First, let’s install Stubby using the Homebrew package manager.

brew install stubby

Edit the configuration file:

vi /usr/local/etc/stubby/stubby.yml

Update the listen address, so that Stubby will listen only for local queries.

  - ::1

2. Configure an upstream recursive server

Queries sent to the local stub resolver will be forwarded via TLS to the upstream recursive server. Update the config file to use the recursive server that we set up in Part A. 

# my dns server
 - address_data:
    tls_auth_name: "”
      - digest: "sha256"
        value: <pubkey-base64> 

For the tls_pubkey_pinset, use the SPKI pin generated in Part A.3 and copy the output into the value field.

Don’t forget to comment out any other resolvers (added in Stubby by default) that you don’t wish to use.

3. Update the client to use localhost as resolver

Manually update the client network settings to use the local DNS resolver.

vi /etc/resolv.conf
nameserver ::1

4. For DNS over TLS, verify that DNS traffic is encrypted

You can use Wireshark or tcpdump to check that traffic between the client and the DNS server we configured is using TLS, and that no information about the queries are available in cleartext.

5. For DNS over HTTPS, configure an application or proxy to use the server

You can set Firefox to use your DoH server by updating the Network Settings and ticking the box next to ‘Enable DNS over HTTPS.’ Use Custom provider and type the domain or IP address of the server. 

And that’s it.

To learn more about DNS Privacy, please watch this webinar.

More detailed documentation of dnsdist is also available

Rate this article

The views expressed by the authors of this blog are their own and do not necessarily reflect the views of APNIC. Please note a Code of Conduct applies to this blog.


  1. Javier Miranda

    2. Generate certificate is ok work

    3. Generate SPKI for the certificate
    The SPKI pin must be published so that clients can use it to authenticate the server. Use this command to generate the SPKI.

    i need solution proble i not understand

    unable load certificate
    unable to load public key

  2. Christopher Maj

    @Javier Miranda
    Regarding point 3:
    I think you have to point to a working server i.e. replace IP and port:
    “openssl s_client -connect …”
    “openssl s_client -connect CORRECT_IP:PORT”

  3. Christopher Maj

    In section A, point 3. should be executed after point 6.

    Alternatively you can point to generated cert i.e.:
    openssl x509 -in /etc/ssl/certs/ -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl base64

  4. Anonymous

    First of all, I could not get DoT to work with these instructions. Server keeps saying 853 is already taken, when its not. I even ran a simple HTTP server on 853 and the server runs fine, dnsdist crashes on itself.

    Also DoH runs, but all I get back is 400 Bad Requests from the server, body reads “The DNS query could not be parsed”

    The author should have shared ways to test DoT or DoH via curl, or a web browser.


Leave a Reply

Your email address will not be published. Required fields are marked *