Few months ago, I've made a similar work but I wanted something a little more easier to manage. Please have a look at here for my previous work.
This time, I'm gonna do pretty much the same thing but using Pi-hole as base then modify it to include unbound
and stubby
.
This way, I can use the power of Pi-hole with some additional security layers:
- Recursive DNS check (
unbound
) - DNS-over-TLS (
stubby
)
Load the Ubuntu Server image you for Raspberry Pi's from here:
Version 18.04.5:
- Hard Float (armhf): http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ubuntu-18.04.5-preinstalled-server-armhf+raspi3.img.xz
- 64 Bits (arm64): http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ubuntu-18.04.5-preinstalled-server-arm64+raspi3.img.xz
If you are using small TFT screen on your Raspberry Pi, it might not work with the latest Ubuntu Server images. In this case, you will have to fallback on the version
18.04.3
.
Version 20.04.2:
- Hard Float (armhf): http://cdimage.ubuntu.com/ubuntu/releases/20.04/release/ubuntu-20.04.2-preinstalled-server-armhf+raspi.img.xz
- 64 Bits (arm64): http://cdimage.ubuntu.com/ubuntu/releases/20.04/release/ubuntu-20.04.2-preinstalled-server-arm64+raspi.img.xz
# Update package cache
sudo apt update --fix-missing -y
# Get latest packages version
# Allow downgrades in case of incompatibilities
sudo apt dist-upgrade -y --allow-downgrades
# Clean up leading old packages
sudo apt autoremove --purge -y
# Check if reboot is required
[ -f /var/run/reboot-required ] && cat /var/run/reboot-required
bash <(curl -Ss https://my-netdata.io/kickstart.sh) all --dont-wait --disable-telemetry
cd /etc/netdata
sudo ./edit-config charts.d.conf
Add sensors=force
at the end of the file and save it.
Restart Netdata to enable Raspberry Pi temperature sensor monitoring.
sudo systemctl restart netdata ; systemctl status netdata -l
curl -sSL https://install.pi-hole.net | bash
Even we'll disable it later, just select anyone of them as displayed:
We'll add some addiotional ones later but you can already keep the default one enabled.
You can leave the default selection if you don't know or unselect the protocol you are not using.
During my initial testing on VM, it seems like Pi-hole will automatically disable the unused protocols.
You can leave the default settings or change them if necessary.
The image is not broken, I just preferred to keep the default settings private.
Acknowledge the warning if you don't know what it means otherwise read and follow the given instructions.
You can use Pi-hole without the web interface but I'd recommend to keep as it is much more convenient. You can still use the CLI if you prefer that, it is installed by default, the web interface is purely optional.
The web server will be required if you have decided to use the web interface but if you already have installed another web server, then you might have to say off
, otherwise leave it to on
.
You can decide to not use it but keeping the default setting will be helpful for debugging in case of issues.
You decide 😁. But for a private usage I'd say, level 0
otherwise for a privacy respectful public usage, level 3
.
As I said, you decide on this one, you are not forced to follow me 😉.
Once you've have defined the Privacy Mode, the configuration wizzard will disappear and the installation process will continue with the given configuration values.
You might encounter some DNS resolution issues at the end of the installation process, it is due to conflict with existing DNS resolvers but it can be solved pretty easily.
You will have around 20 seconds to do the following:
# Connect on your device
ssh user@host
# Edit the DNS resolution file
sudo nano /etc/resolv.conf
# Delete the existing content and add the following:
nameserver 127.0.0.1
Save the file and normally it should work and continue as expected.
Unfortunately on my side I didn't had time to proceed but it worked on my test VM.
If you didn't had time to do anything before the timeout, don't worry, you can still proceed to the install, you have not done all of this for nothing 😉.
Here is what to do in this case:
# Edit the DNS resolution file
sudo nano /etc/resolv.conf
# Delete the existing content, add the following and save the file
nameserver 127.0.0.1
# Test if it works
dig pi-hole.net
# If it does, run the install script again.
# Do it ASAP because the /etc/resolv.conf file will be rewritten by the responsible service until is it disabled later.
curl -sSL https://install.pi-hole.net | bash
Once reloaded, the installer script will prompt for execution mode, you can safely decide to leave the default selection or reconfigure everything.
As you can see below, the installation finished properly as expected 🎉.
As said previously, the /etc/resolv.conf
file will be rewritten automatically by the responsible and conflicting service and will need to be disabled to let Pi-hole working properly without any further similar issues.
In my case, the culprit is the systemd-resolved
service that is using a stub
file instead of the real /etc/resolv.conf
file. You can recognize it by the default content of the /etc/resolv.conf
file or the fact that it's 127.0.0.53
that is used by default instead of 127.0.0.1
.
If you are in a similar situation, run the following commands:
# Check the service status
systemctl status systemd-resolved.service -l
# Disable the service if running
sudo systemctl disable --now systemd-resolved.service ; systemctl status systemd-resolved.service -l
# Remove existing symlink
sudo rm -fv /etc/resolv.conf
# Edit the DNS resolution file
sudo nano /etc/resolv.conf
# Delete the existing content, add the following and save the file
nameserver 127.0.0.1
# Test if it works
dig pi-hole.net
You can also
dig
any other domains if you prefer or either usegetent
orhost
instead ofdig
, the idea of this test is to see if the DNS resolution is repaired or not.
Normally, you should be able to continue without any other similar issues.
If you are like me and had the DNS resolution broken during the installation process, then you have no clue about the generated password that is used to protect the access to the web interface and the API
so you might wonder how to reset it or set a new one, right?
Don't worry, I'll tell you what to do 😉.
Simply run the following command(s):
# Reset the password
pihole -a -p
# And set a new one (yes, it's the same command)
pihole -a -p
Run
pihole --help
orpihole -a -h
for more details.
Normally you should be able to access to the web interface with your device IP adress or hostname (if already defined in /etc/hosts
on your machine or a global DNS
).
If you don't remember the IP address or hostname of your device or simply being lazy to type the address (don't worry, I won't blame you), you can do the following:
# Connect using IP address
echo "http://$(hostname -i)/admin/"
# Connect using hostname
echo "http://$(hostname -f)/admin/"
Then [Ctrl + Click]
to open it in your browser 😉.
If you are looking at the dark mode, log as admin
and go to Settings on the left then:
If you want to add some others blocklists like EasyList used by AdBlock Plus for example, you can use the converted version for Pi-hole from https://github.com/justdomains/blocklists.
In my case, I'm gonna use the following ones:
- EasyList: https://justdomains.github.io/blocklists/lists/easylist-justdomains.txt
- EasyPrivacy: https://justdomains.github.io/blocklists/lists/easyprivacy-justdomains.txt
If you are looking for something else or different ones, here are some references that you can find useful:
- https://firebog.net/
- https://github.com/StevenBlack/hosts
- https://github.com/Laicure/hosts
So, to proceed, go to Group Management on the left then do as displayed below:
Once you've finished to add all your additional blocklists, go to Tools then Update Gravity and click on the Update button.
You should get something similar if the update worked properly:
Now you can take the time to discover the dashboard before continuing or not 😁.
You should not change the blocking mode if you have no idea about possible DNS
replies, otherwise proceed as follow:
# Edit the /etc/pihole/pihole-FTL.conf file
sudo nano /etc/pihole/pihole-FTL.conf
# Add or change the following and save the file
BLOCKINGMODE=NXDOMAIN
See here for more details about other possible values.
By default, Quad9 is using also using
NXDOMAIN
asDNS
reply to block unwanted / malware domains. To know more about that, check here and search forNXDOMAIN
in the page.
Once done, run the following command:
sudo killall -SIGHUP pihole-FTL
Or go to Settings then click on the Restart DNS resolver button:
As we have installed Netdata at the beginning, we can now see some statistics about Pi-hole in Netdata but we have to restart the Netdata service first:
# Restart the service
sudo systemctl restart netdata ; systemctl status netdata -l
# Connect to the Netdata web interface
echo "http://$(hostname -f):19999/#menu_pihole"
# Or with this one for using the IP address instead of the hostname
echo "http://$(hostname -i):19999/#menu_pihole"
Then [Ctrl + Click]
to open the URL in your browser:
sudo apt install unbound stubby
The first thing you will have to do when using Unbound with Pi-hole is to disable the installed internal resolver that will create the same DNS resolution issue we had previously... To do so, simply run the following command:
sudo systemctl disable --now unbound-resolvconf.service ; systemctl status unbound-resolvconf.service -l
Then verify that the content of the /etc/resolv.conf
file is similar to this:
$ cat /etc/resolv.conf
nameserver 127.0.0.1
At this state, Unbound will not working correctly but it's normal, it's because the default port 53
is already used by FTLDNS in Pi-hole.
We need now to configure Unbound and make it listening on another port than 53
.
The port
53
is the default one forDNS
.
Create a new configuration file that will be used to hold the main settings related to your setup without touching the default ones.
In this case, I'll use /etc/unbound/unbound.conf.d/pi-hole-dot.conf
but you can use another name.
sudo nano /etc/unbound/unbound.conf.d/pi-hole-dot.conf
The new configuration file must be stored in the
/etc/unbound/unbound.conf.d/
folder.
Add the following content:
server:
# Network settings
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
# May be set to yes if you have IPv6 connectivity
do-ip6: no
# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no
# Use this only when you downloaded the list of primary root servers!
# If you use the default dns-root-data package, unbound will find it automatically
#root-hints: "/var/lib/unbound/root.hints"
# Trust glue only if it is within the server's authority
harden-glue: yes
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes
# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no
# Reduce EDNS reassembly buffer size.
# Suggested by the unbound man page to reduce fragmentation reassembly problems
edns-buffer-size: 1472
# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes
# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
num-threads: 2
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
#so-rcvbuf: 1m
# Ensure privacy of local IP ranges
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
It's a customized version from the one described here.
Now restart the service and check the status:
sudo systemctl restart unbound ; systemctl status unbound -l
Unfortunately, it's not finished yet... There is another blocking service that is also trying to listen on the port 53
which is Stubby. Don't worry, we'll also solve this issue but we must do some other changes in the Unbound configuration before moving to the Stubby part.
Now we'll need to forward all the DNS
requests that will be passed from FTLDNS
to Unbound to finally, Stubby.
Why that? Simply because:
FTLDNS
is listening on port53
to handle allDNS
requests- These requests will be then forwarded to Unbound for the recursive
DNS
checking. - To apply the
DNS-over-TLS
we need then to forward requests from Unbound to Stubby that will then forward them to the defined Upstream DNS in the configuration file. - To finish,
FTLDNS
will then cache theDNS
replies transmitted withDNS-over-TLS
from Stubby.
It's sounds complicated but once in place it works perfectly 😉.
To forward DNS
requests to Stubby, we'll need to create a new configuration file named /etc/unbound/unbound.conf.d/stubby.conf
:
sudo nano /etc/unbound/unbound.conf.d/stubby.conf
With the following content:
server:
do-not-query-localhost: no
forward-zone:
name: "."
forward-addr: 127.0.0.1@8053
forward-addr: ::1@8053
Save the file and... You're mostly done for the Unbound part! 😎
To improve the DNS privacy with Unbound, we just need to create some additional configuration files:
privacy.conf
qname-minimisation.conf
(should already exist)
With the following content:
/etc/unbound/unbound.conf.d/privacy.conf
server:
minimal-responses: yes
hide-identity: yes
hide-version: yes
/etc/unbound/unbound.conf.d/qname-minimisation.conf
server:
# Send minimum amount of information to upstream servers to enhance
# privacy. Only sends minimum required labels of the QNAME and sets
# QTYPE to NS when possible.
# See RFC 7816 "DNS Query Name Minimisation to Improve Privacy" for
# details.
qname-minimisation: yes
Optionally, you can also change the default logging settings by creating another config file:
sudo nano /etc/unbound/unbound.conf.d/logs.conf
With the following content:
# enable query logging
server:
# If no logfile is specified, syslog is used
# logfile: "/var/log/unbound/unbound.log"
log-time-ascii: yes
log-queries: yes
log-replies: yes
verbosity: 2
Feel free to use different settings and set verbosity level to
0
for example withlog-queries
andlog-replies
set tono
.
Now we need to configure Stubby to stop using the default port 53
and listen on another one.
We'll also need to define the wanted Upstream DNS to use internaly.
The configuration file below has been customized to use Quad9 Upstream DNS with DNSSEC and ECS enabled. See here for more details.
Feel free to change the defined settings according to your needs.
Create a backup of the existing file:
cd /etc/stubby
sudo mv -v stubby.yml stubby.yml.old
sudo nano stubby.yml
and paste the content from the customized version:
#
# This is a yaml version of the stubby configuration file (it replaces the
# json based stubby.conf file used in earlier versions of getdns/stubby).
#
# For more information see
# https://dnsprivacy.org/wiki/display/DP/Configuring+Stubby
#
# This format does not fully support all yaml features - the restrictions are:
# - the outer-most data structure must be a yaml mapping
# - mapping keys must be yaml scalars
# - plain scalars will be converted to json unchanged
# - non-plain scalars (quoted, double-quoted, wrapped) will be interpreted
# as json strings, i.e. double quoted.
# - yaml tags are not supported
# - IPv6 addresses ending in :: are not yet supported (use ::0)
#
# Note that we plan to introduce a more compact format for defining upstreams
# in future: https://github.com/getdnsapi/stubby/issues/79
# Logging is currently configured at runtime using command line arguments. See
# > stubby -h
# for details.
# Specifies whether to run as a recursive or stub resolver
# For stubby this MUST be set to GETDNS_RESOLUTION_STUB
resolution_type: GETDNS_RESOLUTION_STUB
# Ordered list composed of one or more transport protocols:
# GETDNS_TRANSPORT_UDP, GETDNS_TRANSPORT_TCP or GETDNS_TRANSPORT_TLS
# If only one transport value is specified it will be the only transport used.
# Should it not be available basic resolution will fail.
# Fallback transport options are specified by including multiple values in the
# list. Strict mode (see below) should use only GETDNS_TRANSPORT_TLS.
dns_transport_list:
- GETDNS_TRANSPORT_TLS
# Selects Strict or Opportunistic Usage profile as described in
# https://datatracker.ietf.org/doc/draft-ietf-dprive-dtls-and-tls-profiles/
# Strict mode requires that authentication information for the upstreams is
# specified below. Opportunistic may fallback to clear text DNS if UDP or TCP
# is included in the transport list above.
# For Strict use GETDNS_AUTHENTICATION_REQUIRED
# For Opportunistic use GETDNS_AUTHENTICATION_NONE
tls_authentication: GETDNS_AUTHENTICATION_REQUIRED
# EDNS0 option to pad the size of the DNS query to the given blocksize
# 128 is currently recommended by
# https://tools.ietf.org/html/draft-ietf-dprive-padding-policy-03
tls_query_padding_blocksize: 128
# EDNS0 option for ECS client privacy as described in Section 7.1.2 of
# https://tools.ietf.org/html/rfc7871
edns_client_subnet_private : 1
# EDNS0 option for keepalive idle timeout in ms as specified in
# https://tools.ietf.org/html/rfc7828
# This keeps idle TLS connections open to avoid the overhead of opening a new
# connection for every query.
idle_timeout: 10000
# Set the listen addresses for the stubby DAEMON. This specifies localhost IPv4
# and IPv6. It will listen on port 53 by default. Use <IP_address>@<port> to
# specify a different port
listen_addresses:
- 127.0.0.1@8053
- 0::1@8053
# Instructs stubby to distribute queries across all available name servers.
# Set to 0 to treat the upstreams below as an ordered list and use a single
# upstream until it becomes unavailable, then use the next one.
round_robin_upstreams: 1
# Require DNSSEC validation. For releases earlier than 1.2 a trust anchor must
# be configured configured manually. This can be done with unbound-anchor.
dnssec_return_status: GETDNS_EXTENSION_TRUE
# Specify the location of the installed trust anchor file (leave commented out
# for zero configuration DNSSEC)
# dnssec_trust_anchors: "/etc/unbound/getdns-root.key"
dnssec_trust_anchors: "/var/lib/unbound/root.key"
# Control the maximum number of connection failures that will be permitted
# before Stubby backs-off from using an individual upstream (default 2)
# tls_connection_retries: 5
# Control the maximum time in seconds Stubby will back-off from using an
# individual upstream after failures under normal circumstances (default 3600)
# tls_backoff_time: 300
# Limit the total number of outstanding queries permitted
# limit_outstanding_queries: 100
# Specify the timeout on getting a response to an individual request
# (default 5s)
# timeout: 1
# Specify the list of upstream recursive name servers to send queries to
# In Strict mode upstreams need either a tls_auth_name or a tls_pubkey_pinset
# so the upstream can be authenticated.
# The list below includes all the available test servers but only has the subset
# operated the stubby/getdns developers enabled. You can enable any of the
# others you want to use by uncommenting the relevant section. See:
# https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers
# If you don't have IPv6 then comment then out those upstreams.
# In Opportunistic mode they only require an IP address in address_data.
# The information for an upstream can include the following:
# - address_data: IPv4 or IPv6 address of the upstream
# port: Port for UDP/TCP (default is 53)
# tls_auth_name: Authentication domain name checked against the server
# certificate
# tls_pubkey_pinset: An SPKI pinset verified against the keys in the server
# certificate
# - digest: Only "sha256" is currently supported
# value: Base64 encoded value of the sha256 fingerprint of the public
# key
# tls_port: Port for TLS (default is 853)
upstream_recursive_servers:
# IPv4 addresses
# The Surfnet/Sinodun servers
# - address_data: 145.100.185.15
# tls_auth_name: "dnsovertls.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
# - address_data: 145.100.185.16
# tls_auth_name: "dnsovertls1.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
# The getdnsapi.net server
# - address_data: 185.49.141.37
# tls_auth_name: "getdnsapi.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
# IPv6 addresses
# The Surfnet/Sinodun servers
# - address_data: 2001:610:1:40ba:145:100:185:15
# tls_auth_name: "dnsovertls.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
# - address_data: 2001:610:1:40ba:145:100:185:16
# tls_auth_name: "dnsovertls1.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
# The getdnsapi.net server
# - address_data: 2a04:b900:0:100::38
# tls_auth_name: "getdnsapi.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
# Additional servers
# IPv4 addresses
## Quad 9 'secure' service - Filters, does DNSSEC, does ECS
- address_data: 9.9.9.11
tls_auth_name: "dns.quad9.net"
- address_data: 149.112.112.11
tls_auth_name: "dns.quad9.net"
## Quad 9 'secure' service - Filters, does DNSSEC, doesn't send ECS
- address_data: 9.9.9.9
tls_auth_name: "dns.quad9.net"
- address_data: 149.112.112.112
tls_auth_name: "dns.quad9.net"
## Quad 9 'insecure' service - No filtering, does DNSSEC, may send ECS (it is
## unclear if it honours the edns_client_subnet_private request from stubby)
# - address_data: 9.9.9.10
# tls_auth_name: "dns.quad9.net"
## The Uncensored DNS servers
# - address_data: 89.233.43.71
# tls_auth_name: "unicast.censurfridns.dk"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs=
## A Surfnet/Sinodun server supporting TLS 1.2 and 1.3
# - address_data: 145.100.185.18
# tls_auth_name: "dnsovertls3.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 5SpFz7JEPzF71hditH1v2dBhSErPUMcLPJx1uk2svT8=
## A Surfnet/Sinodun server using Knot resolver. Warning - has issue when used
## for DNSSEC
# - address_data: 145.100.185.17
# tls_auth_name: "dnsovertls2.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: NAXBESvpjZMnPWQcrxa2KFIkHV/pDEIjRkA3hLWogSg=
## dns.cmrg.net server using Knot resolver. Warning - has issue when used for
## DNSSEC.
# - address_data: 199.58.81.218
# tls_auth_name: "dns.cmrg.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 3IOHSS48KOc/zlkKGtI46a9TY9PPKDVGhE3W2ZS4JZo=
# - digest: "sha256"
# value: 5zFN3smRPuHIlM/8L+hANt99LW26T97RFHqHv90awjo=
## dns.larsdebruin.net (formerly dns1.darkmoon.is)
# - address_data: 51.15.70.167
# tls_auth_name: "dns.larsdebruin.net "
# tls_pubkey_pinset:
# - digest: "sha256"
# value: AAT+rHoKx5wQkWhxlfrIybFocBu3RBrPD2/ySwIwmvA=
## securedns.eu
# - address_data: 146.185.167.43
# tls_auth_name: "securedns.eu"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 2EfbwDyk2zSnAbBJSpCSWZKKGUD+a6p/yg2bxdC+x2A=
## dns-tls.bitwiseshift.net
# - address_data: 81.187.221.24
# tls_auth_name: "dns-tls.bitwiseshift.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: YmcYWZU5dd2EoblZHNf1jTUPVS+uK3280YYCdz4l4wo=
## ns1.dnsprivacy.at
# - address_data: 94.130.110.185
# tls_auth_name: "ns1.dnsprivacy.at"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: vqVQ9TcoR9RDY3TpO0MTXw1YQLjF44zdN3/4PkLwtEY=
## ns2.dnsprivacy.at
# - address_data: 94.130.110.178
# tls_auth_name: "ns2.dnsprivacy.at"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: s5Em89o0kigwfBF1gcXWd8zlATSWVXsJ6ecZfmBDTKg=
## dns.bitgeek.in
# - address_data: 139.59.51.46
# tls_auth_name: "dns.bitgeek.in"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: FndaG4ezEBQs4k0Ya3xt3z4BjFEyQHd7B75nRyP1nTs=
## Lorraine Data Network (self-signed cert).
# - address_data: 80.67.188.188
# tls_pubkey_pinset:
# - digest: "sha256"
# value: WaG0kHUS5N/ny0labz85HZg+v+f0b/UQ73IZjFep0nM=
## dns.neutopia.org
# - address_data: 89.234.186.112
# tls_auth_name: "dns.neutopia.org"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: wTeXHM8aczvhRSi0cv2qOXkXInoDU+2C+M8MpRyT3OI=
## NIC Chile (self-signed cert)
# - address_data: 200.1.123.46
# tls_pubkey_pinset:
# - digest: "sha256"
# value: sG6kj+XJToXwt1M6+9BeCz1SOj/1/mdZn56OZvCyZZc=
## # OARC. Note: this server currently doesn't support strict mode!
## - address_data: 184.105.193.78
## tls_auth_name: "tls-dns-u.odvr.dns-oarc.net"
## tls_pubkey_pinset:
## - digest: "sha256"
## value: pOXrpUt9kgPgbWxBFFcBTbRH2heo2wHwXp1fd4AEVXI=
#IPv6 addresses
## Quad 9 'secure' service - Filters, does DNSSEC, does ECS
- address_data: 2620:fe::11
tls_auth_name: "dns.quad9.net"
- address_data: 2620:fe::fe:11
tls_auth_name: "dns.quad9.net"
## Quad 9 'secure' service - Filters, does DNSSEC, doesn't send ECS
- address_data: 2620:fe::fe
tls_auth_name: "dns.quad9.net"
- address_data: 2620:fe::9
tls_auth_name: "dns.quad9.net"
## Quad 9 'insecure' service - No filtering, does DNSSEC, may send ECS (it is
## unclear if it honours the edns_client_subnet_private request from stubby)
# - address_data: 2620:fe::10
# tls_auth_name: "dns.quad9.net"
## The Uncensored DNS server
# - address_data: 2a01:3a0:53:53::0
# tls_auth_name: "unicast.censurfridns.dk"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs=
## A Surfnet/Sinodun server supporting TLS 1.2 and 1.3
# - address_data: 2001:610:1:40ba:145:100:185:18
# tls_auth_name: "dnsovertls3.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 5SpFz7JEPzF71hditH1v2dBhSErPUMcLPJx1uk2svT8=
## A Surfnet/Sinodun server using Knot resolver. Warning - has issue when used
## for DNSSEC
# - address_data: 2001:610:1:40ba:145:100:185:17
# tls_auth_name: "dnsovertls2.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: NAXBESvpjZMnPWQcrxa2KFIkHV/pDEIjRkA3hLWogSg=
## dns.cmrg.net server using Knot resolver. Warning - has issue when used for
## DNSSEC.
# - address_data: 2001:470:1c:76d::53
# tls_auth_name: "dns.cmrg.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 3IOHSS48KOc/zlkKGtI46a9TY9PPKDVGhE3W2ZS4JZo=
# - digest: "sha256"
# value: 5zFN3smRPuHIlM/8L+hANt99LW26T97RFHqHv90awjo=
## securedns.eu
# - address_data: 2a03:b0c0:0:1010::e9a:3001
# tls_auth_name: "securedns.eu"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 2EfbwDyk2zSnAbBJSpCSWZKKGUD+a6p/yg2bxdC+x2A=
## dns-tls.bitwiseshift.net
# - address_data: 2001:8b0:24:24::24
# tls_auth_name: "dns-tls.bitwiseshift.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: YmcYWZU5dd2EoblZHNf1jTUPVS+uK3280YYCdz4l4wo=
## ns1.dnsprivacy.at
# - address_data: 2a01:4f8:c0c:3c03::2
# tls_auth_name: "ns1.dnsprivacy.at"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: vqVQ9TcoR9RDY3TpO0MTXw1YQLjF44zdN3/4PkLwtEY=
## ns2.dnsprivacy.at
# - address_data: 2a01:4f8:c0c:3bfc::2
# tls_auth_name: "ns2.dnsprivacy.at"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: s5Em89o0kigwfBF1gcXWd8zlATSWVXsJ6ecZfmBDTKg=
## Go6Lab
# - address_data: 2001:67c:27e4::35
# tls_auth_name: "privacydns.go6lab.si"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: g5lqtwHia/plKqWU/Fe2Woh4+7MO3d0JYqYJpj/iYAw=
## Lorraine Data Network (self-signed cert).
# - address_data: 2001:913::8
# tls_pubkey_pinset:
# - digest: "sha256"
# value: WaG0kHUS5N/ny0labz85HZg+v+f0b/UQ73IZjFep0nM=
## dns.neutopia.org
# - address_data: 2a00:5884:8209::2
# tls_auth_name: "dns.neutopia.org"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: wTeXHM8aczvhRSi0cv2qOXkXInoDU+2C+M8MpRyT3OI=
## NIC Chile (self-signed cert)
# - address_data: 2001:1398:1:0:200:1:123:46
# tls_pubkey_pinset:
# - digest: "sha256"
# value: sG6kj+XJToXwt1M6+9BeCz1SOj/1/mdZn56OZvCyZZc=
## Yeti. Note the servers use a different root trust anchor for DNSSEC!
# - address_data: 2001:4b98:dc2:43:216:3eff:fea9:41a
# tls_auth_name: "dns-resolver.yeti.eu.org"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: YxtXAorQNSo+333ko1ctuXcnpMcplPaOI/GCM+YeMQk=
## # OARC. Note: this server currently doesn't support strict mode!
## - address_data: 2620:ff:c000:0:1::64:25
## tls_auth_name: "tls-dns-u.odvr.dns-oarc.net"
## tls_pubkey_pinset:
## - digest: "sha256"
## value: pOXrpUt9kgPgbWxBFFcBTbRH2heo2wHwXp1fd4AEVXI=
## Servers that listen on port 443 (IPv4 and IPv6)
## Surfnet/Sinodun servers
# - address_data: 145.100.185.15
# tls_port: 443
# tls_auth_name: "dnsovertls.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
# - address_data: 145.100.185.16
# tls_port: 443
# tls_auth_name: "dnsovertls1.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
## dns.cmrg.net server using Knot resolver
# - address_data: 199.58.81.218
# tls_port: 443
# tls_auth_name: "dns.cmrg.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 3IOHSS48KOc/zlkKGtI46a9TY9PPKDVGhE3W2ZS4JZo=
# - digest: "sha256"
# value: 5zFN3smRPuHIlM/8L+hANt99LW26T97RFHqHv90awjo=
## Lorraine Data Network (self-signed cert)
# - address_data: 80.67.188.188
# tls_port: 443
# tls_pubkey_pinset:
# - digest: "sha256"
# value: WaG0kHUS5N/ny0labz85HZg+v+f0b/UQ73IZjFep0nM=
## dns.neutopia.org
# - address_data: 89.234.186.112
# tls_port: 443
# tls_auth_name: "dns.neutopia.org"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: wTeXHM8aczvhRSi0cv2qOXkXInoDU+2C+M8MpRyT3OI=
## The Surfnet/Sinodun servers
# - address_data: 2001:610:1:40ba:145:100:185:15
# tls_port: 443
# tls_auth_name: "dnsovertls.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
# - address_data: 2001:610:1:40ba:145:100:185:16
# tls_port: 443
# tls_auth_name: "dnsovertls1.sinodun.com"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
## dns.cmrg.net server using Knot resolver
# - address_data: 2001:470:1c:76d::53
# tls_port: 443
# tls_auth_name: "dns.cmrg.net"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: 3IOHSS48KOc/zlkKGtI46a9TY9PPKDVGhE3W2ZS4JZo=
# - digest: "sha256"
# value: 5zFN3smRPuHIlM/8L+hANt99LW26T97RFHqHv90awjo=
## Lorraine Data Network (self-signed cert)
# - address_data: 2001:913::8
# tls_port: 443
# tls_pubkey_pinset:
# - digest: "sha256"
# value: WaG0kHUS5N/ny0labz85HZg+v+f0b/UQ73IZjFep0nM=
## dns.neutopia.org
# - address_data: 2a00:5884:8209::2
# tls_port: 443
# tls_auth_name: "dns.neutopia.org"
# tls_pubkey_pinset:
# - digest: "sha256"
# value: wTeXHM8aczvhRSi0cv2qOXkXInoDU+2C+M8MpRyT3OI=
Now that every is configuration properly, we need to restart Unbound and Stubby to apply the new changes:
sudo systemctl restart unbound stubby ; systemctl status unbound stubby -l
You should see something similar if it worked without issues:
● unbound.service - Unbound DNS server
Loaded: loaded (/lib/systemd/system/unbound.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2021-05-27 03:38:06 UTC; 26ms ago
Docs: man:unbound(8)
Process: 13861 ExecStartPre=/usr/lib/unbound/package-helper root_trust_anchor_update (code=exited, statu
Process: 13858 ExecStartPre=/usr/lib/unbound/package-helper chroot_setup (code=exited, status=0/SUCCESS)
Main PID: 13865 (unbound)
Tasks: 2 (limit: 2149)
CGroup: /system.slice/unbound.service
└─13865 /usr/sbin/unbound -d
May 27 03:38:06 [REDACTED] systemd[1]: Starting Unbound DNS server...
May 27 03:38:06 [REDACTED] package-helper[13861]: /var/lib/unbound/root.key has content
May 27 03:38:06 [REDACTED] package-helper[13861]: success: the anchor is ok
● unbound.service - Unbound DNS server
Loaded: loaded (/lib/systemd/system/unbound.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2021-05-27 03:38:06 UTC; 26ms ago
Docs: man:unbound(8)
Process: 13861 ExecStartPre=/usr/lib/unbound/package-helper root_trust_anchor_update (code=exited, statu
Process: 13858 ExecStartPre=/usr/lib/unbound/package-helper chroot_setup (code=exited, status=0/SUCCESS)
Main PID: 13865 (unbound)
Tasks: 2 (limit: 2149)
CGroup: /system.slice/unbound.service
└─13865 /usr/sbin/unbound -d
May 27 03:38:06 [REDACTED] systemd[1]: Starting Unbound DNS server...
May 27 03:38:06 [REDACTED] package-helper[13861]: /var/lib/unbound/root.key has content
May 27 03:38:06 [REDACTED] package-helper[13861]: success: the anchor is ok
May 27 03:38:06 [REDACTED] package-helper[13861]: success: the anchor is ok
May 27 03:38:06 [REDACTED] unbound[13865]: [13865:0] notice: init module 0: subnet
May 27 03:38:06 [REDACTED] unbound[13865]: [13865:0] notice: init module 1: validator
May 27 03:38:06 [REDACTED] unbound[13865]: [13865:0] notice: init module 2: iterator
May 27 03:38:06 [REDACTED] unbound[13865]: [13865:0] info: start of service (unbound 1.6.7).
May 27 03:38:06 [REDACTED] systemd[1]: Started Unbound DNS server.
● stubby.service - DNS Privacy Stub Resolver
Loaded: loaded (/lib/systemd/system/stubby.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2021-05-27 03:38:06 UTC; 268ms ago
Docs: https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Daemon+-+Stubby
Main PID: 13857 (stubby)
Tasks: 1 (limit: 2149)
CGroup: /system.slice/stubby.service
└─13857 /usr/bin/stubby
May 27 03:38:06 [REDACTED] systemd[1]: Started DNS Privacy Stub Resolver.
May 27 03:38:06 [REDACTED] stubby[13857]: [03:38:06.118897] STUBBY: Read config from file /etc/stubby/stu
May 27 03:38:06 [REDACTED] stubby[13857]: [03:38:06.124157] STUBBY: DNSSEC Validation is ON
May 27 03:38:06 [REDACTED] stubby[13857]: [03:38:06.124223] STUBBY: Transport list is:
May 27 03:38:06 [REDACTED] stubby[13857]: [03:38:06.124239] STUBBY: - TLS
May 27 03:38:06 [REDACTED] stubby[13857]: [03:38:06.124255] STUBBY: Privacy Usage Profile is Strict (Auth
May 27 03:38:06 [REDACTED] stubby[13857]: [03:38:06.124269] STUBBY: (NOTE a Strict Profile only applies w
May 27 03:38:06 [REDACTED] stubby[13857]: [03:38:06.124286] STUBBY: Starting DAEMON....
sudo nano /etc/dhcpcd.conf
Comment the line similar to:
static domain_name_servers=9.9.9.9 149.112.112.112
That way:
# static domain_name_servers=9.9.9.9 149.112.112.112
Then save the file. Once done, restart the service:
sudo systemctl restart dhcpcd.service ; systemctl status dhcpcd.service -l
Then go in the web interface and add Unbound as local DNS resolver:
Save the new settings with the Save button at the bottom right of the page.
If something goes wrong here, the system will automatically reset to the previously defined values.
Now you can test all the involved services, all should work correctly:
# Check Unbound
dig pi-hole.net @127.0.0.1 -p 5335
# Check Stubby
dig pi-hole.net @127.0.0.1 -p 8053
# Check FTLDNS
dig pi-hole.net @127.0.0.1 -p 53
To finish, we can now validate if DNSSEC
is also working as expected:
# Should return SERVAIL
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335
# Should return NOERROR
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335
- FTLDNS:
127.0.0.1:53
- Unbound:
127.0.0.1:5335
- Stubby:
127.0.0.1:8053
- Web Interface:
http://hostname/admin
- API:
127.0.0.1:4711
- Netdata:
http://hostname:19999
To enable the Unbound monitoring in Netdata, we'll just create a new configuration file for Unbound and give read permission to Netdata on unbound-control
configuration files.
- Create new Unbound config file for Netdata:
# Create the dedicated file to host Netdata related settings for Unbound
sudo nano /etc/unbound/unbound.conf.d/netdata.conf
And add the following content:
server:
# Enable extended statistics.
statistics-interval: 0
extended-statistics: yes
# Set to yes if graphing tool needs it
statistics-cumulative: yes
# enable remote-control
remote-control:
control-enable: yes
Restart the unbound
service:
sudo systemctl restart unbound ; systemctl status unbound -l
- Assign read permission on
unbound-control
files for Netdata:
# Move to the unbound configuration directory
cd /etc/unbound
# Assign read permision via file ACLs
sudo setfacl -m user:netdata:r unbound.conf
sudo setfacl -m user:netdata:r unbound_control.key
sudo setfacl -m user:netdata:r unbound_control.pem
# Check applied file ACLs permissions
getfacl unbound.conf
getfacl unbound_control.key
getfacl unbound_control.pem
- Restart Netdata to apply the changes.
# Restart the service
sudo systemctl restart netdata ; systemctl status netdata -l
# Connect to the Netdata web interface
echo "http://$(hostname -f):19999/#menu_unbound_local"
# Or with this one for using the IP address instead of the hostname
echo "http://$(hostname -i):19999/#menu_unbound_local"
Then [Ctrl + Click]
to open the URL in your browser:
Coming soon.
In this section, I'll explain how to setup the autologin
+ screen
+ Pi-hole Chronometer
.
Chronometer
is a way to display stats to LCD display.
I can't capture the result as it will contain too many private details.
It's very simple to setup the autologin
:
# Check the getty path
which getty
# Edit the getty service
sudo systemctl edit getty@tty1
Add the following content:
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin [username] --noclear %I $TERM
Do the following changes:
- Replace
/sbin/getty
by the path given with thewhich
command - Replace
[username]
by the username you want to autologin
You can find more details here: https://wiki.archlinux.org/index.php/Getty#Automatic_login_to_virtual_console
Normally screen
should be already installed on your systemd but if not, just run the following command:
sudo apt install screen
Now that all dependencies are installed, you can create a small launch script that will called when the session will automatically open:
#!/bin/bash
# Create an attached Screen session usable from SSH
screen -S pi-hole-mon bash -c '/usr/local/bin/pihole -c'
You can also add
-r [refresh delay in seconds]
but on my side using-r 1
was creating too much screen flickering so I left the default refresh delay.
Save the script as pi-hole-mon.sh
and make it executable: chmod -v +x pi-hole-mon.sh
.
Once done, add the following content in the .bashrc
file to call it when the session is started:
# Load screen + pi-hole chronometer
if [ $(tty) == /dev/tty1 ]; then
for I in {5..1} ; do echo "Initializing monitor in $I seconds..." ; sleep 1 ; done
~/pi-hole-mon.sh
fi
Now you can reboot to see the result 😁
If everything has worked correctly, you should have a running screen
session created that display the Pi-hole chronometer
stats 🤘
To connect on that session, run the following commands:
# Connect via SSH on the remote host
ssh user@host
# List existing screen sessions
screen -ls
# Connect on the existing session
screen -x
# To disconnect without killing the screen session
[Ctrl+a] d
You can find more details here: https://linoxide.com/linux-how-to/screen-remote-ssh/
In this section, I'll try to explain how to setup several clients to connect to our fresh new Pi-hole (DoT
) deployment.
Simply edit the /etc/systemd/resolved.conf
file and add or change the following:
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Entries in this file show the compile time defaults.
# You can change settings by editing this file.
# Defaults can be restored by simply deleting this file.
#
# See resolved.conf(5) for details
[Resolve]
DNS=[YOUR-SERVER-IP]
#DNSOverTLS=opportunistic
FallbackDNS=9.9.9.11 149.112.112.11
Domains=~.
#LLMNR=no
#MulticastDNS=no
DNSSEC=yes
#Cache=yes
#DNSStubListener=yes
Replace
[YOUR-SERVER-IP]
by the IP address assigned to your Pi-hole (DoT
) device.You can also define any other Upstream DNS than the ones from Quad9 that you can see here.
Additionally, you can also try to enable the DNSSEC
option but according to this test, it works better when disabled.
I had some local
DNS
resolution issues whenDNSSEC
was not enabled, so I put it back on.
Regarding the DNSOverTLS
option, make sure to have a systemd
version upper than 239
. As far as I know, it has been supported starting from version 242
.
To finish and apply your changes, simply run the following commands:
sudo systemctl restart systemd-resolved.service ; systemctl status systemd-resolved.service -l
You can now get more details and very the status with:
$ systemd-resolve --status
Global
LLMNR setting: no
MulticastDNS setting: no
DNSOverTLS setting: opportunistic
DNSSEC setting: yes
DNSSEC supported: yes
DNS Servers: [REDACTED]
Fallback DNS Servers: 9.9.9.11
149.112.112.11
DNS Domain: ~.
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
...
The output has been limited for privacy reasons.
Then finally, run the following command to verify that the DNS resolution is still working correctly:
$ dig pi-hole.net @127.0.0.53
; <<>> DiG 9.11.3-1ubuntu1.15-Ubuntu <<>> pi-hole.net @127.0.0.53
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59201
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;pi-hole.net. IN A
;; ANSWER SECTION:
pi-hole.net. 900 IN A 178.128.134.214
;; Query time: 557 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Thu May 27 21:03:04 CEST 2021
;; MSG SIZE rcvd: 56
The IP address
127.0.0.53
is the stub DNS resolver address used bysystemd
instead of the classical127.0.0.1
.
As you can see in the status, it says NOERROR
and so means that it's normally working 😁.
Go to your /etc/netplan
config folder and edit the YAML
config file:
sudo nano /etc/netplan/50-cloud-init.yaml
And add or change the following:
nameservers:
addresses: [1.2.3.4]
in the ethernets
and/or wifis
sections.
Replace
1.2.3.4
by the real IP address of your Pi-hole device.
Then save the file and run the following commands:
# Generate new config files
sudo netplan --debug generate
# Try the new changes
sudo netplan --debug try
# If the test worked, apply changes permanently
sudo netplan --debug apply
Now you can try if that worked:
dig pi-hole.net @1.2.3.4
Replace
1.2.3.4
by the real IP address of your Pi-hole device.
Coming soon.
- https://learn.netdata.cloud/guides/monitor/pi-hole-raspberry-pi
- https://github.com/pi-hole/pi-hole/#one-step-automated-install
- https://github.com/pi-hole/AdminLTE
- https://docs.pi-hole.net/main/prerequisites/
- https://www.reddit.com/r/pihole/comments/8kw5z8/dashboard_default_password/
- https://docs.pi-hole.net/guides/misc/whitelist-blacklist/
- https://github.com/StevenBlack
- https://firebog.net/
- https://github.com/justdomains/blocklists
- https://github.com/Laicure/hosts
- https://docs.pi-hole.net/database/gravity/example/
- https://docs.pi-hole.net/ftldns/configfile/
- https://docs.pi-hole.net/ftldns/blockingmode/
- https://docs.pi-hole.net/ftldns/privacylevels/
- https://docs.pi-hole.net/guides/dns/unbound/
- https://learn.netdata.cloud/guides/collect-unbound-metrics
- https://learn.netdata.cloud/docs/agent/collectors/go.d.plugin/modules/unbound
- https://docs.pi-hole.net/guides/dns/upstream-dns-providers/
- https://wikileaks.org/wiki/Alternative_DNS
- https://gist.github.com/Jiab77/72c868ecebce1d0027258eeec53b5a0f
- https://wiki.archlinux.org/title/Stubby
- https://dnsprivacy.org/wiki/display/DP/Configuring+Stubby
- https://www.linuxbabe.com/ubuntu/ubuntu-stubby-dns-over-tls
- https://dfarq.homeip.net/dns-over-tls-protect-your-network-with-ubuntu/
- https://www.quad9.net/support/faq
- https://www.quad9.net/news/blog/enable-private-dns-using-quad9-on-android-9/
- https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+-+The+Solutions#DNSPrivacyTheSolutions-DNS-over-TLS(DoT)
- https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers