Set Up DNS over HTTPS (DoH) Resolver on Ubuntu with DNSdist

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

What is DNS over HTTPS 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.

HTTPS is the standard way to encrypt plain text HTTP web pages. With DNS over HTTPS (DoH), your DNS queries will be encrypted, so no third parties can see your DNS queries.

DNS over HTTPS (DoH) Resolver on Ubuntu with DNSdist

Why Run Your Own DoH Resolver?

There are already some public DNS resolvers like 1.1.1.1 and 9.9.9.9 that support DNS over HTTPS, so you can use them if you don’t have the skill or time to run your own. Starting with Firefox version 61, you can enable DNS over HTTPS in the browser settings, which is a big progress for Internet security and privacy. Firefox uses Cloudflare resolvers (1.1.1.1) by default. However, some folks argue that this allows Cloudflare to gather information on Firefox users. They seem to have more trust in their ISP than Cloudflare. But I think if you are paranoid about privacy, you should run your own DoH resolver, so neither Cloudflare nor your ISP can spy on you.

DoH vs DoT

Besides DNS over HTTPS, there’s another protocol that also aims to encrypt DNS queries. It’s called DNS over TLS (DoT). Previously I wrote a guide to using  DNS over TLS on Ubuntu desktop, but now I’m switching to DNS over HTTPS.

For people live in countries with severe Internet censorship like China, it’s more advantageous to use DoH.

  • DoT operates on TCP port 853, which can be easily blocked by a national firewall.
  • DoH operates on TCP port 443, which is the standard port for HTTPS websites, which makes DoH super hard to block, because if TCP port 443 is blocked, then nearly all HTTPS websites will also be blocked.

My DoH resolver is running on a VPS (virtual private server) located outside of China and the Great Firewall of China can’t intercept my DNS queries. I can even hide my DoH resolver’s IP address behind Cloudflare CDN (Content Delivery Network). My VPS also runs a VPN server.

Another advantage of DoH is that it allows web applications to access DNS information via existing browser APIs, so no stub resolver is needed.

DoH Support in Major DNS Resolvers

  • BIND supports DoH since version 9.17. Ubuntu 22.04 ships with BIND 9.18 and Ubuntu 20.04 ships with BIND 9.16.
  • Knot resolver supports DoH since version 4.0.0. The current latest version is 5.11. It has an official repository for Debian, Ubuntu, CentOS, Fedora.
  • Unbound supports DoH since version 1.12.0. Ubuntu 22.04 ships with Unbound 1.13.
  • PowerDNS recursor doesn’t support DoH right now.

Actually, I prefer to run DoH resolver with DNSdist, which added DoH support in version 1.4.0. The current latest version is 1.7. It has an official repository for Debian, Raspbian, Ubuntu, and CentOS. DNSdist is a DNS load balancer that can forward DNS queries to a backend DNS resolver, so no matter what DNS resolver you are using, you can use DNSdist to run your own DoH server. DNSdist is developed by the PowerDNS team.

Prerequisites

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

Once your DNS resolver is up and running, follow the instructions below.

Step 1: Install DNSdist on Ubuntu Server

Ubuntu 22.04 users can install dnsdist from the default repository (sudo apt install dnsdist).

If you use Ubuntu 20.04/18.04, it’s recommended to install DNSdist from the upstream repository, so you will have the latest stable version. First, you need to create a source list file for DNSdist.

Ubuntu 20.04

echo "deb [arch=amd64] http://repo.powerdns.com/ubuntu focal-dnsdist-17 main" | sudo tee /etc/apt/sources.list.d/pdns.list

Ubuntu 18.04

echo "deb [arch=amd64] http://repo.powerdns.com/ubuntu bionic-dnsdist-17 main" | sudo tee /etc/apt/sources.list.d/pdns.list

Next, we create a preference file for DNSdist to pin the package, so we won’t be accidentally installing DNSdist from another repository.

sudo nano /etc/apt/preferences.d/dnsdist

Add the following lines into the file.

Package: dnsdist*
Pin: origin repo.powerdns.com
Pin-Priority: 600

Save and close the file. Then run the following command to import the PowerDNS public key, so the APT package manager can verify the interity of software packages downloaded from this repository.

curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo apt-key add -

Next, update the repository list and install DNSdist.

sudo apt update

sudo apt install dnsdist

