arrow_upward

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to Set up Your Own DoH Server on CentOS 8
#1
First off, I must confess that this HowTo is nothing but a poor man's way to deploy a fully functional DoH server. It's basically my way of utilizing the available resources at hand on @Neoon's NAT-VPS on his NanoKVM platform; thus if we forget about the transparent reverse-proxy (HAProxy) at the NAT-gate, this implementation is basically concerned to tight together 3 independent pieces of software:
  • A web server that will accept DoH's HTTPS requests. It will be the only one handling the SSL part of the communication (thus it's our SSL termination point,) while serving as a proxy for the DoH server over HTTP.
  • A DoH server, running in the background and doing the grunt work of translating Wireformats between HTTP and UDP, conforming to the IETF DNS-over-HTTPS (RFC 8484).
  • A DNS resolver, handling the name resolution part of this setup.

Systemd-resolved as the DNS resolver
Please refer to the 'A Case for systemd-resolved as the default DNS Resolver' thread for more details on this (I did specifically created it, because it deserved to be discussed thoroughly.) In here, I'll just publish systemd-resolved configuration file -located at /etc/systemd/resolved.conf- that's needed for this use case:
#  This file is part of systemd.
#  (..........)

[Resolve]
DNS=9.9.9.9
FallbackDNS=1.1.1.1 8.8.8.8
#Domains=
LLMNR=no
#MulticastDNS=no
DNSSEC=allow-downgrade
DNSOverTLS=opportunistic
#DNSOverTLS=yes
Cache=yes
#DNSStubListener=udp

Now, you're supposed to start the service and enable it permanently (if you choose to), then by running this command, you'll see the service current status:
[root@vps ~]# resolvectl   [OR  systemd-resolve --status]

Global
      LLMNR setting: no
MulticastDNS setting: no
 DNSOverTLS setting: opportunistic
     DNSSEC setting: allow-downgrade
   DNSSEC supported: yes
        DNS Servers: 9.9.9.9
Fallback DNS Servers: 1.1.1.1
                     8.8.8.8
         DNSSEC NTA: 10.in-addr.arpa
                     16.172.in-addr.arpa
                     168.192.in-addr.arpa
                     17.172.in-addr.arpa
                     18.172.in-addr.arpa
                     19.172.in-addr.arpa
                     20.172.in-addr.arpa
                     21.172.in-addr.arpa
                     22.172.in-addr.arpa
                     23.172.in-addr.arpa
                     24.172.in-addr.arpa
                     25.172.in-addr.arpa
                     26.172.in-addr.arpa
                     27.172.in-addr.arpa
                     28.172.in-addr.arpa
                     29.172.in-addr.arpa
                     30.172.in-addr.arpa
                     31.172.in-addr.arpa
                     corp
                     d.f.ip6.arpa
                     home
                     internal
                     intranet
                     lan
                     local
                     private
                     test
(...............)

As a test:
[root@vps ~]# dig google.com

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45774
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;google.com. IN A

;; ANSWER SECTION:
google.com. 299 IN A 172.217.22.14

;; Query time: 66 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Sat Feb 08 13:33:49 +01 2020
;; MSG SIZE  rcvd: 55

# A second time, to test the caching feature:
[root@natty ~]# dig google.com

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42439
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;google.com. IN A

;; ANSWER SECTION:
google.com. 44 IN A 172.217.22.14

;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Sat Feb 08 13:38:03 +01 2020
;; MSG SIZE  rcvd: 55

To get an idea on the resolver stats, run this:
[root@vps ~]# systemd-resolve --statistics
DNSSEC supported by current servers: yes

Transactions
Current Transactions: 0
 Total Transactions: 11

Cache
 Current Cache Size: 7
         Cache Hits: 3
       Cache Misses: 8

DNSSEC Verdicts
             Secure: 4
           Insecure: 9
              Bogus: 0
      Indeterminate: 0

That should be all for the DNS resolver part (for more specifics on systemd-resolved, check the thread mentioned above.)

Setting up a DoH Server
if we run a search on Gihub public repositories on IETF-compliant DoH, three results are resturned, but m13253/DNS-over-HTTPS seems to be the most mature.

Thus we'll be using m13253/DNS-over-HTTPS as our DoH-server, which supports the following features:
  • IPv4 / IPv6
  • EDNS0 large UDP packet (4 KiB by default)
  • EDNS0-Client-Subnet (/24 for IPv4, /56 for IPv6 by default)

