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] http://repo.powerdns.com/ubuntu bionic-dnsdist-14 main
vi /etc/apt/preferences.d/dnsdist Package: dnsdist* Pin: origin repo.powerdns.com Pin-Priority: 600
curl https://repo.powerdns.com/FD380FBB-pub.asc | 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 192.168.100.53
and the domain dnspriv.apnic.net.
openssl req -newkey rsa:2048 -nodes -keyout dnspriv.apnic.net.key -x509 -days 365 -out dnspriv.apnic.net.pem
Then copy the certificate and key in their respective directories. In this case, we will use /etc/ssl/certs
and /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 192.168.100.53:853 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('0.0.0.0:5300', {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.
addACL('0.0.0.0/0') addACL('::/0')
Add one or more downstream DNS servers where dnsdist will forward queries to. In this case, we are using the same server.
newServer({address="192.168.100.53",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('192.168.100.53','/etc/ssl/certs/dnspriv.apnic.net.pem', '/etc/ssl/private/dnspriv.apnic.net.key')
The above line means the server will listen on 192.168.100.53
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('0.0.0.0','/etc/ssl/certs/dnspriv.apnic.net.pem', '/etc/ssl/private/dnspriv.apnic.net.key') addTLSLocal('[::]','/etc/ssl/certs/dnspriv.apnic.net.pem', '/etc/ssl/private/dnspriv.apnic.net.key')
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=0.0.0.0:5300
Confirm that the server is listening to the port.
Listening on 0.0.0.0:853 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("0.0.0.0:443", "/etc/ssl/certs/dnspriv.apnic.net.pem", "/etc/ssl/private/dnspriv.apnic.net.key", "/dns-query", { doTCP=true, reusePort=true, tcpFastOpenSize=0 }) addDOHLocal("[::]:443", "/etc/ssl/certs/dnspriv.apnic.net.pem", "/etc/ssl/private/dnspriv.apnic.net.key", "/dns-query", { doTCP=true, reusePort=true, tcpFastOpenSize=0 })
2. Run the service
Use the command:
dnsdist -C /etc/dnsdist/dnsdist.conf --local=0.0.0.0:5300
or
systemctl start dnsdist systemctl status dnsdist
Check the output and confirm that it’s listening on the configured port:
Listening on 0.0.0.0:443 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.
listen_addresses: - 127.0.0.1 - ::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.
upstream_recursive_servers: # my dns server - address_data: 192.168.100.53 tls_auth_name: "dnspriv.apnic.net” tls_pubkey_pinset: - 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 127.0.0.1 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.
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.
Thanks,will try this out 🙂
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
@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 192.168.100.53:853 …”
to
“openssl s_client -connect CORRECT_IP:PORT”
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/dnspriv.apnic.net.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl base64
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.
Great article! One question: I see that ‘ and ” are both used in the configuration. Probably they both work, but wouldn’t it have been better to choose one and use that throughout the entire document?