By default, DNSdist tries to bind to port 53. Because you have an existing DNS resolver like BIND listening on port 53, the dnsdist.service would fail to start.

Job for dnsdist.service failed because the control process exited with error code

Since we are just deploying a DoH resolver and don’t care about DNS load balancing, we can configure DNSdist to listen on another port. Edit the DNSdist configuration file.

sudo nano /etc/dnsdist/dnsdist.conf

There’s no content in this file. For now, simply add the following line in this file, so DNSdist will listen on TCP and UDP port 5353, instead of port 53.

setLocal("127.0.0.1:5353")

Save and close the file. Then restart DNSdist.

sudo systemctl restart dnsdist

Check its status.

systemctl status dnsdist

It should be active (running).

dnsdist doh resolver ubuntu

Step 2: Install Let’s Encrypt Client (Certbot) on Ubuntu Server

DNS over HTTPS 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 check the version number, run

certbot --version

Sample output:

certbot 0.40.0

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

I recommend using the standalone or webroot plugin to obtain TLS certificate for dnsdist.

Standalone Plugin

If there’s no web server running on your Ubuntu server, you can use the standalone plugin to obtain TLS certificate from Let’s Encrypt. Create DNS A record for the subdomain (doh.example.com), then run the following command.

sudo certbot certonly --standalone --preferred-challenges http --agree-tos --email [email protected] -d doh.example.com

Where:

  • certonly: Obtain a certificate but don’t install it.
  • --standalone: Use the standalone plugin to obtain a certificate
  • --preferred-challenges http: Perform http-01 challenge to validate our domain, which will use port 80.
  • --agree-tos: Agree to Let’s Encrypt terms of service.
  • --email: Email address is used for account registration and recovery.
  • -d: Specify your domain name.

As you can see the from the following screenshot, I successfully obtained the certificate.

dnsdist dns over https let's encrypt

Using webroot Plugin

If your Ubuntu server has a web server listening on port 80 and 443, then it’s a good idea to use the webroot plugin to obtain a certificate because the webroot plugin works with pretty much every web server and we don’t need to install the certificate in the web server.

First, you need to create a virtual host for doh.example.com.

Apache

If you are using Apache, then

sudo nano /etc/apache2/sites-available/doh.example.com.conf

And paste the following lines into the file.

<VirtualHost *:80>        
        ServerName doh.example.com

        DocumentRoot /var/www/dnsdist
</VirtualHost>

Save and close the file. Then create the web root directory.

sudo mkdir /var/www/dnsdist

Set www-data (Apache user) as the owner of the web root.

sudo chown www-data:www-data /var/www/dnsdist -R

Enable this virtual host.

sudo a2ensite doh.example.com

Reload Apache for the changes to take effect.

sudo systemctl reload apache2

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 doh.example.com -w /var/www/dnsdist

Nginx

If you are using Nginx, then

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

Paste the following lines into the file.

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

      root /var/www/dnsdist/;

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

Save and close the file. Then create the web root directory.

sudo mkdir -p /var/www/dnsdist

Set www-data (Nginx user) as the owner of the web root.

sudo chown www-data:www-data /var/www/dnsdist -R

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 doh.example.com -w /var/www/dnsdist

Step 4: Enable DoH in DNSdist

Edit the DNSdist configuration file.

sudo nano /etc/dnsdist/dnsdist.conf

Add the following lines into the file.

-- allow query from all IP addresses
addACL('0.0.0.0/0')

-- add a DoH resolver listening on port 443 of all interfaces
addDOHLocal("0.0.0.0:443", "/etc/letsencrypt/live/doh.example.com/fullchain.pem", "/etc/letsencrypt/live/doh.example.com/privkey.pem", { "/" }, { doTCP=true, reusePort=true, tcpFastOpenSize=0 })

-- downstream resolver
newServer({address="127.0.0.1:53",qps=5, name="resolver1"})

Save and close the file. DNSdist runs as the _dnsdist user, so we need to give the _dnsdist user permission to read the TLS certificate with the following commands.

sudo apt install acl

sudo setfacl -R -m u:_dnsdist:rx /etc/letsencrypt/

Then check the syntax of the configuration file.

sudo dnsdist --check-config

If the syntax is ok, restart DNSdist.

sudo systemctl restart dnsdist

Note that if there’s a web server listening on TCP port 443, DNSdist would fail to restart. You can temporarily stop the web server. I will explain how to make the web server and DNSdist use TCP port 443 at the same time at the end of this article.

