So in a litte while Google Chrome is going to enable DNS over HTTPs, and Firefox has already enabled it, by default!
So I think to myself “myself, do you want cloudflare watching all your DNS queries?” - nope is the the answer!
Right, so I’ll build my own DNS over HTTPs (DoH) server.

So what I’m going to do is:

  • Setup nginx to act as a front end proxy.
  • Build and install a go package called doh-server
  • Check that it all works 👨‍💻
  • Configure my browser

This doc assumes you have a working DNS server (I use bind9), and you can sort your own SSL certs for nginx.
It also assumes you know your way around a command line 🤪

Set up nginx

So I’m not going to talk you through how to install nginx, or get it running, that is beyond the scope of this doc.
I’m also not going to tell you how to setup Let’s Encrypt, that is also outside the scope of this doc.

So let’s get straight into the config:

upstream dns-backend {
    server 127.0.0.1:8053;
    keepalive 30;
}
server {
    listen 80;
    listen [::]:80;

    location /.well-known/acme-challenge {
        alias /var/www/dehydrated;
    }
    root /var/www/html;

    server_name ns1.example.com;
    return 301 https://$host;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    location /.well-known/acme-challenge {
      alias /var/www/dehydrated;
    }
    ssl_certificate /etc/dehydrated/certs/ns1.example.com/fullchain.pem;
    ssl_certificate_key /etc/dehydrated/certs/ns1.example.com/privkey.pem;

    # enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # disables all weak ciphers
    ssl_ciphers 'AES128+EECDH:AES128+EDH';

    ssl_prefer_server_ciphers on;

    root /var/www/html;

    index index.html index.htm index.nginx-debian.html;

    server_name ns1.example.com;
    access_log /var/log/nginx/ns1.example.com-access.log;
    error_log /var/log/nginx/ns1.example.com-error.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_set_header Connection "";
        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;
    }

}

So let me talk you through the config:
I start by creating a config that points to our, not yet installed, doh-server. That server is going to be running on port 8053:

upstream dns-backend {
    server 127.0.0.1:8053;
    keepalive 30;
}

Next I’ve got a server block for port 80 (non-secure / non https).
It has some config for my Let’s Encrypt SSL cert bot (dehydrated), and redirects everthing to the SSL version of the website (return 301):

server {
    listen 80;
    listen [::]:80;

    location /.well-known/acme-challenge {
        alias /var/www/dehydrated;
    }
    root /var/www/html;

    server_name ns1.example.com;
    return 301 https://$host;
}

and lastly, the SSL version config, includng the reverse proxy bit which I will repeat here (because it is the important bit):

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_set_header Connection "";
    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;
}

So assuming that you have your SSL certs sorted, and your nginx config is all good you should be able to start nginx 🥰

Install doh-server

so you’ll need some build packages, so let’s install some build tools:
aptitude install curl software-properties-common build-essential git

And you’re going to need to get your golang environment setup:
Install Go on Debian Buster

now let’s install the DoH server:

git clone git@github.com:m13253/dns-over-https.git
cd dns-over-https/
make
make install

Now let’s config the DoH server:
The DoH server has a config in /etc/dns-over-https/doh-server.conf
You want to change the upstream to use 127.0.0.1:53 and that’s about it, here’s what I’ve got:

listen = [
    "127.0.0.1:8053",
    "[::1]:8053"
]
local_addr = ""
cert = ""
key = ""
path = "/dns-query"
upstream = [
	"127.0.0.1:53"
]
timeout = 10
tries = 3
tcp_only = false
verbose = false
log_guessed_client_ip = false

Once you’ve got that saved you can restart the DoH server with systemctl restart doh-server

Testing the install

So let’s recap:
We have:

  • Configured nginx and got it running nicely, forwarding DoH requests
  • Installed a DoH server
  • We already had a DNS resolver installed and running, right? 🧐

Now let’s do some testing.

