How to get faster and more secure DNS resolution with Knot Resolver on Fedora

Knot Resolver is a caching DNS resolver that can help speed up and secure DNS resolution on your Linux workstation. Here is how you set up Knot Resolver to validate DNSSEC and cache the DNS requests from your local device or set it up as a server for your other devices.

This tutorial is applicable to Knot Resolver version 2.1.0 and newer on Fedora 27. This tutorial will be updated to cover Debian 10 “Buster” and Ubuntu 18.04 “Bionic Beaver” in the future as packaging issues makes it more difficult to setup Knot Resolver on these distributions.

This tutorial is split in two sections: The first part deals with getting the service up and running for the local system, and how to enable it as a service for other systems on your network. You’ll have a fully working caching DNS server by following the first section. The second section goes more into details on configuration to enable some of Knot Resolver’s more advanced features.

Installing and starting Knot Resolver

Knot Resolver works out of the box with all the basic features without any configuration. I’ll get back to enabling some of the more interesting features of Knot Resolver in a later section of the tutorial.

Start by installing the knot-resolver package from your distribution’s package repository using your favorite package manager (e.g. apt or dnf). This tutorial assumes you’ll be installing version 2.1.0 or later.

There is actually just one step to get the service up and running: enable and start an instance of the service using the following command:

systemctl enable --now kresd@1.service

You can then immediately start using Knot Resolver on your local system. However, you must first ensure no other process is managing the /etc/resolv.conf configuration file before proceeding. Please read my extensive tutorial on taking manual control over the DNS configuration on Linux for details. Follow the instructions for configuring /etc/resolv.conf to use a DNS resolver running on localhost in the last section of that tutorial to actually use your Knot Resolver instance.

Allowing remote connections

By default, Knot Resolver will only allow connections from the local system. This is useful if you intend to use it as a caching server for a single device. However, you’ll get the best performance by sharing the same DNS cache between all the devices in your network. (Although it can be beneficial to also setup local caches on laptops and devices that roam between different networks.)

You can configure Knot Resolver to also listen on external network interfaces in a few different ways. However, we’ll stick to utilizing systemd service socket assignments. Create an override for Knot Resolver’s default socket configuration using the following command:

systemctl edit kresd.socket

This will open a blank text editor. By default, the service listens on TCP and UDP port 53 on the loopback interface (accessible to the local device local device only). Below is an example configuration that binds the service to all interfaces for TCP, and to specific interfaces for IPv4 and IPv6 by address. Refer to the systemd.socket (5) man page for more details on available configuration options.

[Socket]
ListenStream=
ListenStream=53
ListenDatagram=203.0.113.53:53
ListenDatagram=[2001:0db8::53:cafe]:53

The empty ListenStream option removes the default socket binding to the loopback interface, which allows you to rebind it to all interfaces (including loopback) on the next line. This takes care of all TCP connections. However, you can’t catch all UDP connections in the same way. Which is why you must set the external IP address you want to listen to in the configuration file. Note that you’re not removing the binding for the loopback interface with an empty configuration line, so that configuration is still in place for UDP.


The last step required is to poke a hole in your firewall for incoming connections. The below are example commands that create permissive rules with FirewallD or UFW. You should adjust these to only allow connections from your local network unless you want Knot Resolver to be available on the public internet.

# Add DNS to default FirewallD zone (CentOS, Fedora)
firewallctl zone "" --permanent add service "domain"

# Add DNS to (Ubuntu)
ufw allow "domain"

Tweaking the default configuration

You can find extensive documentation on optional modules and their configuration in Knot Resolver Modules documentation.

If you don’t want to read through all that documentation; I’ll suggest a configuration that improves reliance, improves security and performances, and improves compatibility.

Open up /etc/knot-resolver/kresd.conf in your favorite editor and include the following configuration:

-- Enable optional modules
modules = {
  'policy',
  'hints',
  'serve_stale < cache',
  'workarounds < iterate',
  'stats',
  'predict'
}

-- Forward queries to Quad9
policy.add(policy.all(policy.FORWARD({'9.9.9.9', '2620:fe::fe', '149.112.112.112'})))

-- Cache size
cache.size = 150 * MB

-- Prefetch learning (20-minute blocks over 24 hours)
predict.config(20, 72)

-- Enable DNSSEC validation
trust_anchors.file = '/etc/knot-resolver/root.keys'

Most of the above configuration is straightforward, but I’ll explain what the configuration changes do in more details.

The first set of changes load some additional modules in a specific order to enable additional features. E.g. the workarounds module is loaded before the core iterate module. The workarounds module contains a set of hotfixes to ensure compatibility with DNS servers with known compatibility problems with modern DNS standards.

The policy module can be used to manipulate request handling. I’ve set uncached requests to be forwarded to the IPv4 and IPv6 addresses of Quad9. Quad9 is a security-enhanced public DNS server that proactively blocks domains used to communicate with botnet and other malicious activity that promises to not log any requests from users. Quad9 will act as an intermediary caching proxy which will significantly improve performance of most DNS queries. If you remove this directive, Knot Resolver will have to resolve the whole DNS chain from the root servers and down to the queried domain’s DNS servers. This is significantly slower than using an intermediary caching provider.

The serve_stale module allows expired entries to be served from the cache for some additional time when the upstream DNS server is unable to respond to queries. This can help mitigate the impact on you with regards to the websites you visit the most during a large scale distributed denial of service (DDoS) attack against critical DNS infrastructure (as we’ve witnessed again and again in recent years).

The stats and predict modules work together to identify usage patterns and preemptively refresh expired queries from the cache. We all have patterns for when in the day we check our email and specific websites. So too do many of the internet-of-things devices we have in our homes. Knot Resolver may end up sending some unnecessary requests for this feature to work. However, in my experience its actually quite good at deciding which domains it should keep up-to-date in its cache and when it should stop updating domains I no longer visit.

Lastly, Knot Resolver is configured to validate DNSSEC. Domain owners can cryptographically sign their domains in a way that a validating resolver (such as Knot Resolver) can ensure that a third-party hasn’t modified or hijacked the domain. In Fedora, this just requires this line to be present.

Encrypt external DNS requests

All your DNS requests traditionally leave your network unencrypted, allowing for someone with the ability to intercept your network traffic to see what domains you’re accessing. You can encrypt this traffic with TLS with some additional configuration.

Note that not many DNS providers support TLS yet. You can replace the example forwarding configuration used above with the following configuration to use DNS over TLS from Quad9.

-- Forward queries encrypted to Quad9 over TLS
policy.add(policy.all(policy.TLS_FORWARD({
  {'9.9.9.9', hostname='dns.quad9.net', ca_file='/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem'},
  {'2620:fe::fe', hostname='dns.quad9.net', ca_file='/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem'},
  {'149.112.112.112', hostname='dns.quad9.net', ca_file='/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem'}
})))

The certificate bundle used in the configuration contains the same root certificates as your web browser trusts. You can install the bundle by installing the ca-certificates package from your package repository (it should be present on most systems by default, however).