When Certbot renews the TLS certificate, you need to grant permission again, but you can add the command in the crontab file.

sudo crontab -e

Like this:

@daily certbot renew --quiet; setfacl -R -m u:_dnsdist:rx /etc/letsencrypt/

Step 5: Configure DoH in Firefox Web Browser

Go to Preferences -> General and scroll down to the bottom to configure Network Settings. Enable DNS over HTTPS, and set your own DoH resolver.

firefox configure dns over https resolver

We can then fine-tune the DoH configurations by going to about:config tab in Firefox.

network.trr.mode

By default, The network.trr.mode parameter in Firefox is set to 2, which means if DoH query fails, Firefox will pass the DNS query to the host system. I want to always use the DoH resolver, so change the network.trr.mode to 3 so the host resolver won’t be used. This allows us to have an easy way to test if your DoH resolver is working.

network.trr.allow-rfc1918

This is set to false by default, which means if the DNS response includes private IP addresses, then it will be considered a wrong reponse that won’t be used. If you use response policy zone in BIND, you probably have some hostnames pointing to private IP addresses, then set this value to true.

network.trr.bootstrapAddress

Firefox needs to look up the IP address of the DoH resolver in order to send DNS queries. You can put the IP address in this field to eliminate this initial query.

Testing

Now enter a domain name like linuxbabe.com in the Firefox address bar. If the web page loads normally, it’s a good sign your DoH resolver is working. Then go to the terminal console of your DNS server and check the DNS query logs. I use BIND, so I enter the following command to check the DNS query log.

Ubuntu 20.04

sudo journalctl -eu named

Ubuntu 18.04

sudo journalctl -eu bind9

As you can see from the BIND log below, Firefox queried the following domains.

  • www.linuxbabe.com: my website
  • fonts.gstatic.com: This serves Google fonts on my website
  • cdn.shareaholic.net: a sharing widget on my website
  • newsletter.linuxbabe.com: my self-hosted email marketing platform.
  • translate.google.com: the Google translate widget on my website

BIND dns over https query log

The above query log tells me that my DNS over HTTPS resolver is working. If I stop the BIND resolver (sudo systemctl stop named), Firefox tells me it can’t find that site. And when I start BIND, the web page loads again.

Step 6: Configure DoH Client on Ubuntu Desktop

I know there are 3 open-source DNS clients on Linux that support encrypted DNS.

  • systemd-resolved
  • stubby
  • dnscrypt-proxy

Currently, dnscrypt-proxy is the best DoH client implementation on Linux. It also runs on BSD, macOS, Windows and Android. so I will show you how to use it.

Run the following command to install dnscrypt-proxy on your Ubuntu desktop.

sudo apt install dnscrypt-proxy

It will automatically start, as you can see with:

sudo systemctl status dnscrypt-proxy

If it didn’t start, you can run the following command to start it.

sudo systemctl enable dnscrypt-proxy --now

Check the listening address.

sudo ss -lnptu | grep dnscrypt-proxy

dnscrypt-proxy listening address

On my computer, dnscrypt-proxy listens on 127.0.2.1:53. There’s another systemd service (dnscrypt-proxy-resolvconf.service) installed by dnscrypt-proxy,

systemctl status dnscrypt-proxy-resolvconf

This is a service that sets 127.0.2.1 as the resolver on your computer. You can check the current DNS resolver for your computer with:

cat /etc/resolv.conf

By default, dnscrypt-proxy uses Clouldflare as the upstream DoH resolver. To use your own DoH resolver, edit the configuration file.

sudo nano /etc/dnscrypt-proxy/dnscrypt-proxy.toml

Find the following line

server_names = ['cloudflare']

Change it to your DoH resolver hostname. Note that the location of parameters is important. You should not put the server_names parameter in other places in this file.

server_names = ['doh.example.com']

Then comment out the [sources] section.

#[sources]
#  [sources.'public-resolvers']
#  url = 'https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md'
#  cache_file = '/var/cache/dnscrypt-proxy/public-resolvers.md'
#  minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
#  refresh_delay = 72
#  prefix = ''

We need to add our own DoH resolver like below at the bottom of this file.

[static]  
  [static.'doh.example.com']
  stamp = 'sdns://AgUAAAAAAAAAAAAOZG5zLmdvb2dsZS5jb20NL2V4cGVyaW1lbnRhbA'

