CaffeineAddict
Well-Known Member
What is encrypted DNS?
Purpose of encrypted DNS is to prevent eavesdropping on your DNS queries.
For web browsing there is HTTPS that prevents spying on content you send/receive from a web server, however this doesn't cover your DNS queries;
ISP, MITM, governments and similar actors can spy on your web browsing activity.
There are 2 methods of encrypted DNS:
1. DoH (DNS over HTTPS)
2. DoT (DNS over TLS)
In this tutorial we'll use DoT.
What is DNSSEC?
Purpose of DNSSEC is integrity and authenticity, it exists to confirm we're receiving DNS responses from true server (the one we configured) instead of a fake one (e.g. one set by an attacker).
Therefore its utility is to protect against attacks like DNS spoofing and cache poisoning.
In conclusion you want both DNS encryption as well as DNS authentication, this are 2 separate things.
To enable encrypted DNS there are 2 conditions:
1. DoT or DoH capable DNS resolver
2. DoT or DoH capable DNS server
Same applies to DNSSEC, both the resolver and server need to support it.
In this tutorial for DNS resolver we'll use
unbound is FOSS,
github:
github.com
docs:
And for DNS server that supports both we'll use 2 Swiss servers (feel free to find some others but make sure they support both DoT and DNSSEC and ofc. that you trust them):
quad9.net
Installation
Package name for
Debian and derivatives:
Redhat and derivatives:
Arch and derivatives:
Configuration
Below is documented config file to use, it includes the most useful options to configure some of which are set to default value,
it requires changing a few lines to match your system, feel free to customize other options later.
The file should be named
The following options should be modified:
See code commets for more info.
See code commets for more info.
If not its location is distro dependent, it could be in
Run these commands below to make sure distro provided files don't mess up with our configuration.
But verify the 2 files exist in
When you're done with config customization, check for errors in main file which includes our unbound configuration file:
Make sure there is only one
You want to configure
Create a new file named in
Copy code below into it and save:
If you enabled loging you also want to rotate logs, here is config for
Create new file in
Finally apply changes:
Enable DNS resolving with unbound
Unbound will listen by default on localhost (127.0.0.1) on port 53,
therefore
If you use
To apply restart
The change in
It should list address 127.0.0.1
If it doesn't work and you lose internet access, you can simply undo changing DNS in NetworkManager.
Testing DNS
Now the moment of truth, we want to confirm that both DNS encryption and DNSSEC work, run this:
If you have no
To confirm DNSSEC works there must be
Loging
Extended loging can be enabled in
This concludes this tutorial, if something doesn't work as expected or you have questions let me know.
Purpose of encrypted DNS is to prevent eavesdropping on your DNS queries.
For web browsing there is HTTPS that prevents spying on content you send/receive from a web server, however this doesn't cover your DNS queries;
ISP, MITM, governments and similar actors can spy on your web browsing activity.
There are 2 methods of encrypted DNS:
1. DoH (DNS over HTTPS)
2. DoT (DNS over TLS)
In this tutorial we'll use DoT.
What is DNSSEC?
Purpose of DNSSEC is integrity and authenticity, it exists to confirm we're receiving DNS responses from true server (the one we configured) instead of a fake one (e.g. one set by an attacker).
Therefore its utility is to protect against attacks like DNS spoofing and cache poisoning.
In conclusion you want both DNS encryption as well as DNS authentication, this are 2 separate things.
To enable encrypted DNS there are 2 conditions:
1. DoT or DoH capable DNS resolver
2. DoT or DoH capable DNS server
Same applies to DNSSEC, both the resolver and server need to support it.
In this tutorial for DNS resolver we'll use
unbound, it supports both.unbound is FOSS,
github:
GitHub - NLnetLabs/unbound: Unbound is a validating, recursive, and caching DNS resolver.
Unbound is a validating, recursive, and caching DNS resolver. - NLnetLabs/unbound
And for DNS server that supports both we'll use 2 Swiss servers (feel free to find some others but make sure they support both DoT and DNSSEC and ofc. that you trust them):
- dns.quad9.net
- dns.digitale-gesellschaft.ch
Quad9 | A public and free DNS service for a better security and privacy
A public and free DNS service for a better security and privacy
Installation
Package name for
unbound probably depends on your distro:Debian and derivatives:
Bash:
sudo apt install unbound
Bash:
sudo dnf install unbound
Bash:
sudo pacman -Syu unbound
Configuration
Below is documented config file to use, it includes the most useful options to configure some of which are set to default value,
it requires changing a few lines to match your system, feel free to customize other options later.
The file should be named
unbound.conf and put into /etc/unbound/unbound.conf.dThe following options should be modified:
interface: 192.168.4.100
Set this to your NIC private IP, you can learn the IP withoutgoing-interface: 192.168.4.100
ip a command. (use NIC IP used to connect to internet)Verify thatroot-hints: "/usr/share/dns/root.hints"
/usr/share/dns/root.hints exists.See code commets for more info.
Verify thattls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
/etc/ssl/certs/ca-certificates.crt exists.See code commets for more info.
Verify thatauto-trust-anchor-file: "/var/lib/unbound/root.key"
/var/lib/unbound/root.key exists.If not its location is distro dependent, it could be in
/usr/share/dns/root.key or somewhere else however if both exist then use /var/lib/unbound/root.key
Apache config:
# Configuration file: /etc/unbound/unbound.conf
# Includes files: /etc/unbound/unbound.conf.d/unbound.conf
# https://nlnetlabs.nl/documentation/unbound/unbound.conf
# NOTE: Quick copy and apply config file
# sudo cp unbound.conf /etc/unbound/unbound.conf.d/unbound.conf
# sudo unbound-checkconf /etc/unbound/unbound.conf.d/unbound.conf
# sudo systemctl restart unbound
# Server Options
server:
#
# General options
#
# Interface to use to connect to the network
# This interface is listened to for queries from clients, and answers to clients are given from it
# If an interface name is used instead of an ip address, the list of ip addresses on that interface are used.
# the default is to listen to localhost
interface: 127.0.0.1
#interface: ::0
interface: 192.168.4.100
# Allows you to bind to IP addresses that are nonlocal or do not exist,
# like when the network interface or IP address is down (Default no)
# NOTE: This is needed because on boot, the above specified interface might not yet be configured
ip-freebind: yes
# The port number, default 53, on which the server responds to queries
# 853 is the default DoT port
port: 53
# By default only localhost is allowed, the rest is refused
access-control: 127.0.0.1/8 allow
# NOTE: Excluding access from gateway
#access-control: 192.168.4.1/24 allow
# Interface to use to connect to the network
# This interface is used to send queries to authoritative servers and receive their replies
# If none are given the default (all) is used
# Outgoing queries are sent via a random outgoing interface to counter spoofing
outgoing-interface: 192.168.4.100
# Permit Unbound to open this port or range of ports for use to send queries
# By default only ports above 1024 that have not been assigned by IANA are used
# A larger number of permitted outgoing ports increases resilience against spoofing attempts
# outgoing-port-permit:
# If given, after binding the port the user privileges are dropped (Default is "unbound")
username: "unbound"
# The Unbound server forks into the background as a daemon
# Set the value to no when Unbound runs as systemd service (Default is yes)
do-daemonize: no
# Default is nothing, using builtin hints for the IN class
# The default may become outdated, when servers change, therefore it is good practice to use a root-hints file
# NOTE: This file is part of "dns-root-data" package
root-hints: "/usr/share/dns/root.hints"
# The value of the Differentiated Services Codepoint (DSCP) in the
# differentiated services field (DS) of the outgoing IP packet headers.
# ip-dscp:
#
# Optimization
# https://nlnetlabs.nl/documentation/unbound/howto-optimise/
#
# The number of threads to create to serve clients
# Use 1 for no threading
num-threads: 4
# Number of slabs in the message cache
# Power of 2 close to num-threads
msg-cache-slabs: 4
# Number of bytes size of the message cache
# Default is 4 megabytes.
msg-cache-size: 20m
# Number of slabs in the RRset cache
# Power of 2 close to num-threads
rrset-cache-slabs: 4
# Number of bytes size of the RRset cache
# Default is 4 megabytes
# Use roughly twice as much rrset cache memory
rrset-cache-size: 40m
# Number of slabs in the key cache
# Power of 2 close to num-threads
key-cache-slabs: 4
# Number of bytes size of the key cache
# Default is 4 megabytes
# NOTE: Recommended size unknown, using same as msg-cache-size
key-cache-size: 20m
# Number of slabs in the infrastructure cache
# Power of 2 close to num-threads
infra-cache-slabs: 4
#
# Privacy
#
# If enabled id.server and hostname.bind queries are refused
hide-identity: yes
# If enabled version.server and version.bind queries are refused
hide-version: yes
# If enabled the HTTP header User-Agent is not set
hide-http-user-agent: no
# Send minimum amount of information to upstream servers
qname-minimisation: yes
#
# Hardening
#
# Very small EDNS buffer sizes from queries are ignored
harden-short-bufsize: yes
# Will trust glue only if it is within the servers authority.
harden-glue: yes
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes bogus.
# If turned off you run the risk of a downgrade attack that disables security for a zone.
harden-dnssec-stripped: yes
# The nxdomain must be secure
harden-below-nxdomain: yes
#
# Logging
#
# Level 0 means no verbosity, only errors
# Level 1 (default) gives operational information
verbosity: 1
# Send log messages to log facility LOG_DAEMON (rsyslog)
use-syslog: yes
# Prints one line per query to the log
log-queries: no
# Prints one line per reply to the log
log-replies: no
# Prints the word "query" and "reply" with log-queries and log-replies
log-tag-queryreply: yes
# Print log lines that say why queries return SERVFAIL to clients
log-servfail: yes
# Print log lines to inform about local zone actions.
log-local-actions: no
# Have the validator print validation failures to the log (Default is 0, off)
val-log-level: 1
#
# Caching (in seconds)
#
# Time to live maximum for RRsets and messages in the cache
cache-max-ttl: 21600 # 6h (Default is 86400 - 1day)
# Time to live minimum for RRsets and messages in the cache
cache-min-ttl: 0
# Time to live maximum for negative responses
cache-max-negative-ttl: 3600 # 1h
# Time to live for entries in the host cache
# The host cache contains roundtrip timing, lameness and EDNS support information
# Default is 900
# NOTE: By setting the infra-ttl lower, unbound will probe servers that are not responsive more aggressively
# https://unbound.docs.nlnetlabs.nl/en/latest/reference/history/info-timeout-server-selection.html
infra-host-ttl: 60
#
# Connection
#
# Enable or disable whether IPv4 queries are answered or issued
do-ip4: yes
# Enable or disable whether IPv6 queries are answered or issued
do-ip6: no
# Enable or disable whether TCP queries are answered or issued
do-tcp: yes
# Enable or disable whether UDP queries are answered or issued
do-udp: yes
#
# TLS
#
# The upstream queries use TLS only for transport
tls-upstream: yes
# The port number on which to provide TCP TLS service
tls-port: 853
# These certificates are used for authenticating connections made to outside peers
# NOTE: To update run: sudo update-ca-certificates
tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
#
# DNSSEC
#
# Uses the DNSSEC NSEC chain to synthesize NXDOMAIN and other denials, using information from previous NXDOMAINs answers.
# It helps to reduce the query rate towards targets that get a very high nonexistent name lookup rate.
aggressive-nsec: yes
# This check sees if RRSIGs are present in the answer, when dnssec is expected,
# and retries another authority if RRSIGs are unexpectedly missing.
disable-dnssec-lame-check: no
# "validator iterator" will turn on DNSSEC validation
module-config: "validator iterator"
# Mark bogus messages as indeterminate, otherwise SERVFAIL
# For messages that are found to be secure the AD bit is set in replies
val-permissive-mode: no
# Count of validation restarts with another authority in case of failed validation
val-max-restart: 5
# TTL for data that has failed validation
val-bogus-ttl: 60
# The following line will configure unbound to perform cryptographic
# DNSSEC validation using the root trust anchor.
# Unbound needs rw access to this directory
# NOTE: Automatically updated, or update with:
# sudo unbound-anchor -v -a "/var/lib/unbound/root.key" -r "/usr/share/dns/root.hints" -c "/etc/ssl/certs/ca-certificates.crt"
auto-trust-anchor-file: "/var/lib/unbound/root.key"
#auto-trust-anchor-file: "/usr/share/dns/root.key"
# There may be multiple forward-zone: clauses.
# Each with a name: and zero or more hostnames or IP addresses.
# For the forward zone this list of nameservers is used to forward the queries to.
forward-zone:
# Name of the forward zone.
# This is the full domain name of the zone.
name: "."
# Enabled or disable whether the queries to this forwarder use TLS for transport
forward-tls-upstream: yes
# If enabled, data inside the forward is not cached
# This is useful when you want immediate changes to be visible (Default is no)
forward-no-cache: no
# The servers listed as forward-host: and forward-addr: have to handle further recursion for the query
# https://github.com/DigitaleGesellschaft/DNS-Resolver
# https://www.quad9.net/support/faq/
# IPv4 primary
forward-addr: 185.95.218.42@853#dns.digitale-gesellschaft.ch
forward-addr: 9.9.9.9@853#dns.quad9.net
# IPv4 secondary
forward-addr: 185.95.218.43@853#dns.digitale-gesellschaft.ch
forward-addr: 149.112.112.112@853#dns.quad9.net
# IPv6 primary
forward-addr: 2a05:fc84::42@853#dns.digitale-gesellschaft.ch
# Secure IPv6 primary: Blocklist, DNSSEC, No EDNS Client-Subnet
forward-addr: 2620:fe::fe@853#dns.quad9.net
# IPv6 secondary
forward-addr: 2a05:fc84::43@853#dns.digitale-gesellschaft.ch
# Secure IPv6 secondary: Blocklist, DNSSEC, No EDNS Client-Subnet
forward-addr: 2620:fe::9@853#dns.quad9.net
# If this is enabled, the unbound-control(8) utility can be used to send commands to the running Unbound server
remote-control:
# The option is used to enable remote control (default is no)
control-enable: no
# Give IPv4 or IPv6 addresses or local socket path to listen on for control commands.
# If you set it to an absolute path, a unix domain socket is used.
# This socket does not use the certificates and keys, so those files need not be present.
# To restrict access, Unbound sets permissions on the file to the user and group that is configured,
# the access bits are set to allow the group members to access the control socket file.
# By default localhost (127.0.0.1 and ::1) is listened to.
control-interface: /run/unbound.ctl
# The port number to listen on for IPv4 or IPv6 control interfaces, (default is 8953)
control-port: 8953
Run these commands below to make sure distro provided files don't mess up with our configuration.
But verify the 2 files exist in
/etc/unbound/unbound.conf.d, their actual location may be distro dependent.
Bash:
sudo mv "/etc/unbound/unbound.conf.d/remote-control.conf" "/etc/unbound/unbound.conf.d/remote-control.conf.bak"
sudo mv "/etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf" "/etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf.bak"
When you're done with config customization, check for errors in main file which includes our unbound configuration file:
Bash:
sudo unbound-checkconf -f /etc/unbound/unbound.conf
Make sure there is only one
*.conf file in /etc/unbound/unbound.conf.d (the one provided above), others should be renamed with *.bak extension.You want to configure
rsyslog if you enabled loging in config file.Create a new file named in
/etc/rsyslog.d and name it unbound.confCopy code below into it and save:
Bash:
# Configuration file: /etc/rsyslog.conf
# Includes files: /etc/rsyslog.d/
# https://www.rsyslog.com/doc/configuration/filters.html
# Log messages generated by unbound application
if $programname == "unbound" then /var/log/unbound/unbound.log
# stop processing it further
& stop
If you enabled loging you also want to rotate logs, here is config for
rotatelog.Create new file in
/etc/logrotate.d and name it unbound, then copy/paste code below into it and save:
Bash:
# Configuration file: /etc/logrotate.conf
# Includes files: /etc/logrotate.d/
# https://linux.die.net/man/8/logrotate
# Rotate unbound logs
/var/log/unbound/unbound.log {
daily
rotate 10
minsize 512k
compress
missingok
notifempty
noolddir
dateext
dateformat -%d.%m.%Y
create
maxage 30
postrotate
# When logrotate move the files, the services keep writing to the same file.
# Sending the HUP signal to crond will force it to close existing file handle and open new file handle to the original path
/usr/bin/systemctl kill -s HUP rsyslog.service > /dev/null 2>&1 || true
endscript
}
Finally apply changes:
Bash:
# NOTE: logrotate isn't meant to be enabled
sudo systemctl enable unbound
sudo systemctl enable rsyslog
sudo systemctl restart unbound
sudo systemctl restart rsyslog
sudo systemctl restart logrotate
sudo systemctl status rsyslog
sudo systemctl status logrotate
sudo systemctl status unbound
Enable DNS resolving with unbound
Unbound will listen by default on localhost (127.0.0.1) on port 53,
therefore
NetworkManager UI must be instructed to use this address for DNS.If you use
systemd-networkd or some other manager configure it to use 127.0.0.1 for DNS.To apply restart
NetworkManager or which ever manager you use and configured:
Bash:
sudo systemctl restart NetworkManager
The change in
NetworkManager UI can be verified with:
Bash:
cat /etc/resolv.conf
If it doesn't work and you lose internet access, you can simply undo changing DNS in NetworkManager.
Testing DNS
Now the moment of truth, we want to confirm that both DNS encryption and DNSSEC work, run this:
Bash:
dig +dnssec example.com
If you have no
dig command, install it with your package manager (package name is distro dependent).To confirm DNSSEC works there must be
ad flag in header section and RRSIG in answer section, see sample output below:Loging
Extended loging can be enabled in
/etc/unbound/unbound.conf.d/unbound.conf/etc/rsyslog.d/unbound.confconfiguration instructs rsyslog to write unbound logs to specific log file/etc/logrotate.d/unboundconfiguration contains code to rotate log file
This concludes this tutorial, if something doesn't work as expected or you have questions let me know.
Last edited:

