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.
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.
- Set Up Your Own BIND9 DNS Resolver on Ubuntu 16.04/18.04
- Set Up Your Own BIND9 DNS Resolver on Ubuntu 20.04
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/nginx/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"
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.)
Click the gear button.
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.
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.
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.
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.
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. You may also want to read:
As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care 🙂
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?