The stamp contains your DoH resolver hostname, IP address and query path. To get your own stamp, use the online DNS stamp calculator.

  • Choose DNS-over-HTTPS as the protocol
  • Enter the IP address, hostname, and query path.
  • If you follow this tutorial to set up your own DoH resolver, the path should be set to just /.
  • If you didn’t enable DNSSEC on your resolver, then untick the DNSSEC checkbox.

dnscrypt-proxy online dns stamp calculator

Once you added your DNS stamp, save and close the file. Then restart dnscrypt-proxy.

sudo systemctl restart dnscrypt-proxy

Check its status. Make sure it’s running.

systemctl status dnscrypt-proxy

Now you can test if dnscrypt-proxy is working.

If you don’t want to publish your DNS over HTTPS resolver to the world, you can delete the DNS A record of your DoH resolver hostname, because dnscrypt-proxy has the IP address in the DNS stamp.

Configure DoH on iOS

iOS supports DNS over HTTPS since iOS 14. However, there’s no a simple config option on iOS to set DoH resolver. You need to generate a .mobileconfig file and install it on iOS. It’s a little complicated to generate a signed iOS .mobileconfig file.

If your DoH resolver won’t be used publicly, you can generate a simple unsigned file. Use a text editor to create a file with .mobileconfig extension. Copy and paste the following text. Replace the IP ddress and server URL.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>PayloadContent</key>
        <array>
                <dict>
                        <key>DNSSettings</key>
                        <dict>
                                <key>DNSProtocol</key>
                                <string>HTTPS</string>
                                <key>ServerAddresses</key>
                                <array>
                                        <string>12.34.56.78</string>
                                </array>
                                <key>ServerURL</key>
                                <string>https://doh.example.com/</string>
                        </dict>
                        <key>PayloadDescription</key>
                        <string>Configures device to use Encrypted DNS over HTTPS</string>
                        <key>PayloadDisplayName</key>
                        <string>My own DNS over HTTPS Resolver</string>
                        <key>PayloadIdentifier</key>
                        <string>com.apple.dnsSettings.managed.9d6e5fdf-e404-4f34-ae94-27ed2f636ac4</string>
                        <key>PayloadType</key>
                        <string>com.apple.dnsSettings.managed</string>
                        <key>PayloadUUID</key>
                        <string>35d5c8a0-afa6-4b36-a9fe-099a997b44ad</string>
                        <key>PayloadVersion</key>
                        <integer>1</integer>
                        <key>ProhibitDisablement</key>
                        <false/>
                </dict>
        </array>
        <key>PayloadDescription</key>
        <string>Adds self-hosted DoH resolver to Big Sur and iOS 14 based systems</string>
        <key>PayloadDisplayName</key>
        <string>My own DNS over HTTPS</string>
        <key>PayloadIdentifier</key>
        <string>com.example.doh</string>
        <key>PayloadRemovalDisallowed</key>
        <false/>
        <key>PayloadType</key>
        <string>Configuration</string>
        <key>PayloadUUID</key>
        <string>A4475135-633A-4F15-A79B-BE15093DC97A</string>
        <key>PayloadVersion</key>
        <integer>1</integer>
</dict>
</plist>

Save and close the file. Then you can send the file as an email attachment to your iOS. Open the email on iOS and tap on the attachment to download the config file. After that, go to iOS Settings -> Profile Downloaded. It will prompt you to install the profile.

iso dns over https

Tap on the Install button and enter your iOS password to install it. Tap on Install button again.

ios doh

Once it’s installed, go to iOS Settings -> General ->  VPN, DNS, & Device Management -> DNS. Choose the DNS resolver for your iOS system. The DoH resolver will be sused in both Wi-fi and cellular data environment. Note that you should not configure manual IP address in the iOS Wi-fi settings, or iOS won’t use your DNS over HTTPS resolver.

If you connect to a VPN like ProtonVPN. Your iOS will use the DNS resolver provided by the VPN server. You can go to dnsleaktest.com to check which DNS resolver you are using. If you ever want to remove the .mobileconfig file from iOS, go to Settings -> General -> Profile.

Make DNSdist and web server use port 443 at the same time

A DNS over HTTPS resolver needs to bind to port 443. If you already have Apache/Nginx listening on port 443, then DNSdist can’t bind to port 443. Normally a port can only be used by one process. However, we can use HAproxy (High Availability Proxy) and SNI (Server Name Indication) to make DNSdist and Apache/Nginx use port 443 at the same time.

