02-08-2020, 02:39 PM
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:
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:
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:
As a test:
To get an idea on the resolver stats, run this:
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:
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:
To verify that Go is installed and configured as it should, we run:
As a standard user, we'll compile m13253/DNS-over-HTTPS following these steps:
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:
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:
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:
If we check, our running services, we would find that our doh-server is listening on port 8053, as set in its config file.
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:
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:
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:
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 :-)
- 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:
Code: (Select All)
# 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:
Code: (Select All)
[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:
Code: (Select All)
[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:
Code: (Select All)
[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:
Code: (Select All)
dnf module -y install go-toolset
To verify that Go is installed and configured as it should, we run:
Code: (Select All)
[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:
Code: (Select All)
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:
Code: (Select All)
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:
Code: (Select All)
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:
Code: (Select All)
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.
Code: (Select All)
[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:
Code: (Select All)
<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:
Code: (Select All)
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:
Code: (Select All)
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 :-)