How to Easily Set Up a DNS over TLS Resolver with Nginx on Ubuntu

This tutorial will be showing you how to set up your own DNS over TLS (DoT) resolver on Ubuntu with Nginx, so your DNS queries can be encrypted and protected from prying eyes.

What is DNS over TLS and Why It’s Important

DNS (Domain Name System) is responsible for translating domain names to IP addresses. It’s designed in 1987 with no security or privacy in mind. By default DNS queries are not encrypted. They are sent in plain text on the wire and can be exploited by middle entities. For example, the Great Firewall of China (GFW) uses a technique called DNS cache poison to censor the Chinese Internet. (They also use other methods, which are beyond the scope of this article.)

GFW checks every DNS query that is sent to a DNS server outside of China. Since plain text DNS protocol is based on UDP, which is a connection-less protocol, GFW can spoof both the client IP and server IP. When GFW finds a domain name on its block list, it changes the DNS response. For instance, if a Chinese Internet user wants to visit google.com, GFW returns an IP address located in China instead of Google’s real IP address, to the user’s DNS resolver. Then the DNS resolver returns the fake IP address to the user’s computer, so the user cannot visit google.com.

DNS over TLS means that DNS queries are sent over a secure connection encrypted with TLS, the same technology that encrypts HTTP traffic.

Set Up a DNS over TLS Resolver with Nginx on Ubuntu

Why Run Your Own DoT Resolver?

There are already some public DNS resolvers like 1.1.1.1 and 9.9.9.9 that supports DNS over TLS, so you can use them if you don’t have the skill or time to run your own. However, some folks argue that this still allows big DNS servcie providers to gather information on users. They seem to have more trust in their ISP. But I think if you are paranoid about privacy, you should run your own DoT resolver, so neither big DNS service providers nor your ISP can spy on you.

Currently, not all DNS resolvers (BIND, Unbound, Knot resolver, PowerDNS recursor, etc) support DNS over TLS. Instead of making a guide for a specific resolver, I’m going to show you how to set up Nginx TLS proxy for your existing DNS resolver to provide DoT service, so no matter what DNS resolver you are using, you can follow this tutorial.

Prerequisites

It’s assumed that you have a DNS resolver running on your Ubuntu server. You can use any DNS resolver (BIND, Unbound, Knot resolver…) I personally use BIND.

You also need a domain name, because DNS clients will need to establish secure TLS connection with our DNS resolver. I registered my domain name from NameCheap because the price is low and they give whois privacy protection free for life.

Once you meet the above requirements, follow the instructions below.

Step 1: Install Nginx on Ubuntu Server

It’s very easy to do. Simply run the following command.

sudo apt install nginx

Step 2: Obtain a Trusted TLS Certificate from Let’s Encrypt

DNS over TLS requires installing a TLS certificate on the server-side. We will obtain and install Let’s Encrypt certificate. The advantage of using Let’s Encrypt certificate is that it’s free, easier to set up, and trusted by client software.

Run the following commands to install Let’s Encrypt client (certbot) from the default Ubuntu repository.

sudo apt install certbot

To obtain a Let’s Encrypt TLS certificate, we can create a Nginx virtual host with the following command. Repalce dot.example.com with your own domain name. Don’t forget to create DNS A record for this sub-domain.

sudo nano /etc/nginx/conf.d/dot.example.com.conf

Copy the following text and paste it into the virtual host file.

server {
      listen 80;
      server_name dot.example.com;

      root /usr/share/nignx/html/;

      location ~ /.well-known/acme-challenge {
         allow all;
      }
}

Save and close the file. Reload Nginx for the changes to take effect.

sudo systemctl reload nginx

Once virtual host is created and enabled, run the following command to obtain Let’s Encrypt certificate using webroot plugin.

sudo certbot certonly --webroot --agree-tos --email [email protected] -d dot.example.com -w /usr/share/nginx/html/

Step 3: Create DNS over TLS Proxy in Nginx

Edit the Nginx main configuration file.

sudo nano /etc/nginx/nginx.conf

Add the following lines at the bottom of this file. Note that they need to be placed outside of the http context.

stream {
    # DNS upstream pool
    upstream dns {
        zone dns 64k;
        server 127.0.0.1:53;
    }

   # DoT server for decryption
   server {
        listen 853 ssl;
        ssl_certificate /etc/letsencrypt/live/dot.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/dot.example.com/privkey.pem;
        proxy_pass dns;
    }
}

In the above configuration, we make Nginx terminate TLS connection on port 853, then it will redirect DNS requests to the local DNS resolver listening on 127.0.0.1:53.