DNSdist Configuration

Edit the DNSdist configuration file.

sudo nano /etc/dnsdist/dnsdist.conf

Change the DoH listening address to 127.0.0.1.

addDOHLocal("127.0.0.1:443", "/etc/letsencrypt/live/doh.example.com/fullchain.pem", "/etc/letsencrypt/live/doh.example.com/privkey.pem", { "/" }, { doTCP=true, reusePort=true, tcpFastOpenSize=0 })

Save and close the file. Then restart DNSdist.

sudo systemctl restart dnsdist

Nginx Configuration

If you use Nginx, edit the server block file.

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

In the SSL server block, find the following directive.

listen 443 ssl;

Change it to

listen 127.0.0.2:443 ssl;

This time we make it listen on 127.0.0.2:443 because 127.0.0.1:443 is already taken by DNSdist. Save and close the file. The Nginx main configuration file /etc/nginx/nginx.conf and the default server block /etc/nginx/sites-enabled/default might include a default virtual host listening on 443, so you might need to edit this file too.

Then restart Nginx.

sudo systemctl restart nginx

Apache Configuration

If you use Apache web server, edit your virtual host file.

sudo nano /etc/apache2/sites-enabled/example.com.conf

In the SSL virtual host, change

<VirtualHost *:443>

To

<VirtualHost 127.0.0.2:443>

This time we make it listen on 127.0.0.2:443 because 127.0.0.1:443 is already taken by DNSdist. Save and close the file. Then edit the /etc/apache2/ports.conf file.

sudo nano /etc/apache2/ports.conf

Change

Listen 443

To

Listen 127.0.0.2:443

dns over https dnsdist ubuntu apache

Save and close the file. Restart Apache.

sudo systemctl restart apache2

HAProxy Configuration

Now install HAproxy.

sudo apt install haproxy

Start HAProxy

sudo systemctl start haproxy

Edit configuration file.

sudo nano /etc/haproxy/haproxy.cfg

If you use Nginx, copy and paste the following lines to the end of the file. Replace 12.34.56.78 with the public IP address of your server. Replace doh.example.com with the domain name used by DNSdist and www.example.com with the domain name used by your web server.

frontend https
   bind 12.34.56.78:443
   mode tcp
   tcp-request inspect-delay 5s
   tcp-request content accept if { req_ssl_hello_type 1 }

   use_backend dnsdist if { req_ssl_sni -i doh.example.com }
   use_backend nginx if { req_ssl_sni -i www.example.com }
   use_backend nginx if { req_ssl_sni -i example.com }

   default_backend dnsdist

backend dnsdist
   mode tcp
   option ssl-hello-chk
   server dnsdist 127.0.0.1:443

backend nginx
   mode tcp
   option ssl-hello-chk
   server nginx 127.0.0.2:443 check

If you use Apache, copy and paste the following lines to the end of the file. Replace 12.34.56.78 with the public IP address of your server. Replace doh.example.com with the domain name used by DNSdist and www.example.com with the domain name used by your web server.

frontend https
   bind 12.34.56.78:443
   mode tcp
   tcp-request inspect-delay 5s
   tcp-request content accept if { req_ssl_hello_type 1 }

   use_backend dnsdist if { req_ssl_sni -i doh.example.com }
   use_backend apache if { req_ssl_sni -i www.example.com }
   use_backend apache if { req_ssl_sni -i example.com }

   default_backend dnsdist

backend dnsdist
   mode tcp
   option ssl-hello-chk
   server dnsdist 127.0.0.1:443

backend apache
    mode tcp
    option ssl-hello-chk
    server apache 127.0.0.2:443 check

Save and close the file. Then restart HAproxy.

sudo systemctl restart haproxy

In the configuration above, we utilized the SNI (Server Name Indication) feature in TLS to differentiate VPN traffic and normal HTTPS traffic.

  • When doh.example.com is in the TLS Client Hello, HAProxy redirect traffic to the DNSdist backend.
  • When www.example.com is in the TLS Client Hello, HAProxy redirect traffic to the apache/nginx backend.
  • If the client doesn’t specify the server name in TLS Client Hello, then HAproxy will use the default backend (DNSdist).

You can test this setup with the openssl tool. First, run the following command multiple times.

echo | openssl s_client -connect your-server-IP:443 | grep subject

