If you’re looking for an easy setup, checkout my review of NextDNS: DoT and DoH provider for easy ADBlocking.
Introduction
Traditional DNS queries and responses are sent over UDP or TCP without encryption. This is vulnerable to eavesdropping and spoofing (including DNS-based Internet filtering). Responses from recursive resolvers to clients are the most vulnerable to undesired or malicious changes, while communications between recursive resolvers and authoritative name servers often incorporate additional protection. (Google)
To simplify, anybody on your network, your ISP, etc … can easily spoof DNS response and decide to send you to a different website than the one you desired. Also, it has some privacy implication where anybody between you and the DNS server can know what website you visit.
Guide
The guide is divided in multiple part. The first one covers how to setup a DNS-over-HTTPS (DoH) while using dnscrypt-proxy as DNS server to answer the requests.
The second part explains how to make couple of changes to that configuration to have PiHole (dns server that block ads) as DNS server behind DoH.
The third part explains how to add DNS-over-TLS to your setup. Useful if you own Android 9 (Pie) devices.
The last part will provide you with a list of client for Windows, Linux, Android and iOS that supports DoH natively to be able to use it on all your devices.
Server
I advise you to setup a free f1 micro instance at Google Cloud Computing. You can setup anywhere you want, I only advise there because they have a good image for Ubuntu 18.04 and the f1 micro instance is free forever. All the request the machine will do will be encrypted and not accessible by Google.
Again, if you’re more familiar with Digital Ocean, AWS, etc … please use the hosting provider you know the best. For this guide, I only advise you to have a Debian based image (Debian, Ubuntu, etc …)
Architecture
Dnscrypt-Proxy
Let’s start by installing dnscrypt-proxy. This is a client that will take care to forward securely all the DNS requests your devices are going to do to your server. It uses either its own protocol (dnscrypt) or DoH.
I’m choosing Dnscrypt-proxy because it provides a fair range of server in all the world provided by the community or by big players (like google, cloudflare, etc …).
One of the contributor provides a PPA to help us install and keep the program up-to-date.
sudo add-apt-repository ppa:shevchuk/dnscrypt-proxy sudo apt install dnscrypt-proxy
Once installed, the service will start automatically with your machine.
Configuration
To make thing simple in the guide, I’m using the DNS server of Cloudflare. In case you don’t want to use their servers, you have the full list of available servers on the application website. Keep in mind you can choose more than one.
Open the file /etc/dnscrypt-proxy/dnscrypt-proxy.toml
in your favorite editor. Find the general section and change the server_name
variable.
server_names = ['cloudflare']
Once done, restart the service.
sudo systemctl restart dnscrypt-proxy
By default, the program use the socket library of systemd to listen 127.0.2.1:53
.
And that’s it for dnscrypt-proxy.
DNS-over-HTTPS server
The next step is to install the server that implement the DoH protocol to get an HTTP request and do a DNS request.
I provide 2 ways to install it, either you download the deb I provide or you compile the program (in golang) yourself.
Download
For this tutorial, I’ve taken the time to compile and package DNS-over-HTTPS (Golang) and provide a deb file easily installable.
Compile
If you prefer to build it yourself, you can follow the guide provided in the GitHub repository.
Install
If you compile it yourself, you won’t need to do this, the make install
will have already taken care of it.
sudo dpkg -i doh-server_*_amd64.deb
This will install and start the service for you.
Configuration
Open the file /etc/dns-over-https/doh-server.conf
in your favorite editor. Keep somewhere the listen IP/Port. We’ll need it when we’ll setup Nginx.
Change upstream
variable.
# HTTP listen port listen = [ "127.0.0.1:8053", "[::1]:8053", ] # TLS certification file # If left empty, plain-text HTTP will be used. # You are recommended to leave empty and to use a server load balancer (e.g. # Caddy, Nginx) and set up TLS there, because this program does not do OCSP # Stapling, which is necessary for client bootstrapping in a network # environment with completely no traditional DNS service. cert = "" # TLS private key file key = "" # HTTP path for resolve application path = "/dns-query" # Upstream DNS resolver # If multiple servers are specified, a random one will be chosen each time. upstream = [ "127.0.2.1:53", ] # Upstream timeout timeout = 60 # Number of tries if upstream DNS fails tries = 10 # Only use TCP for DNS query tcp_only = false # Enable logging verbose = false
This will tell DoH-server to use our dnscrypt-proxy to do its DNS requests.
Once done, restart the service.
sudo systemctl restart doh-server
Nginx
This section focus on installing and configuring Nginx to take care of the HTTPS part of DNS-over-HTTPS. To do this, we configure it as a reverse proxy and use let’s encrypt to generate a certificate.
Install
We add the PPA with TLS 1.3 of Nginx to get the latest stable version with TLS 1.3.
sudo add-apt-repository ppa:ondrej/nginx sudo apt install nginx-full
Configuration
This is an example of a configuration. You need to change the server_name
to the domain you’ll use for DoH. Also check that the uptream server point to doh-server ip and port. If you didn’t change anything in the configuration of doh-server, it’s already configured correctly.
For now, we don’t enable SSL, this will be done after with certbot & let’s encrypt.
upstream dns-backend { server 127.0.0.1:8053; } server { listen 80; server_name dns.example.com; root /var/www/html/dns; access_log /var/log/nginx/dns.access.log; location /dns-query { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_redirect off; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 86400; proxy_pass http://dns-backend/dns-query ; } }
Put the content of the configuratione into /etc/nginx/sites-available/dns-over-https
.
Then do a symlink to the enabled folder. Ask Nginx to check that to configuration works, and reload nginx.
sudo ln -s /etc/nginx/sites-available/dns-over-https /etc/nginx/sites-enabled/dns-over-https sudo nginx -t sudo systemctl reload nginx
And there you go, you have now Nginx that will takes care of serving HTTP request to doh-server.
Stapling
The idea is to make Nginx take care of checking if the certificate is expired and keep that information in cache. This is to avoid doing too many requests on the Certificate Authority (CA) of the certificate.
Create a new file into /etc/nginx/conf.d/stapling.conf
with the following content:
ssl_stapling on; ssl_stapling_verify on; resolver 127.0.2.1;
This will activate the stapling for all your website hosted with Nginx and using HTTPS.
Feel free to change the resolver
variable. By default I made it use the dnscrypt-proxy we configured, but you can change it to any other DNS server.
Certbot
Certbot is the tool developed by EFF to help you request SSL certificate using let’s encrypt. Not only it will generate a certificate for your domain, it will also configure Nginx for you and take care of renewing the certificate.
Install
Usually the version available in the distribution is a little old. We’re going to use the official PPA.
sudo add-apt-repository ppa:certbot/certbot sudo apt install python-certbot-nginx
Configuration
Certbot provides a variety of ways to obtain SSL certificates, through various plugins. The Nginx plugin will take care of reconfiguring Nginx and reloading the config whenever necessary:
sudo certbot --nginx -d dns.example.com
This runs certbot
with the --nginx
plugin, using -d
to specify the names we’d like the certificate to be valid for.
If this is your first time running certbot
, you will be prompted to enter an email address and agree to the terms of service. After doing so, certbot
will communicate with the Let’s Encrypt server, then run a challenge to verify that you control the domain you’re requesting a certificate for.
If that’s successful, certbot
will ask how you’d like to configure your HTTPS settings.
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
I advise to choose redirect to be sure it use only HTTPS.
SSL Defaults
Certbot comes with “good-enough” SSL defaults, but they haven’t been updated in a while. It keeps support for TLS1.0 which has been deprecated for years. No device should use it anymore. Moreover the chosen cypher list contains weak cyphers. To resolve this issue, I compiled a new configuration file for you to replace the weak defaults of Certbot.
Edit the file /etc/letsencrypt/options-ssl-nginx.conf
and replace its content by this.
# This file contains important security parameters. If you modify this file # manually, Certbot will be unable to automatically provide future security # updates. Instead, Certbot will print and log an error message with a path to # the up-to-date file that you will need to refer to when manually updating # this file. ssl_session_cache shared:le_nginx_SSL:1m; ssl_session_timeout 1440m; ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; # Enable modern TLS cipher suites ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; # The order of cipher suites matters ssl_prefer_server_ciphers on; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Then reload nginx.
sudo systemctl reload nginx
Renewal
By design, the certificate will expire in 90 days. Certbot will take care of renewing it 30 days before expiry. In the case you want to test the renewal process you can run this command.
If you remove the --dry-run
, you’ll actively ask Certbot to renew the certificate.
sudo certbot renew --dry-run
Conclusion
Congratulation you have now a DNS-over-HTTPS server running that can accept request at https://dns.example.com/dns-query
.
This conclude the first part of the guide. The second convers the differents clients available, like dnscrypt-proxy (windows/linux) and Intra (Android). And the third one how to make this DoH block advertising.
20th January 2019 at 01:55
You mixed the arguments in
sudo ln -s /etc/nginx/sites-enabled/dns-over-https /etc/nginx/sites-available/dns-over-https
nice guide otherwise. Thanks!
21st January 2019 at 17:10
Thanks for the correction. I always mix up the symlinks.
Tutorial corrected.
28th July 2019 at 06:49
A good mnemonic for this is to think of `-s` as “source” (although it’s actually symbolic).
24th January 2019 at 10:53
what are ip’s of this dns server?
25th January 2019 at 16:34
You have 2 servers.
One in USA and one in Netherlands.
USA: dns-gcp.aaflalo.me
Netherlands: dns.aaflalo.me
20th March 2019 at 13:36
Excellent tutorial. Many thanks!
17th April 2019 at 22:21
Hi Antoine,
Thank you for this guide. Everything works great.
The only minor issue for me is within the pi-hole web interface. All requests appear as coming from the same client ‘localhost’. Is there a way to fix this so all clients appear individually with their own IP address?
Thank you!
18th April 2019 at 02:48
Hello Robby,
I couldn’t find a way to have them. Since the request are always coming from the DoH-server, the dns server only see a request from localhost.
Although you could activate the log in nginx to have the ip but you won’t have the request that was made. You’d need to create your own log format to analyse the request body and have it contain the request done.
Not impossible.
18th April 2019 at 03:00
That makes sense. Thanks again for the guide and explanation.
16th May 2019 at 08:51
Howdy!
Thanks for the guide, but I swear I’ve followed it exactly as described but when I put my address in Intra on my Galaxy S8 all I get is “several DNS Queries failed”. Any tips on what I might be doing wrong?
Disclaimer: I am a complete linux scrub and probably effed up the instructions somewhere.
18th May 2019 at 19:56
I’m stuck at ‘certbot configuration’
sudo certbot –nginx -d dns.example.com
I’m assuming dns.example.com needs to be replaced..
Where does the domain name come from?
1st June 2019 at 13:18
You need to own a domain and have the ip of the subdomain pointing to your server.
Else you won’t be able to generate the SSL certificate.
27th December 2021 at 06:42
Ä°ntra kodu name yazynlar hayys
31st May 2019 at 22:28
How can I test to know that it is working properly and that my dns is encrypted?
1st June 2019 at 13:17
If you can connect got the server,m with a DoH client using the HTTPS URL, it means it’s secured.
If it wasn’t secured the client wouldn’t be able to do the requests.
1st June 2019 at 08:45
Hi. Thank you so much for useful tips.
i just wonder 2 questions as below:
1. SSL Defaults
# Enable modern TLS cipher suites
ssl_ciphers ‘…..’;
How did you get this cipher ? I have to generate this cipher from where ?
2. dns.example.com
In case of yours, this will be dns-gcp.aaflalo.me or dns.aaflalo.me.
How can it got this ? Do i have to buy my personal domain, then create dns.my.personal ?
Sorry if this questions are so fool.
1st June 2019 at 13:15
10th June 2019 at 10:00
Hi,
firstly thank you for the fantastic guide. I would like to know, if possible, how can we setup EDNS0? since i give my doh server access to my friends from different countries. I use doh-server with unbound (no dns-crypt proxy) and in the logs all requests are from 127.0.0.1.
Thank you in advance.
28th June 2019 at 17:31
Hello Nicolas,
For NGINX set X-Forwarded-For : http://www.networkinghowtos.com/howto/set-the-x-forwarded-for-header-on-a-nginx-reverse-proxy-setup/
From DoH-Server you’ll have to setup the ECS : https://github.com/m13253/dns-over-https.
Also Check EDNS Client Subnet Module Options in https://nlnetlabs.nl/documentation/unbound/unbound.conf/
I guess, in your
25th July 2019 at 04:21
Please remove my previous comment.
Hello.
This is my nginx -t result.
nginx: [warn] âssl_staplingâ ignored, host not found in OCSP responder âocsp.int-x3.letsencrypt.orgâ in the certificate â/etc/letsencrypt/live/dns.mydomain.xyz/fullchain.pemâ
nginx: [warn] conflicting server name âdns.mydomain.xyzâ on 0.0.0.0:80, ignored
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
25th July 2019 at 14:43
Previous comment removed as asked.
For the resolver IP, did you put your DNS server (or cloudflare, google, etc …) ? (see https://www.aaflalo.me/2018/10/tutorial-setup-dns-over-https-server/#Stapling )
If your server IP doesn’t work, use 1.1.1.1 and 1.0.0.1 (cloudflare).
1st October 2019 at 20:18
Hi Antoine!
This tutorial is a great help to set up my own DOH server. Using Infra with my Android devices everything works great.
I have seen that your servers are also part of the public resolvers list (https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v2/public-resolvers.md). I’m able to connect to your servers using dns-cloak on ios.
I tried to use my own servers with dns-cloak (same tutorial), but I failed so far. It stucks during connecting. Have there been any additional steps (excluding the dnscrypt stamp creation) to get it working?
Thanks in advance for your response.
Best,
Stephan
2nd October 2019 at 10:28
Hi Antoine!
Was able to fix my issues on my own. I used wrong hashes. it has to be calculated with:
$ openssl asn1parse -in doh.example.com.chain.pem -out /dev/stdout -noout -strparse 4 | openssl dgst -sha256
Still, I want to thank you for the great tutorial
18th October 2019 at 18:15
Hello Antoine,
I’ve just set up our DoH server but got this result in the end when I get the link https://ourdomain/dns-query and I’m not sure that this is the right ouput to get so could you please let me know?
{“Status”:2,”Comment”:”Invalid argument value: \”ct\” = \”\””}
Thanks.
21st October 2019 at 20:04
Hello Sean,
You need a real DoH client to do a request. Or use CURL to reproduce a DoH request.
Right now, you’ve done a an invalid DoH request on the server. But good news, it’s setup correctly.
22nd October 2019 at 17:49
Hi Antonie,
Thank you for letting me know it! Actually I’ve been learning on the DoH stuff for the first time so would you mind showing me how to set up a DoH client or use a Curl request? Please let me know. Thanks alot!
22nd October 2019 at 19:06
Hello Sean,
You have here a nice blog post talking about different way to test a DoH Server:
Troubleshooting DNS over HTTPS
Or even CloudFlare guide :
CloudFlare guide
22nd October 2019 at 19:42
Hi Antoine,
Thank you very much for your help:)
I just ran a Curl request and here’s what I got as a feedback. Could you please let me know that this is still the right outcome or there’s something wrong with my DoH server setup?
curl -D – “https://our domain/dns-query?ct&dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE”
HTTP/2 503
server: nginx/1.16.1
date: Tue, 22 Oct 2019 18:32:29 GMT
content-type: application/json; charset=UTF-8
content-length: 114
access-control-allow-headers: Content-Type
access-control-allow-methods: GET, HEAD, OPTIONS, POST
access-control-allow-origin: *
access-control-max-age: 3600
x-powered-by: DNS-over-HTTPS/2.0.1 (+https://github.com/m13253/dns-over-https)
strict-transport-security: max-age=31536000; includeSubDomains
{“Status”:2,”Comment”:”DNS query failure (read udp 127.0.0.1:59856-\u003e127.0.0.1:53: read: connection refused)”}
22nd October 2019 at 19:59
Basically, you don’t have a DNS Server running on the configured IP and port behind your DoH server. If you followed my tutorial, it means dnscrypt-proxy isn’t running or listening on 127.0.0.1 port 53.
22nd October 2019 at 20:17
Ok, here’s the output of the dnscrypt-proxy and the ports listening. Looks like the proxy is running fine but for some reason, the port 53 isn’t listening. Could you please let me know where I need to double-check?
â dnscrypt-proxy.service – DNSCrypt-proxy client
Loaded: loaded (/lib/systemd/system/dnscrypt-proxy.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2019-10-22 19:08:43 UTC; 3min 10s ago
Docs: https://github.com/jedisct1/dnscrypt-proxy/wiki
Main PID: 2058 (dnscrypt-proxy)
Tasks: 7 (limit: 2361)
CGroup: /system.slice/dnscrypt-proxy.service
ââ2058 /usr/bin/dnscrypt-proxy –config /etc/dnscrypt-proxy/dnscrypt-proxy.toml
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] dnscrypt-proxy 2.0.25
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] Wiring systemd TCP socket #0, dnscrypt-proxy.socket, 127.0.2.1:53
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] Wiring systemd UDP socket #1, dnscrypt-proxy.socket, 127.0.2.1:53
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] [cloudflare] OK (DoH) – rtt: 21ms
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] [google] OK (DoH) – rtt: 9ms
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] Sorted latencies:
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] – 9ms google
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] – 21ms cloudflare
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] Server with the lowest initial latency: google (rtt: 9ms)
Oct 22 19:08:43 dnscrypt-proxy[2058]: [2019-10-22 19:08:43] [NOTICE] dnscrypt-proxy is ready – live servers: 2
root@server:/home/ubuntu# netstat -tupl | grep 53
tcp 0 0 0.0.0.0:http 0.0.0.0:* LISTEN 1653/nginx: master
tcp 0 0 localhost:8053 0.0.0.0:* LISTEN 1690/doh-server
tcp 0 0 0.0.0.0:853 0.0.0.0:* LISTEN 1653/nginx: master
tcp 0 0 0.0.0.0:https 0.0.0.0:* LISTEN 1653/nginx: master
tcp6 0 0 [::]:http [::]:* LISTEN 1653/nginx: master
tcp6 0 0 ip6-localhost:8053 [::]:* LISTEN 1690/doh-server
22nd October 2019 at 21:40
As you can see in your log, dnscrypt-proxy listen on 127.0.2.1:53
You need to update doh-server file to redirect traffic there and then restart it.
23rd October 2019 at 17:14
Thank you Antoine!
You’re helping me a lot going through this unfamiliar setup so I got our DoH server working and verified it by using intra app saying that my connection is protected from DNS manipulation attacks.
Also, I have a question on dnscrypt-proxy. Can we set up our own nameservers and use them instead of using Cloudflare or Gooogle ones so that way not only can we have a full control of querying DNS, but also they won’t see us on what we’re doing?
Would that be possible?
Or this is what I have to implement to control all?
https://github.com/DNSCrypt/dnscrypt-proxy/wiki/How-to-setup-your-own-DNSCrypt-server-in-less-than-10-minutes
23rd October 2019 at 19:27
No Problem đ
Yes you can change the provider you use for DNS, you have the full list here:
https://dnscrypt.info/public-servers
Also, if you prefer, you can also learn how to set up an Unbound server and then all you’ll need to do is uninstall dnscrypt and update the configuration doh-server.
23rd October 2019 at 22:09
Thank you for the info as well Antoine:)
Alright, I just set up our own DNSCrypt-Proxy server by following the instruction that I sent you and maybe I can even learn how to do unbound server so what’s the advantage or disadvantage of using DNSCrypt or Unbound?
24th October 2019 at 14:56
dnscrypt server is only needed if you’re not hosting it locally. The idea is to provide your user a server on which they can connect securely.
You’re already do that when you want to provide DoH.
In other words, having Unbound in recursive mode (listening only on local host) and having DoH on top of it is enough.
Currently, you do have an unbound setup in the docker container for the DNSCrypt-Server, because DNSCrypt is another protocol to encrypt DNS request/response. It still needs a real DNS server to process the requests.
(Source: docker image jedisct1/dnscrypt-server)
In other words, you can easily remove one layer by removing DNSCrypt from the loop and having DoH-server directly connecting to unbound.
24th October 2019 at 18:22
Ok, what I’m understanding is, I have unbound and DoH server set up but they still require real nameservers.
Once we set up our own nameservers, how do I remove the DNSCrypt server and connect the DoH to unbound?
Just take the server name out from the file?
/etc/dnscrypt-proxy/dnscrypt-proxy.toml
24th October 2019 at 19:30
It’s not nameserver, it’s DNS server. You have 2 types of DNS server, proxy and recursive.
What you need to do is simply uninstall anything related to dnscrypt (server and proxy), and configure unbound (lots of tutorial online) to only accept request from localhost.
Once unbound is configured, you change the configuration of doh-server:
to use the IP and port of unbound. (127.0.0.1:53)
24th October 2019 at 19:57
Alright, thank you!
So I found these two tutorials to achieve my goal so could you please confirm that those are all that we need to set up?
– Set up DNS servers :
https://www.digitalocean.com/community/tutorials/how-to-configure-bind-as-a-private-network-dns-server-on-ubuntu-18-04
– How to set up unbound
https://www.bentasker.co.uk/documentation/linux/407-building-and-running-your-own-dns-over-https-server
24th October 2019 at 20:03
You don’t need Bind (first tutorial).
You don’t need to build unbound either …
This one is better (don’t need to do anything about pihole).
Tutorial Setting Unbound
24th October 2019 at 20:45
Ok, I’ve just installed unbound on my DoH server but it failed to start. Any idea on this?
root@:/var/lib/unbound# systemctl status unbound
â unbound.service – Unbound DNS server
Loaded: loaded (/lib/systemd/system/unbound.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Thu 2019-10-24 19:38:14 UTC; 8s ago
Docs: man:unbound(8)
Process: 14513 ExecStart=/usr/sbin/unbound -d $DAEMON_OPTS (code=exited, status=1/FAILURE)
Process: 14505 ExecStartPre=/usr/lib/unbound/package-helper root_trust_anchor_update (code=exited, status=0/SUCCE
Process: 14485 ExecStartPre=/usr/lib/unbound/package-helper chroot_setup (code=exited, status=0/SUCCESS)
Main PID: 14513 (code=exited, status=1/FAILURE)
Oct 24 19:38:14 systemd[1]: unbound.service: Main process exited, code=exited, status=1/FAILURE
Oct 24 19:38:14 systemd[1]: unbound.service: Failed with result ‘exit-code’.
Oct 24 19:38:14 systemd[1]: Failed to start Unbound DNS server.
Oct 24 19:38:14 systemd[1]: unbound.service: Service hold-off time over, scheduling restart.
Oct 24 19:38:14 systemd[1]: unbound.service: Scheduled restart job, restart counter is at 5.
Oct 24 19:38:14 systemd[1]: Stopped Unbound DNS server.
Oct 24 19:38:14 systemd[1]: unbound.service: Start request repeated too quickly.
Oct 24 19:38:14 systemd[1]: unbound.service: Failed with result ‘exit-code’.
Oct 24 19:38:14 systemd[1]: Failed to start Unbound DNS server.
FYI, I stopped the dnscrypt-proxy service and all I did is, installed the unbound package since you mentioned that I don’t need to do anything with Pi-Hole.
sudo apt install unbound
wget -O root.hints https://www.internic.net/domain/named.root
sudo mv root.hints /var/lib/unbound/
FYI, I stopped the dnscrypt-proxy service as well.
24th October 2019 at 21:57
Did you do this part of the tutorial ?
https://github.com/anudeepND/pihole-unbound#configure-unbound
25th October 2019 at 17:49
Ohk I haven’t done that yesterday but I will.
Here are some questions to clear my mind and could you please answer to them if possible?
1. Like I said, we have set up our own dnscrypt-proxy but looks like it’s still using public dns resolvers available outside in /etc/dns-over-https/doh-server.conf and that’s why we need to set up unbound. Am I correct?
[sources.’public-resolvers’]
urls = [‘https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/public-resolvers.md’, ‘https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md’]
cache_file = ‘public-resolvers.md’
minisign_key = ‘RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3’
refresh_delay = 72
prefix = ”
2. Do we need to set up our own nameservers as well?
3. We’ll host a number of PBN websites on DoH server so can we manage them via Pi-Hole set up?
9th November 2019 at 03:06
Hello Antoine Aflalo,
I installed doh-server using the command “sudo dpkg -i doh-server_*_amd64.deb”,but doh-server.service did not appear to be started. I tried to start it, but it did not work.
Here is the status:
systemctl status doh-server
â doh-server.service – DNS-over-HTTPS Server
Loaded: loaded (/lib/systemd/system/doh-server.service; enabled; vendor preset: enabled)
Active: activating (auto-restart) since Sat 2019-11-09 09:54:39 CST; 1s ago
Docs: https://github.com/m13253/dns-over-https
Process: 32120 ExecStart=/usr/local/bin/doh-server -conf /etc/dns-over-https/doh-server.conf (code=exited, status=0/SUCCESS)
Main PID: 32120 (code=exited, status=0/SUCCESS)
12th November 2019 at 04:52
What happens if you launch it manually ?
/usr/local/bin/doh-server -conf /etc/dns-over-https/doh-server.conf
12th November 2019 at 12:58
I’ve solved the problem, and I’ve done everything in the tutorial except modify “dnscrypt-proxy.toml”.(I didn’t find it)
I tried to initiate a DNS query for “www.google.com” and I sent the request as follows:
“https://mydomain/dns-query?ct&dns=d3d3Lmdvb2dsZS5jb20”
where “d3d3Lmdvb2dsZS5jb20” is base64 encoding of “www.google.com”, the output is {“Status”:2,”Comment”:”DNS packet parse failure (dns: bad rdata)”}.
I don’t know how to use it. How do I make a requestïŒ
Thanks a lot!
14th November 2019 at 23:18
Hello,
When you use CURL what do you see ?
curl -D - "https://YOUR.SERVER.COM/dns-query?ct&dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE"
You’re supposed to see a weird string with “example.com”
13th November 2019 at 16:31
HI Antoine, I have some problem while setting my DoH server. I did everything in this post but if I try to access doh server via firefox(about:config network.trr.uri = dns.xxx.xxx/dns-query) nginx throws me 400 Bad Request. Do you have any idea?
14th November 2019 at 23:17
Hello,
When you use CURL what do you see ?
curl -D - "https://YOUR.SERVER.COM/dns-query?ct&dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE"
You’re supposed to see a weird string with “example.com”
13th December 2019 at 12:01
“This conclude the first part of the guide. The second convers the differents clients available, like dnscrypt-proxy (windows/linux) and Intra (Android).”
Where is the guide that discusses client configuration as mentioned?
20th January 2020 at 17:07
I need to take the time to write the last part. Also the number of clients have change since the last time I checked.
20th January 2020 at 13:32
I can only agree: “this ist the best tut to setup doh I found. thanks for it!
I followed it step-by-step on a t2.micro from AWS in Japan. I am located in China and sick of thinking about if anybody sniffed in my dns requests. unfortunately I didn’t make it work so far.
upstream is set, resolver seems to work fine, i compiled and linked doh, nginx works, i setup a domain and a cert correctly but eventually I ended up with a bad gateway error when accessing https://dns-over-https.jens-schendel.com/dns-query with my browser (with or without vpn).
any idea?
20th January 2020 at 17:06
Did you check the log of DoH-server ?
Is it running ?
Because this sound like it’s not running or misconfigured.
21st January 2020 at 01:36
Where does doh-server log to?
found some dirs under /var/log for the proxy, but all empty – even after turning logging on with verbose = true
restarting doh-server several times and tailing syslog showed that doh-servr was not running:
tcp_only = false wasn’t recognised as a valid option! and upstream = [
“127.0.2.1:53”,
] does only work with an protocol mentioned like udp, tcp or tls
now a request in my browser is throwing a status 2 “Invalid argument value: \”ct\” = \”\”” reply.
BTW your N-Server seems to have the same problem, pls check:
https://dns.aaflalo.me/dns-query
21st January 2020 at 16:07
That means it works. The error message you’re seeing is because the request you sent is invalid but doh-server has answered it.
Now if you use a DoH client with the server, it’ll work.
29th February 2020 at 14:34
Has anyone figured out how to get real client data? Currently it only shows ‘localhost’ under ‘Top clients’ on Pihole dashboard.
Thanks!
31st March 2020 at 18:59
Great article!
For those who wants to do all of above with a docker setup:
I followed the steps in this article and created this ->
Docker Swarm based full Private DNS with DOH server: https://hub.docker.com/r/satishweb/doh-server
18th May 2020 at 02:46
On Ubuntu 20.04, with Cerbot I am seeing:
“An unexpected error occurred:
AttributeError: module ‘acme.challenges’ has no attribute ‘TLSSNI01’
Please see the logfiles in /var/log/letsencrypt for more details.”
You will need to modify line 1110 of /usr/lib/python3/dist-packages/certbot_nginx/configurator.py, change “return [challenges.HTTP01, challenges.TLSSNI01]” to return [challenges.HTTP01]”
18th May 2020 at 02:49
The other possibility is to install certbot using pip (but then you need to setup yourself the cron).
pip3 install certbot certbot-nginx
16th June 2020 at 18:19
Hi Antoine,
Thank you for your tutorial, really helpful. I’ve followed all the steps with no errors, but when trying to use DoH queries, it give me a 404 error:
# curl -D â “https://my-domain/dns-query?ct&dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE”
404 Not Found
404 Not Found
nginx/1.16.1
here’s my listening services:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 96u IPv4 96160 0t0 TCP 127.0.2.1:domain (LISTEN)
systemd 1 root 104u IPv4 96164 0t0 UDP 127.0.2.1:domain
sshd 24465 root 3u IPv4 2699996 0t0 TCP *:ssh (LISTEN)
sshd 24465 root 4u IPv6 2700007 0t0 TCP *:ssh (LISTEN)
doh-serve 25137 nobody 3u IPv6 2706537 0t0 TCP [::1]:8053 (LISTEN)
doh-serve 25137 nobody 5u IPv4 2706539 0t0 TCP 127.0.0.1:8053 (LISTEN)
nginx 25769 root 9u IPv4 2712859 0t0 TCP *:http (LISTEN)
nginx 25769 root 10u IPv6 2712860 0t0 TCP *:http (LISTEN)
nginx 25769 root 11u IPv6 2712861 0t0 TCP *:https (LISTEN)
nginx 25769 root 12u IPv4 2712862 0t0 TCP *:https (LISTEN)
nginx 25771 www-data 9u IPv4 2712859 0t0 TCP *:http (LISTEN)
nginx 25771 www-data 10u IPv6 2712860 0t0 TCP *:http (LISTEN)
nginx 25771 www-data 11u IPv6 2712861 0t0 TCP *:https (LISTEN)
nginx 25771 www-data 12u IPv4 2712862 0t0 TCP *:https (LISTEN)
nginx 25771 www-data 16u IPv4 2712902 0t0 UDP 127.0.0.1:34909->127.0.2.1:domain
dnscrypt- 26077 dnscrypt-proxy 7u IPv4 96160 0t0 TCP 127.0.2.1:domain (LISTEN)
seems that /dns-query is not found?
Thank you!
17th June 2020 at 10:45
Problem solved, was Nginx config.
Thank you!
5th February 2021 at 16:24
HI Alex, i have same issue. How did you solve it?
15th October 2020 at 16:23
I just want to understand , if I run DNS over HTTPS client as systemd, using that server listening interface at port 53 on my machine gives me DOH via cloudflared resovler , then what is the purpose of DOH-server ?
the only benefit I see if we want to run our own DOH server have have it used publically or on a private network.
15th October 2020 at 17:06
For running locally, yes you don’t need to setup your own DoH, actually, you could run your own DNS server or a pihole directly.
It’s useful if you want to make it accessible from outside your network (especially DoT for Android devices).
15th October 2020 at 17:28
if somebody wants to access from outside , running vpn would be a better idea in my opinion.
17th October 2020 at 11:50
In the case of DoT and android, it worth using it because it consumes less battery than any VPN solution.
But yes, my own setup relies heavily on wireguard VPN, only my android devices are using the DoT.
22nd November 2020 at 08:16
Hi and thanks for he amazing guide!
One question please, in my web page i see this page “Comment “Invalid argument value: \”ct\” = \”\”” ,
when i try your dns at https://dns-nyc.aaflalo.me/dns-query , i see blank page.
How can i do the same?
Thanks in advance!
9th February 2021 at 13:01
please help i keep getting errors
root@nss1:~# curl -D – https://doh.example.com/dns-query?ct&dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE
[1] 144544
root@nss1:~# HTTP/2 400
server: nginx/1.18.0
date: Tue, 09 Feb 2021 17:58:33 GMT
content-type: text/plain; charset=utf-8
400 Bad Request: malformed Host header
[1]+ Done curl -D – https://doh.example.com/dns-query?ct
9th February 2021 at 13:41
Add
-H 'accept: application/dns-json'
to your CURL command9th April 2021 at 06:18
Really nice tutorial!
I just have 1 doubt.
As google only offers free instances in the US regions (but not in Europe), and AWS only has free micro instances for the first 12 months, I was planning to update my raspi from just pihole to doh + pihole as in your guide. Using a DNS server based in US would be really bad because of the high latency and I already have a VPN set up to connect to my home lan when I’m on the go so that I can use whatever DNS I have set up there.
I imagine that in this case the HTTPS part between the client and the first DNS server (the raspberry) would be useless…right? This will take place in my internal LAN so there is no need to secure that transmission, only the part that goes then from the Pihole to the upstream DNS will be secured using DoH.
Do I understand it correctly?
I will set up NGINX to take care of the requests but keeping a local host without any SSL encryption, otherwise it is basically the same as in your guide.
Thanks again!
P.S. for the moment I’m not aware of any service that offers forever-free microinstances in the EU region. I hope this will change in the future đ
22nd April 2021 at 04:22
Hi Antoine,
Thank you for this tutorial.
Please, what is the problem?
I have installed the dependency but this problem persists
———————————————————————————————-
Makefile:61: recipe for target ‘deps’ failed
make: *** [deps] Error 1
22nd April 2021 at 04:27
Hi Antoine
Thank you for this tutorial.
Please, what is the problem?
When I run the command make and I get this error.
I have installed the deps dependency but this problem persists
———————————————————————————————————–
Makefile:61: recipe for target âdepsâ failed
make: *** [deps] Error 1
25th May 2021 at 13:12
Hi Antoine,
I am getting not getting cert renewal with error – “404 Not Found”
nginx error logs show the following. please advise why cert cannot be renewed.
2021/05/26 00:52:11 [error] 5091#5091: *448 open() “/var/www/html/dns/.well-known/acme-challenge/_Bd94gEQuBwWCYt_FTCTxSruwpeuDcbxVR5yjnYHNqM” failed (2: No such file or directory), client: 18.222.145.89, server: mydomain.com, request: “GET /.well-known/acme-challenge/_Bd94gEQuBwWCYt_FTCTxSruwpeuDcbxVR5yjnYHNqM HTTP/1.1”, host: “mydomain.com”, referrer: “http://mydomain.com/.well-known/acme-challenge/_Bd94gEQuBwWCYt_FTCTxSruwpeuDcbxVR5yjnYHNqM”
9th September 2021 at 22:47
this was a great tutorial, the only trouble I had was trying to wget your download url was a headache, would be great if you could host it or share the direct curl command.
16th February 2022 at 18:00
Hi Antoine, my DoH is working just fine (many thanks!). How can one measure the speed f the /dns-query from a browser – say Chrome. Ideally I would like to compare the speed on my VPS with that from Cloudflare. I may be slower, but need to know how much slower. Thanks.
12th October 2024 at 03:46
curl -H “accept: application/dns-json” “https://doh1.example.com/dns-query?name=google.com&type=A”
I receive error message
* Connection #0 to host doh1.example.com left intact
Unable to find the DNS parameter