The DoH server returns JSON, so you can just test in your browser:
https://ns1.example.com/dns-query?name=example.org&type=A

Or, since you’re already working in a terminal (and this website is called Terminal Addict 😇)

curl -s "https://ns1.example.com/dns-query?name=example.org&type=A" | python -m json.tool

You should get a JSON response like the following:

{
    "AD": true,
    "Answer": [
        {
            "Expires": "Thu, 12 Sep 2019 22:59:07 UTC",
            "TTL": 7090,
            "data": "93.184.216.34",
            "name": "example.org.",
            "type": 1
        },
        {
            "Expires": "Thu, 12 Sep 2019 22:59:07 UTC",
            "TTL": 7090,
            "data": "A 8 2 86400 20190921055451 20190831145703 31036 example.org. r4fWzdiYS7Px0qXX+7cHtPsj2a3lFhmeM4OXvOsDd2WorpP3Na/FupUFozdp2ao7xyguW+tZSJEI01dKuMt5MDfmJR4cN2n+IiRMvLvQuG60SnQNRBmOTTwca79GNTv5To8rxU5kSixH1liyho2/c/hvIqZKajHs6Bxr460A/RM=",
            "name": "example.org.",
            "type": 46
        }
    ],
    "Authority": [
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "l.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "j.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "h.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "e.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "f.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "g.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "k.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "b.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "d.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "i.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "a.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "m.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "c.root-servers.net.",
            "name": ".",
            "type": 2
        },
        {
            "Expires": "Thu, 12 Sep 2019 21:18:35 UTC",
            "TTL": 1058,
            "data": "NS 8 0 518400 20190925170000 20190912160000 59944 . E7Vt9bijsTA8J2m5mcI9mc7uyk2PAbjtuDS4zGFaDVpVP4UyFFPYrttCA5CrXFurVN+Qf0nzB31pLzPfrgnY2xqe2S0Pm7Qq+t7rCp/Q1mNp3JbZG4fYxRXoz9uz4zSCaXDJ9WP4zrWrYd++ZGuUs4DXawg9qo0QhX5KRnXehDM9WkjY3JM61tw8CN5KS1ODov7Nw5qZI2uJ69npPnElm163cePQgZTkoGA8AO5vgmgHVbZZqQ3GkxNa8lzHNH/G66xEvPXLe03h6QptCHcOugU6SwCzmxbt2hQnvbQXZk4eGz330CmwlwVPEAKP3VwWsruEkU+LWBIohDg/0Yk6Dg==",
            "name": ".",
            "type": 46
        }
    ],
    "CD": false,
    "Question": [
        {
            "name": "example.org.",
            "type": 1
        }
    ],
    "RA": true,
    "RD": true,
    "Status": 0,
    "TC": false,
    "edns_client_subnet": "103.123.164.0/0"
}

Configure your browser

Well in Firefox this is pretty easy.
Search your preferences / settings in Firefox for DNS.

FF-DoH-settings.png

In Google Chrome the setting will be released in version 78 I’m told, I guess I’ll keep an eye out 🙃

Conclusion

So, maybe you’re super sentive about people spying on you, maybe you like playing with new gadgets / tech. Who knows.
But, I certainly don’t trust cloudflare enough to be giving them my browser DNS queries!

So we’ve now got DNS over HTTPs to use, and we own all our own history / data 👍

Comments

Paul

If you are interested as a user in “just using a private DNS server” and not building you own, let me know.
I might build a public facing DoH server for people to use.

Leave a Comment

Liked what you've read?

  1. Leave me a message, or
  2. Drop me an email

It only takes a second of your time, but it means the world to me.
Don't comment on Facebook, comment here !

I don't keep any of your information, I only ask for a name, and an email to stop spammers!
Pretty please !! :)

Your email address will not be published. Required fields are marked *

You won't be notified of replies, so come back and visit again!

⇑ Top