We didn’t specify server name in the above command, so HAproxy will always pass the request to the default backend (DNSdist), and its certificate will be sent to the client. Next, run the following two commands.

echo | openssl s_client -servername www.example.com -connect your-server-IP:443 | grep subject

echo | openssl s_client -servername doh.example.com -connect your-server-IP:443 | grep subject

Now we specified the server name in the commands, so HAproxy will pass requests according to the SNI rules we defined.

When renewing Let’s Encrypt certificate for your website, it’s recommended that you use the http-01 challenge instead of tls-alpn-01 challenge, because HAproxy is listening on port 443 of the public IP address, so it can interfere with the renewal process.

sudo certbot renew --preferred-challenges http-01

Hide Your DoH Resolver Behind Cloudflare

Some time ago, the IP address of my VPS is blocked by the Great Firewall of China. I can unblock it by hiding my DoH resolver’s IP address behind Cloudflare CDN (Content Delivery Network). Generally speaking, the Great Firewall of China doesn’t block Cloudflare IP addresses, because doing so will cause collateral damage.

Set Up Your Own VPN Server

To bypass national firewalls and unblock Google, Facebook, Twitter and other websites, I recommend using the HTTPS-based VPN protocol: OpenConnect VPN.

HTTPS handshake

Want to know how the server and client establish a secure HTTPS connection? Read the article below to learn about HTTPS handshake.

Wrapping Up

I hope this tutorial helped you set up a DNS over HTTPS 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: 9 Average: 4.8]