Save and close the file. Then test Nginx configuration and restart.

sudo nginx -t
sudo systemctl restart nginx

If there’s a firewall running on Ubuntu server, you need to open TCP port 853. For example, if you use the UFW firewall, run the following command.

sudo ufw allow 853/tcp

Since we are using DNS over TLS, there’s no need to worry about DNS amplification attack.

Step 5: Configure the Stubby DoT Client on Ubuntu Desktop

Stubby is an open-source DNS stub resolver developed by the getdns team. A stub resolver is a small DNS client on the end-user’s computer that receives DNS requests from applications such as Firefox and forward requests to a recursive resolver like 1.1.1.1 or 8.8.8.8. Stubby is special in that it supports DNS over TLS. By default, it will only send DNS requests encrypted.

Install Stubby on Ubuntu desktop from the default repository.

sudo apt install stubby

Once installed, stubby runs in the background. check its status with:

systemctl status stubby

Stubby listens on TCP and UDP port 53 of localhost (127.0.0.1). By default, Stubby uses third-party DNS over TLS resolvers. We need to configure it to use our own.

sudo nano /etc/stubby/stubby.yml

Scroll down to the upstream_recursive_servers: section and add the following text above other DNS servers. Replace 12.34.56.78 with the IP address of your DoT resolver.

# My Own DNS over TLS resolver
  - address_data: 12.34.56.78
    tls_auth_name: "dot.example.com"

configure stubby to use dns over tls resolver

Then find the following line:

round_robin_upstreams: 1

Change 1 to 0. This will make stubby always use your own DNS over TLS resolver. If it’s not available, stubby will use other DNS servers. Save the file and restart stubby for the changes to take effect.

sudo systemctl restart stubby

Step 6: Configure Ubuntu Desktop to Use Stubby

Although Stubby is running, it’s not being used by the operating system. Click the Network Manager icon on the upper-right corner of your desktop. Then select wired settings. (If you are using Wi-fi, select Wi-fi settings.)

encrypt dns

Click the gear button.

cloudflare dns over tls

Select IPv4 tab, then in DNS settings, switch Automatic to OFF, which will prevent your Ubuntu system from getting DNS server address from your router. Enter 127.0.0.1 in the DNS field. Click Apply button to save your changes.

dns over tls port 853

Then restart NetworkManager for the changes to take effect.

sudo systemctl restart NetworkManager

Once you are reconnected, you can see that your Ubuntu system is now using 127.0.0.1 as the DNS server in the Details tab.

stub resolver dns over tls

How to Check if Your DNS Traffic is Encrypted

We can use WireShark to monitor DNS traffic. Install WireShark on Ubuntu desktop.

sudo apt install wireshark

If you are asked “Should non-superusers be able to capture packets?”, answer Yes. Once it’s installed, run the following command to add your user account to the wireshark group so that you can capture packets.

sudo adduser your-username wireshark

Log out and log back in for the changes to take effect. Then open WireShark from your application menu, select your network interface in WireShark. For example, my Ethernet interface name is enp5s0. Then enter port 853 as the capture filter. This will make WireShark only capture traffic on port 853, which is the port used by DNS over TLS.

ubuntu 18.04 stubby

Click the button on the upper-left corner to start capturing. After that, in terminal window, run the following command to query domain name by using the dig utility. For instance, I can query the A record of my domain name.

dig A linuxbabe.com

Now you can see the captured DNS traffic in WireShark. Connections were made over TCP and encrypted with TLS, which is what we want. You should check if the Destination column includes the IP address of your DoT resolver.

secure dns

If DNS queries are sent without encryption, then the computer would contact DNS server on port 53. You can capture packets again with port 53 as the capture filter, but you won’t see any packets in WireShark, which means stubby is encrypting your DNS queries.

Wrapping Up

I hope this tutorial helped you set up a DNS over TLS resolver with Nginx on Ubuntu. As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care 🙂

Rate this tutorial
[Total: 1 Average: 5]

One Response to “How to Easily Set Up a DNS over TLS Resolver with Nginx on Ubuntu

  • Cyberian
    3 seconds ago

    Using iOS app (DNS Master) which allows editing of the DNS server address https://dns.mydomain.com:853 it does not connect to the server. What am I doing wrong?

Leave a Comment

  • Comments with links are moderated by admin before published.
  • Your email address will not be published.
  • Use <pre> ... </pre> HTML tag to quote the output from your terminal/console.
  • Please use the community (https://community.linuxbabe.com) for questions unrelated to this article.
  • I don't have time to answer every question. Making a donation would incentivize me to spend more time answering questions.