To use m13253/DNS-over-HTTPS, we have to first install Google's Golang. On Centos 8, we simply have to issue the following command as an admin:
dnf module -y install go-toolset

To verify that Go is installed and configured as it should, we run:
[root@natty ~]# go version
go version go1.12.12 linux/amd64

#go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/root/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/lib/golang"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="......................."

As a standard user, we'll compile m13253/DNS-over-HTTPS following these steps:
mkdir -p temp && cd temp
git clone https://github.com/m13253/dns-over-https.git --depth=1
cd dns-over-https
make
sudo make install

The binary should then be installed in the /usr/local/bin but the config file is located in the /etc/dns-over-https/ folder. The install also makes available the doh-server own systemd service file to control it in the standard way.

To undo the installation step, simply run:
sudo make uninstall

Now, to link our DoH-server to our DNS resolver listening on 127.0.0.53:53/udp, we have to make just one edit to the '/etc/dns-over-https/doh-server.conf' file:
vi /etc/dns-over-https/doh-server.conf

(..............................)
upstream = [
   #"udp:1.1.1.1:53",
   #"udp:1.0.0.1:53",
   #"udp:8.8.8.8:53",
   #"udp:8.8.4.4:53",
   "udp:127.0.0.53:53"
]
(..................................)

If you want to completely offload all the DNS resolution step to an external public DNS server, uncomment the addresses above.

Now, that we have our configuration as we wanted, time to start and enable the server:
systemctl start doh-server
systemctl enable doh-server

If we check, our running services, we would find that our doh-server is listening on port 8053, as set in its config file.
[root@natty ~]# netstat -tulpn|grep doh-server
tcp        0      0 127.0.0.1:8053          0.0.0.0:*               LISTEN      14825/doh-server    
tcp6       0      0 ::1:8053                :::*                    LISTEN      14825/doh-server

At this point, we're done with the DOH-server part.


Apache 2.4 as the Web Server of this Stack
Given that I'm already running Apache 2.4 as my Web server, I won't use Nginx although it's the best suited for this job.

HTTPD is is running in mod_event and has many virtual hosts among them our generic 'doh.example.com' with the following config file:

<VirtualHost *:443>
  ServerName doh.example.com
  ServerAdmin [email protected]
  UseCanonicalName off

  <IfModule http2_module>
    Protocols h2 http/1.1
  </IfModule>

   SSLEngine on
   Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;"

  ProxyRequests off
  # RequestHeader set X-Forwarded-Proto "https"
  ProxyPreserveHost On
  <Location />
     SSLRequireSSL
  </Location>
     ProxyPass /dns-query http://127.0.0.1:8053/dns-query
     ProxyPassReverse /dns-query http://127.0.0.1:8053/dns-query
</VirtualHost>

With this last step, check your httpd config ( httpd -t ) and reload your web server.

Now, if you send this query to your doh.example.com you'll get an answer in the json format:
curl -s "https://doh.example.com/dns-query?name=google.com&type=A" | python -m json.tool

{
   "Status": 0,
   "TC": false,
   "RD": true,
   "RA": true,
   "AD": false,
   "CD": false,
   "Question": [
       {
           "name": "google.com.",
           "type": 1
       }
   ],
   "Answer": [
       {
           "name": "google.com.",
           "type": 1,
           "TTL": 299,
           "Expires": "Sat, 08 Feb 2020 14:07:32 UTC",
           "data": "172.217.22.14"
       }
   ]
}

If you make it till this stage successfully, Congratulation, now you can test it with Firefox :-)

Testing your Custom DoH Server with Firefox
In the about:config tab, type: network.trr; a list of directives will show. We are interested in the 'network.trr.custom_uri' and 'network.trr.bootstrapAddress'(only relevant when network.trr.mode===3), which you'll set like this:
network.trr.custom_uri         https://doh.example.com/dns_query
network.trr.bootstrapAddress  IP_address_Custom_DoH

That's ALL there is to it.

Now!... Why bother you may ask?... Privacy!.. I would say!.. No one can handle your data as faithfully and diligently as you would. At least that's the assumption of this HowTo :-)
VirMach's Buffalo_VPS-9 Holder (Dec. 20 - July 21)
microLXC's Container Holder (july 20 - ?)
VirMach's Phoenix_VPS-9 Holder (Apr. 20 - June 20)
NanoKVM's NAT-VPS Holder (jan. 20 - ?)
#2
You could add part on DoT .. by tls proxy (nginx, stubby can work) or server with direct support.

