knot-resolver
knot-resolver
(kresd
, to distinguish it from the authoritative DNS
server knot!) is pretty neat, but not as popular as other recursive
resolvers.
kresd
has DNSSEC enabled by default, can run DoT/DoH, can serve the
same as well, supports RPZ, an DNS application firewall, policies,
views, persistent caches, has an prometheus endpoint, can use XDP
(!) and many other features. Platform support is decent as well –
I’ve run in on OpenBSD, FreeBSD and Linux.
I run kresd
on my router/firewall in a overly complicated
configuration, but I’ll describe a working configuration below in
hopes of making someone else interested in this software.
First of all, kresd
is configured in Lua. That might put some people
off. One great perk of this is that all modules are also written in
Lua, so they are easy to inspect and it’s (probably) easy
to write your own as well.
-- Specify where kresd should listen for requests:
net.listen('127.0.0.1', 53, { kind = 'dns', freebind = true })
net.listen('192.168.0.1', 53, { kind = 'dns', freebind = true })
net.listen('127.0.0.1', 8453, { kind = 'webmgmt' })
-- Query cache size
cache.size = 100 * MB
-- Each of these enable a lot of functionality, and I urge you to read the
-- documentation on each.
modules = {
-- The policy module implements policies for global query matching, e.g.
-- solves “how to react to certain query”.
"policy",
-- Serve static hints from /etc/hosts
"hints",
-- Serve stale records (up to 24 hours) if we timeout to our upstream NS
"serve_stale < cache",
-- Give us useful statistics, please
"stats",
-- Keep commonly used records hot in cache
"predict",
-- Enable web module (for webmgmt)
"http"
}
-- This is not recommended to run in production, but for a home lab it's
-- probably fine. Here we set a 30 minute window, and the period to 24 hours.
predict.config({ window = 30, period = 24*(60/30) })
-- I'm too lazy to setup a TLS certificate for this, allow the webmgmt interface
-- to be reached over HTTP. I use this as a prometheus endpoint.
http.config({
tls = false,
})
-- Sets the prefix for the prometheus metrics
http.prometheus.namespace = 'kresd_'
-- I use a local authoritative DNS server (knot, ironically) for my internal
-- zones. I just threw in example.com here due to reasons.
internal_domains = policy.todnames({
'home.arpa.',
'example.com.'
})
-- The authoritative server runs on 127.0.0.1, port 2153
policy.add(policy.suffix(policy.STUB({'127.0.0.1@2153'}), internal_domains))
-- Finally, recurse all other domains over TLS to these public resolvers.
policy.add(
policy.all(
policy.TLS_FORWARD({
{'8.8.8.8', hostname='dns.google' },
{'8.8.4.4', hostname='dns.google' },
{'1.1.1.1', hostname='cloudflare-dns.com' },
{'1.0.0.1', hostname='cloudflare-dns.com' },
{'9.9.9.9', hostname='dns.quad9.net' }
})
))
-- You can also use policy.slice() to forward different parts of queries to
-- different servers.