42 Responses to “Set Up DNS over HTTPS (DoH) Resolver on Ubuntu with DNSdist

  • Hi, thanks for the great guide. It worked if I set the DoH to “https://doh.example.com” however just “doh.example.com” does not work for me. On some devices I am not allowed to add “https://” so I was wondering if there is a solution to my problem? I did setup HAProxy. Thanks for your help.

    • Xiao Guoan (Admin)
      4 years ago

      Maybe you can configure the web server (Apache/Nginx) to redirect HTTP requests to HTTPS (301 redirect).

  • Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Plugins selected: Authenticator standalone, Installer None
    Obtaining a new certificate
    Performing the following challenges:
    http-01 challenge for moodle.ac.id
    Cleaning up challenges
    Problem binding to port 80: Could not bind to IPv4 or IPv6.
    sa@sa:~$ sudo certbot certonly –standalone –preferred-challenges http –agree-tos –email [email protected] -d moodle.ac.id

    i have any problem, can help me..!! thanks

    • Xiao Guoan (Admin)
      4 years ago

      If you have a web server running, then don’t use the standalone method, because the web server listens on port 80.

  • Gennady
    4 years ago

    Please tell me, if I enter any request in the browser on the client’s computer, will my provider be able to determine this request? Are responses from the system encrypted or requests too? I want to understand how my request from the desktop is encrypted if this request is executed BEFORE receiving this very request in the DNS.

    • Xiao Guoan (Admin)
      4 years ago

      If you configure DNS over HTTPS in the web browser, then only DNS requests initiated by the web browser will be encrypted.
      Systemd currently supports DNS over TLS. DNS over HTTPS support is on the way. https://github.com/systemd/systemd/issues/8639

      If you want the whole system’s DNS traffic encypted, you can use Stubby.

      Your ISP will only know that your computer sends requests to your DNS resolver, but can’t see the content, because the connection is encrypted. They don’t even know that you are sending DNS requests over a TCP connection.

  • Gennady
    4 years ago

    Is it possible to set up a website (php, html) on Apache on doр.example.com or example.com domains, or does this require a separate subdomain?

  • Gennady
    4 years ago

    I would be very glad if you could write an example of setting up a server on the QUIC protocol, for example, to make a setting on the litespeed server.

  • Hi Linuxbabe, This works really well. How to add ocspResponses and custom headers in the DoH configuration?

  • ffuentes
    4 years ago

    Hi, I tried to install this on a ubuntu server vps on Oracle Cloud and it doesn’t work. The main issue is certificates. When doing tests on certificates haproxy sometimes shows one and sometimes the other. The only differences with this example domain is that I have two subdomains on a server instead of three and that Oracle Cloud doesn’t show the public ip directly so you cannot bind directly to it but rather to its private ip but it’s been working so far when it was just a nginx server so it does have connectivity.

    • Xiao Guoan (Admin)
      4 years ago

      If your server is in a VPC network, configure HAProxy to bind to the private IP address.

  • IMRON HS
    3 years ago

    Hai Xiao, on this step “Make DNSdist and web server use port 443 at the same time” (I used Apache):

    ls /etc/apache2/sites-enabled/
    000-default.conf  doh.gie.co.id.conf
    

    I just have two file in directory /sites-enabled/, how I can chane the SSL virtual host?
    becouse my doh.gie.co.id.conf file just have this line:

    
            ServerName doh.gie.co.id
    
            DocumentRoot /var/www/dnsdist
    
    

    Please help, thank you..

    • IMRON HS
      3 years ago

      Sorry, thisi my Screenshoot file doh.gie.co.id.conf

    • Xiao Guoan (Admin)
      3 years ago

      If your Apache web server just have two virtual hosts: 000-default.conf and doh.gie.co.id.conf, then you don’t have a virtual host listening on TCP port 443. You just need to edit the /etc/apache2/ports.conf file.

      sudo nano /etc/apache2/ports.conf

      Change

      Listen 443

      To

      Listen 127.0.0.2:443

      Save and close the file. Restart Apache.

      sudo systemctl restart apache2

      PS: If you set up an SSL virtual host in Apache in the future, you need to edit the SSL virtual host file and change VirtualHost *:443 to VirtualHost 127.0.0.2:443.

    • Xiao Guoan (Admin)
      3 years ago

      Yes.

  • Hi,
    Great article! I’m close but missing one part. I’m running:

    # dnsdist –check-config

    And get:

    Fatal Lua error: [string “chunk”]:7: Caught exception: addDOHLocal() called but DNS over HTTPS support is not present!

    Any help would be great.

    • Xiao Guoan (Admin)
      3 years ago

      Which version of Ubuntu are you using?

      • 16.04

        I had to rebuild dnsdist with DOH enabled. Not too bad.

        Now:

        echo | openssl s_client -connect :443

        Seems to work. But dog does not:

        $ dog apple.com –https @https:///dns-query
        Error [tls]: The certificate was not trusted.

        Any suggestions? Or another way to test.
        Thanks!

  • Thanks again. Got everything working, but I still get a lot of:

    dns named[13788]: REFUSED unexpected RCODE resolving 'www.facebook.com/TYPE65/IN'

    For example. Is there a way to have bind forward the type 65 requests to dnsdist so it gets resolved?

    Thanks,
    Jeff

  • Fatal error: [string “chunk”]:13: attempt to call global ‘RDRule’ (a nil value)

  • Fatal error: [string “chunk”]:13: attempt to call global ‘addDOHlocal’ (a nil value)

  • Darshan Rooprai
    2 years ago

    Hi, Excellent article. I have just one query. I need to see the actual IP addresses trying to use doh and their DNS request. I am getting 127.0.0.1: in the syslog.

  • I use pi-hole with unbound configure, and now configure DoH, now Why is my dnsdist listen on “a.root-servers.net” every second?

    • Xiao Guoan (Admin)
      2 years ago

      dnsdist doesn’t listen on “a.root-servers.net”. It sends DNS queries to “a.root-servers.net”.

      You should learn how recursive DNS works.

      The DNS system can be divided into three tiers. They are:

      There’s another class of DNS servers usually called local DNS server whose IP address is specified on your operating system.

      When your browser connects to a website say example.com, the browser first queries your local DNS server to get the IP address of example.com.

      • If the local DNS server doesn’t have the A record of example.com, it will query one of the root DNS servers.
      • The root DNS server will say: I don’t have the A record but I know the top-level domain DNS server which is responsible for .com domains.
      • Then your local DNS server query the top-level domain DNS server which is responsible for .com domains. The TLD DNS server will respond: I don’t know either but I know which DNS server is authoritative for example.com.
      • So your local DNS server queries the authoritative DNS server. Because the actual DNS record is stored on that authoritative DNS server, so it will give your local DNS server an answer.

      Then this query result is cached on your local DNS server but it can be outdated. When the TTL time has expired, your local DNS server will update the query result from the authoritative DNS server.

      When someone registers a domain name, he/she can specify which DNS server is the authoritative DNS server. This information is called an NS record. The NS record will tell a top-level domain DNS server which nameserver holds the domain’s A record, MX record, etc.

  • Ok, thanks for the reply, why did it send the request every second? Query is way too much, Dnsdist is not a quiet program? Because Unbound is not doing it like that.

    • Xiao Guoan (Admin)
      2 years ago

      Dnsdist needs to query the TLS certificate status. That’s why.

  • DerAlSem
    2 years ago

    Tried to add for 22.04:

     echo "deb [arch=amd64] http://repo.powerdns.com/ubuntu jammy-dnsdist-15 main" | sudo tee /etc/apt/sourc
    es.list.d/pdns.list
    deb [arch=amd64] http://repo.powerdns.com/ubuntu jammy-dnsdist-15 main
    
    sudo apt update
    Ign:1 http://repo.powerdns.com/ubuntu jammy-dnsdist-15 InRelease
    Err:2 http://repo.powerdns.com/ubuntu jammy-dnsdist-15 Release
      404  Not Found [IP: 188.166.116.224 80]
    Hit:3 http://ru.archive.ubuntu.com/ubuntu jammy InRelease
    Get:4 http://ru.archive.ubuntu.com/ubuntu jammy-updates InRelease [109 kB]
    Get:5 http://ru.archive.ubuntu.com/ubuntu jammy-backports InRelease [99.8 kB]
    Get:6 http://ru.archive.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
    Get:7 http://ru.archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [305 kB]
    Get:8 http://ru.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [126 kB]
    Reading package lists... Done
    E: The repository 'http://repo.powerdns.com/ubuntu jammy-dnsdist-15 Release' does not have a Release file.
    N: Updating from such a repository can't be done securely, and is therefore disabled by default.
    N: See apt-secure(8) manpage for repository creation and user configuration details.
    

    Any advise, please?

    • Xiao Guoan (Admin)
      2 years ago

      repo.powerdns.com doesn’t support Ubuntu 22.04 now. You can install dnsdist from the default Ubuntu repository.

      sudo apt install dnsdist
  • Great guide! Only issue I have is I can’t seem to get Windows to force use it :(. I told Windows to use DNS over HTTPS “On (Manual Template)” and put in my server name ex: https://doh.example.com, but when I click Save, it errors and says “Can’t save IP settings. Check on or more settings and try again.” 🙁

  • Hello, please help, I am so confronted with this problem. How to solve it?

    • Xiao Guoan (Admin)
      2 years ago

      Check journal logs:

      sudo journalctl -eu dnsdist
      • What to do with it
        Fatal error:
        Error seting up TLS context for DoH listener on ‘0.0.0.0:443’ : An error occuered while trying to load the TLS server private key file: etc/letsencrypt/live/lips-good.tk/privjey

      • Error

    • jadeite
      1 month ago

      Hi, I met the same problem. Check your dnsdist config file, REPLACE ‘dns.example.com’ with the domain you configured.

  • ….

  • Hi, actually this is a hard way if you have nginx web server. I finally switched DoH processing to Nginx plus it also can be connected with any DNS service you may have (unbound, bind, even with Pi-Hole)

    Using NGINX as a DoT or DoH Gateway

  • Hi,
    I’ve followed your excellent guide and now have DoT/DoH setup and working. I’m i correct in thinking this encrypts the DNS queries between my network clients and my DNS server only.

    That is when my DNS server heads off to query root/top level servers it does not use DoT/DoH. So effectively those queries are not encrypted?
    Or have i missed something/not configured the DNS server correctly for its outgoing queries?

    • jadeite
      1 month ago

      No, DoH only protect the traffic between your resolver and public resolver. The way to protect trafftic between resolver and namesever is still on the way.

  • ello ello, is it possible to use http certificate authentication on an https doh server via nginx? like the client authenticates using an ssl cert in order to allow queries?
    thanks for the cool tutorial

  • max johnson
    1 week ago

    this is really involved (for me a casual virtualbox user). Is neither DoH nor Dot native to ubuntu [xbuntu]?

    • Guoan Xiao (Admin)
      3 mins ago

      This article is for server admins to set up DNS over HTTPS resolver.

      If you need a simple DNS over HTTPS solution on Ubuntu Desktop, then just enable DNS over HTTPS in the Firefox web browser. Go to Firefox Settings -> Privacy & Security -> DNS over HTTPS, and choose the level of protection. I use the Default Protection, because I use VPN all the time. Firefox won’t use DNS over HTTPS when VPN is active.

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.

The maximum upload file size: 2 MB. You can upload: image. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop file here