People can use their own private dns in Android pi 9 and above with that.
Sincere Thanks to VirMach for my VPS9. Also many thanks to Shadow Hosting and cubedata for the experiences I had with their VPSs.
#3
(02-08-2020, 06:08 PM)rudra Wrote: You could add part on DoT .. by tls proxy (nginx, stubby can work) or server with direct support.

I've exclusively reserved this thread for DoH -as the title suggests- because of the fact that my HowTos are reports of actual implementations of projects on live VPSes. As of today, the only personal live system I've got is the one on NanoKVM platform which , as you may know, is a NAT-VPS. That means I don't have access to port 853, thus standard DoT deployment is impossible there.

It's for that reason that I didn't switch Apache Web Server (2.4.41) with Nginx. Nginx can be set to proxy both DoH and DoT traffics in the same setup. In the DoT mode, nginx-mod-stream package is needed (libnginx-mod-stream package in Debian) to
proxy plain TCP over to your DNS resolver solution
(systemd-resolved, Unbound, Bind/named, etc....) In this situation too Nginx should handles SSL termination.

(02-08-2020, 06:08 PM)rudra Wrote: People can use their own private dns in Android pi 9 and above with that.
Indeed, a private DoT server is ideal for people with devices supporting DoT natively (like Android Pie -9), or people in my situation with DNS issues with their ISP (check Heads-Up: Firefox rolling DNS-over-HTTPS (DoH) for the background story.)

Thanks for your input @rudra!
VirMach's Buffalo_VPS-9 Holder (Dec. 20 - July 21)
microLXC's Container Holder (july 20 - ?)
VirMach's Phoenix_VPS-9 Holder (Apr. 20 - June 20)
NanoKVM's NAT-VPS Holder (jan. 20 - ?)
#4
This is a follow-up post aiming at addressing the use of Nginx as the WebServer in the DOH Server Setup, instead of the Apache WebServer used in the OP.

The Apache WebServer (httpd) is the grand-daddy of WebServers; I like it a lot and has my complete trust. BUT, there are just situations where you can still use it but behind the scene. This is the case for the setup I'm intending to implement on my newly deployed CentOS-8 on VirMach's Phoenix-based VPS-9. In this setup, I'm using Nginx as the public-facing WebServer where all HTTPS connections will terminate and serving as both a static web server and a reverse-proxy for a bunch of services running in the host, among them httpd, nodejs, etc... The intent of such setup is to leverage the power of Nginx as an Asynchronous web server.

Enter the question of how to rewrite the OP with this adjustment. The answer is that everything stays the same except for the third section which should be replaced by the following:

Nginx as the Reverse-Proxy in our DoH Server Stack
In this post I'll skip the actual TLS setup steps [2], and will just present the actual configuration of the server block of our generic 'doh.example.com':
server {
       listen 443 ssl http2;
       server_name doh.example.com;
       root /tmp/NOEXIST;

     location / {
           return 404;
      }

     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://server 127.0.0.1:8053/dns-query ;
       }

   add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
   ssl_certificate /etc/letsencrypt/live/doh.example.com/fullchain.pem; # managed by Certbot
   ssl_certificate_key /etc/letsencrypt/live/doh.example.com/privkey.pem; # managed by Certbot
   include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
   ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

# Enable OCSP Stapling, point to certificate chain
   ssl_trusted_certificate "/etc/letsencrypt/live/doh.example.com/fullchain.pem";
}


Notes:
[1]-As the title suggests, I've exclusively reserved this thread for DoH. I'll post another tutorial about a setup where Nginx is proxying both DoT and DoH queries.
[2]-In the above mentioned upcoming titurial, I'll discuss the TLS setup in the Nginx context.

Stay tuned!
VirMach's Buffalo_VPS-9 Holder (Dec. 20 - July 21)
microLXC's Container Holder (july 20 - ?)
VirMach's Phoenix_VPS-9 Holder (Apr. 20 - June 20)
NanoKVM's NAT-VPS Holder (jan. 20 - ?)



person_pin_circle Users browsing this thread: 2 Guest(s)
Sponsors: VirMach - Host4Fun - CubeData - Evolution-Host - HostDare - Hyper Expert - Shadow Hosting - Bladenode - Hostlease - RackNerd - ReadyDedis - Limitless Hosting