Configuration
Simple configuration
The following example presents a simple configuration file
which can be used as a base for your Knot DNS setup:
# Example of a very simple Knot DNS configuration.
server:
listen: 0.0.0.0@53
listen: ::@53
zone:
- domain: example.com
storage: /var/lib/knot/zones/
file: example.com.zone
log:
- target: syslog
any: info
Now let's walk through this configuration step by step:
The listen statement in the server section
defines where the server will listen for incoming connections.
We have defined the server to listen on all available IPv4 and IPv6 addresses,
all on port 53.
The zone section defines the zones that the server will
serve. In this case, we defined one zone named example.com which is stored
in the zone file /var/lib/knot/zones/example.com.zone
.
The log section defines the log facilities for
the server. In this example, we told Knot DNS to send its log messages with
the severity info
or more serious to the syslog (or systemd journal).
For detailed description of all configuration items see
Configuration Reference.
Zone templates
A zone template allows a single zone configuration to be shared among several
zones. There is no inheritance between templates; they are exclusive. The
default
template identifier is reserved for the default template:
template:
- id: default
storage: /var/lib/knot/master
semantic-checks: on
- id: signed
storage: /var/lib/knot/signed
dnssec-signing: on
semantic-checks: on
master: [master1, master2]
- id: slave
storage: /var/lib/knot/slave
zone:
- domain: example1.com # Uses default template
- domain: example2.com # Uses default template
semantic-checks: off # Override default settings
- domain: example.cz
template: signed
master: master3 # Override masters to just master3
- domain: example1.eu
template: slave
master: master1
- domain: example2.eu
template: slave
master: master2
Note
Each template option can be explicitly overridden in zone-specific configuration.
Access control list (ACL)
Normal DNS queries are always allowed. All other DNS requests must be
authorized before they can be processed by the server. A zone can have
configured ACL which is a sequence of rules describing
what requests are authorized. An automatic ACL
feature can be used to simplify ACL management.
Every ACL rule can allow or deny one or more request types (actions)
based on the source IP address, network subnet, address range, protocol,
remote certificate key PIN and/or
if the request is secured by a given TSIG key. See keymgr -t
on how to generate a TSIG key.
If there are multiple ACL rules assigned to a zone, they are applied in the
specified order of the acl configuration. The first rule that matches
the given request is applied and the remaining rules are ignored. Some examples:
acl:
- id: address_rule
address: [2001:db8::1, 192.168.2.0/24]
action: transfer
- id: deny_rule
address: 192.168.2.100
action: transfer
deny: on
zone:
- domain: acl1.example.com
acl: [deny_rule, address_rule] # Allow some addresses with an exception
key:
- id: key1 # The real TSIG key name
algorithm: hmac-sha256
secret: 4Tc0K1QkcMCs7cOW2LuSWnxQY0qysdvsZlSb4yTN9pA=
acl:
- id: deny_all
address: 192.168.3.0/24
deny: on # No action specified and deny on implies denial of all actions
- id: key_rule
key: key1 # Access based just on TSIG key
action: [transfer, notify]
zone:
- domain: acl2.example.com
acl: [deny_all, key_rule] # Allow with the TSIG except for the subnet
In the case of dynamic DNS updates, some additional conditions may be specified
for more granular filtering. See more in the section Restricting dynamic updates.
Note
If more conditions (address ranges and/or a key)
are given in a single ACL rule, all of them have to be satisfied for the rule to match.
Tip
In order to restrict regular DNS queries, use module queryacl.
Secondary (slave) zone
Knot DNS doesn't strictly differ between primary (formerly known as master)
and secondary (formerly known as slave) zones. The only requirement for a secondary
zone is to have a master statement set. For effective zone synchronization,
incoming zone change notifications (NOTIFY), which require authorization, can be
enabled using automatic ACL or explicit ACL
configuration. Optional transaction authentication (TSIG) is supported for both
zone transfers and zone notifications:
server:
automatic-acl: on # Enabled automatic ACL
key:
- id: xfr_notify_key # Common TSIG key for XFR an NOTIFY
algorithm: hmac-sha256
secret: VFRejzw8h4M7mb0xZKRFiZAfhhd1eDGybjqHr2FV3vc=
remote:
- id: primary
address: [2001:DB8:1::1, 192.168.1.1] # Primary server IP addresses
# via: [2001:DB8:2::1, 10.0.0.1] # Local source addresses (optional)
key: xfr_notify_key # TSIG key (optional)
zone:
- domain: example.com
master: primary # Primary remote(s)
An example of explicit ACL with different TSIG keys for zone transfers
and notifications:
key:
- id: notify_key # TSIG key for NOTIFY
algorithm: hmac-sha256
secret: uBbhV4aeSS4fPd+wF2ZIn5pxOMF35xEtdq2ibi2hHEQ=
- id: xfr_key # TSIG key for XFR
algorithm: hmac-sha256
secret: VFRejzw8h4M7mb0xZKRFiZAfhhd1eDGybjqHr2FV3vc=
remote:
- id: primary
address: [2001:DB8:1::1, 192.168.1.1] # Primary server IP addresses
# via: [2001:DB8:2::1, 10.0.0.1] # Local source addresses if needed
key: xfr_key # Optional TSIG key
acl:
- id: notify_from_primary # ACL rule for NOTIFY from primary
address: [2001:DB8:1::1, 192.168.1.1] # Primary addresses (optional)
key: notify_key # TSIG key (optional)
action: notify
zone:
- domain: example.com
master: primary # Primary remote(s)
acl: notify_from_primary # Explicit ACL(s)
Note that the master option accepts a list of remotes, which are
queried for a zone refresh sequentially in the specified order. When the server
receives a zone change notification from a listed remote, only that remote is
used for a subsequent zone transfer.
Note
When transferring a lot of zones, the server may easily get into a state
where all available ports are in the TIME_WAIT state, thus transfers
cease until the operating system closes the ports for good. There are
several ways to work around this:
Allow reusing of ports in TIME_WAIT (sysctl -w net.ipv4.tcp_tw_reuse=1)
Shorten TIME_WAIT timeout (tcp_fin_timeout)
Increase available local port count
Primary (master) zone
A zone is considered primary if it doesn't have master set. As
outgoing zone transfers (XFR) require authorization, it must be enabled
using automatic ACL or explicit ACL
configuration. Outgoing zone change notifications (NOTIFY) to remotes can be
set by configuring notify. Transaction authentication
(TSIG) is supported for both zone transfers and zone notifications:
server:
automatic-acl: on # Enabled automatic ACL
key:
- id: xfr_notify_key # Common TSIG key for XFR an NOTIFY
algorithm: hmac-sha256
secret: VFRejzw8h4M7mb0xZKRFiZAfhhd1eDGybjqHr2FV3vc=
remote:
- id: secondary
address: [2001:DB8:1::1, 192.168.1.1] # Secondary server IP addresses
# via: [2001:DB8:2::1, 10.0.0.1] # Local source addresses (optional)
key: xfr_notify_key # TSIG key (optional)
acl:
- id: local_xfr # Allow XFR to localhost without TSIG
address: [::1, 127.0.0.1]
action: transfer
zone:
- domain: example.com
notify: secondary # Secondary remote(s)
acl: local_xfr # Explicit ACL for local XFR
Note that the notify option accepts a list of remotes, which are
all notified sequentially in the specified order.
A secondary zone may serve as a primary zone for a different set of remotes
at the same time.
Dynamic updates
Dynamic updates for the zone are allowed via proper ACL rule with the
update
action. If the zone is configured as a secondary and a DNS update
message is accepted, the server forwards the message to its first primary
master or ddns-master if configured.
The primary master's response is then forwarded back to the originator.
However, if the zone is configured as a primary, the update is accepted and
processed:
acl:
- id: update_acl
address: 192.168.3.0/24
action: update
zone:
- domain: example.com.
acl: update_acl
Note
To forward DDNS requests signed with a locally unknown key, an ACL rule for
the action update
without a key must be configured for the zone. E.g.:
acl:
- id: fwd_foreign_key
action: update
# possible non-key options
zone:
- domain: example.com.
acl: fwd_foreign_key
Restricting dynamic updates
There are several additional ACL options for dynamic DNS updates which affect
the request classification based on the update contents.
Updates can be restricted to specific resource record types:
acl:
- id: type_rule
action: update
update-type: [A, AAAA, MX] # Updated records must match one of the specified types
Another possibility is restriction on the owner name of updated records. The option
update-owner is used to select the source of domain
names which are used for the comparison. And the option update-owner-match
specifies the required relation between the record owner and the reference domain
names. Example:
acl:
- id: owner_rule1
action: update
update-owner: name # Updated record owners are restricted by the next conditions
update-owner-match: equal # The record owner must exactly match one name from the next list
update-owner-name: [foo, bar.] # Reference domain names
Note
If the specified owner name is non-FQDN (e.g. foo
), it's considered relatively
to the effective zone name. So it can apply to more zones
(e.g. foo.example.com.
or foo.example.net.
). Alternatively, if the
name is FQDN (e.g. bar.
), the rule only applies to this name.
If the reference domain name is the zone name, the following variant can be used:
acl:
- id: owner_rule2
action: update
update-owner: zone # The reference name is the zone name
update-owner-match: sub # Any record owner matches except for the zone name itself
template:
- id: default
acl: owner_rule2
zone:
- domain: example.com.
- domain: example.net.
The last variant is for the cases where the reference domain name is a TSIG key name,
which must be used for the transaction security:
key:
- id: example.com # Key names are always considered FQDN
...
- id: steve.example.net
...
- id: jane.example.net
...
acl:
- id: owner_rule3_com
action: update
update-owner: key # The reference name is the TSIG key name
update-owner-match: sub # The record owner must be a subdomain of the key name
key: [example.com] # One common key for updating all non-apex records
- id: owner_rule3_net
action: update
update-owner: key # The reference name is the TSIG key name
update-owner-match: equal # The record owner must exactly match the used key name
key: [steve.example.net, jane.example.net] # Keys for updating specific zone nodes
zone:
- domain: example.com.
acl: owner_rule3_com
- domain: example.net.
acl: owner_rule3_net
Automatic DNSSEC signing
Knot DNS supports automatic DNSSEC signing of zones. The signing
can operate in two modes:
Manual key management:
In this mode, the server maintains zone signatures (RRSIGs) only. The
signatures are kept up-to-date and signing keys are rolled according to
the timing parameters assigned to the keys. The keys must be generated and
timing parameters must be assigned by the zone operator.
Automatic key management:
In this mode, the server maintains signing keys. New keys are generated
according to the assigned policy and are rolled automatically in a safe manner.
No intervention from the zone operator is necessary.
For automatic DNSSEC signing, a policy must
be configured and assigned to the zone. The policy specifies how the zone
is signed (i.e. signing algorithm, key size, key lifetime, signature lifetime,
etc.). If no policy is specified, the default signing parameters are used.
The DNSSEC signing process maintains some metadata which is stored in the
KASP database. This database is backed
by LMDB.
Warning
Make sure to set the KASP database permissions correctly. For manual key
management, the database must be readable by the server process. For
automatic key management, it must be writeable. If no HSM is used,
the database also contains private key material – don't set the permissions
too weak.
Manual key management
For automatic DNSSEC signing with manual key management, the
manual has to be enabled in the policy:
policy:
- id: manual
manual: on
zone:
- domain: myzone.test
dnssec-signing: on
dnssec-policy: manual
To generate signing keys, use the keymgr utility.
For example, we can use Single-Type Signing:
$ keymgr myzone.test. generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes
And reload the server. The zone will be signed.
To perform a manual rollover of a key, the timing parameters of the key need
to be set. Let's roll the key. Generate a new key, but do not activate
it yet:
$ keymgr myzone.test. generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes active=+1d
Take the key ID (or key tag) of the old key and disable it the same time
the new key gets activated:
$ keymgr myzone.test. set <old_key_id> retire=+2d remove=+3d
Reload the server again. The new key will be published (i.e. the DNSKEY record
will be added into the zone). Remember to update the DS record in the
parent zone to include a reference to the new key. This must happen within one
day (in this case) including a delay required to propagate the new DS to
caches.
Automatic ZSK management
With manual disabled in the assigned policy (the default),
the DNSSEC keys are generated automatically (if they do not already exist)
and are also automatically rolled over according to their configured lifetimes.
The default zsk-lifetime is finite, whereas ksk-lifetime
infinite, meaning no KSK rollovers occur in the following example:
policy:
- id: custom_policy
signing-threads: 4
algorithm: ECDSAP256SHA256
zsk-lifetime: 60d
zone:
- domain: myzone.test
dnssec-signing: on
dnssec-policy: custom_policy
After configuring the server, reload the changes:
Check the server logs (regularly) to see whether everything went well.
Note
Enabling automatic key management with already existing keys requires attention:
Any key timers set to future timestamps are automatically cleared,
preventing interference with automatic operation procedures.
If the keys are in an inconsistent state (e.g. an unexpected number of keys
or active keys), it might lead to undefined behavior or, at the very least,
a halt in key management.
Automatic KSK management
For automatic KSK management, first configure ZSK management as described above,
and use submission section along with several options in
policy section, specifying the desired (finite) lifetime for
KSK and semi-automatic DS submission (see also DNSSEC key states and
DNSSEC key rollovers):
remote:
- id: parent_zone_server
address: 192.168.12.1@53
submission:
- id: parent_zone_sbm
parent: [parent_zone_server]
policy:
- id: custom_policy
signing-threads: 4
algorithm: ECDSAP256SHA256
zsk-lifetime: 60d
ksk-lifetime: 365d
ksk-submission: parent_zone_sbm
zone:
- domain: myzone.test
dnssec-signing: on
dnssec-policy: custom_policy
After the initially-generated KSK reaches its lifetime, new KSK is published and after
convenience delay the submission is started. The server publishes CDS and CDNSKEY records
and the user shall propagate them to the parent. The server periodically checks for
DS at the parent zone and when positive, finishes the rollover.
Note
When the initial keys are automatically generated for the first time, the KSK
is actually in the ready
state, allowing the initial parent DS submission
to take place automatically.
Zone signing
The signing process consists of the following steps:
Processing KASP database events. (e.g. performing a step of a rollover).
Updating the DNSKEY records. The whole DNSKEY set in zone apex is replaced
by the keys from the KASP database. Note that keys added into the zone file
manually will be removed. To add an extra DNSKEY record into the set, the
key must be imported into the KASP database (possibly deactivated).
Fixing the NSEC or NSEC3 chain.
Removing expired signatures, invalid signatures, signatures expiring
in a short time, and signatures issued by an unknown key.
Creating missing signatures. Unless the Single-Type Signing Scheme
is used, DNSKEY records in a zone apex are signed by KSK keys and
all other records are signed by ZSK keys.
Updating and re-signing SOA record.
The signing is initiated on the following occasions:
Start of the server
Zone reload
Reaching the signature refresh period
Key set changed due to rollover event
NSEC3 salt is changed
Received DDNS update
Forced zone re-sign via server control interface
On a forced zone re-sign, all signatures in the zone are dropped and recreated.
The knotc zone-status
command can be used to see when the next scheduled
DNSSEC re-sign will happen.
On-secondary (on-slave) signing
It is possible to enable automatic DNSSEC zone signing even on a secondary
server. If enabled, the zone is signed after every AXFR/IXFR transfer
from primary, so that the secondary always serves a signed up-to-date version
of the zone.
It is strongly recommended to block any outside access to the primary
server, so that only the secondary server's signed version of the zone is served.
Enabled on-secondary signing introduces events when the secondary zone changes
while the primary zone remains unchanged, such as a key rollover or
refreshing of RRSIG records, which cause inequality of zone SOA serial
between primary and secondary. The secondary server handles this by saving the
primary's SOA serial in a special variable inside KASP DB and appropriately
modifying AXFR/IXFR queries/answers to keep the communication with
primary server consistent while applying the changes with a different serial.
Catalog zones
Catalog zones (RFC 9432) are a concept whereby a list of zones to be configured is maintained
as contents of a separate, special zone. This approach has the benefit of simple
propagation of a zone list to secondary servers, especially when the list is
frequently updated.
Terminology first. Catalog zone is a meta-zone which shall not be a part
of the DNS tree, but it contains information about the set of member zones and
is transferable to secondary servers using common AXFR/IXFR techniques.
A catalog-member zone (or just member zone) is a zone based on
information from the catalog zone and not from configuration file/database.
Member properties are some additional information related to each member zone,
also distributed with the catalog zone.
A catalog zone is handled almost in the same way as a regular zone:
It can be configured using all the standard options (but for example
DNSSEC signing is useless as the zone won't be queried by clients), including primary/secondary configuration
and ACLs. A catalog zone is indicated by setting the option
catalog-role. Standard DNS queries to a catalog zone are answered
with REFUSED as though the zone doesn't exist unless there is a matching ACL
rule for action transfer configured.
The name of the catalog zone is arbitrary. It's possible to configure
multiple catalog zones.
Warning
Don't choose a name for a catalog zone below a name of any other
existing zones configured on the server as it would effectively "shadow"
part of your DNS subtree.
Upon catalog zone (re)load or change, all the PTR records in the format
unique-id.zones.catalog. 0 IN PTR member.com.
(but not too.deep.zones.catalog.
!)
are processed and member zones created, with zone names taken from the
PTR records' RData, and zone settings taken from the configuration
templates specified by catalog-template.
The owner names of the PTR records shall follow this scheme:
<unique-id>.zones.<catalog-zone>.
where the mentioned labels shall match:
Additionally, records in the format
group.unique-id.zones.catalog. 0 IN TXT "conf-template"
are processed as a definition of the member's group property. The
unique-id
must match the one of the PTR record defining the member.
It's required that at most one group is defined for each member. If multiple
groups are defined, one group is picked at random.
All other records and other member properties are ignored. They remain in the catalog
zone, however, and might be for example transferred to a secondary server,
which may interpret catalog zones differently. SOA still needs to be present in
the catalog zone and its serial handled appropriately. An apex NS record must be
present as for any other zone. The version record version 0 IN TXT "2"
is required at the catalog zone apex.
A catalog zone may be modified using any standard means (e.g. AXFR/IXFR, DDNS,
zone file reload). In the case of incremental change, only affected
member zones are reloaded.
The catalog zone must have at least one catalog-template
configured. The configuration for any defined member zone is taken from its
group property value, which should match some catalog-template name.
If the group property is not defined for a member, is empty, or doesn't match
any of defined catalog-template names, the first catalog-template
(in the order from configuration) is used. Nesting of catalog zones isn't
supported.
Any de-cataloged member zone is purged immediately, including its
zone file, journal, timers, and DNSSEC keys. The zone file is not
deleted if zonefile-sync is set to -1 for member zones.
Any member zone, whose PTR record's owner has been changed, is purged
immediately if and only if the <unique-id> has been changed.
When setting up catalog zones, it might be useful to set
catalog-db and catalog-db-max-size
to non-default values.
Note
Whenever a catalog zone is updated, the server reloads itself with
all configured zones, including possibly existing other catalog zones.
It's similar to calling knotc zone-reload (for all zones).
The consequence is that new zone files might be discovered and reloaded,
even for zones that do not relate to updated catalog zone.
Catalog zones never expire automatically, regardless of what is declared
in the catalog zone SOA. However, a catalog zone can be expired manually
at any time using knotc -f zone-purge +expire.
Currently, expiration of a catalog zone doesn't have any effect on its
member zones.
Warning
The server does not work well if one member zone appears in two catalog zones
concurrently. The user is encouraged to avoid this situation whatsoever.
Thus, there is no way a member zone can be migrated from one catalog
to another while preserving its metadata. Following steps may be used
as a workaround:
Back up the member zone's metadata
(on each server separately).
Remove the member zone from the catalog it's a member of.
Wait for the catalog zone to be propagated to all servers.
Add the member zone to the other catalog.
Restore the backed up metadata (on each server separately).
Catalog zones configuration examples
Below are configuration snippets (e.g. server and log sections missing)
of very simple catalog zone setups, in order to illustrate the relations
between catalog-related configuration options.
First setup represents a very simple scenario where the primary is
the catalog zone generator and the secondary is the catalog zone consumer.
Primary configuration:
acl:
- id: slave_xfr
address: ...
action: transfer
template:
- id: mmemb
catalog-role: member
catalog-zone: catz.
acl: slave_xfr
zone:
- domain: catz.
catalog-role: generate
acl: slave_xfr
- domain: foo.com.
template: mmemb
- domain: bar.com.
template: mmemb
Secondary configuration:
acl:
- id: master_notify
address: ...
action: notify
template:
- id: smemb
master: master
acl: master_notify
zone:
- domain: catz.
master: master
acl: master_notify
catalog-role: interpret
catalog-template: smemb
When new zones are added (or removed) to the primary configuration with assigned
mmemb template, they will automatically propagate to the secondary
and have the smemb template assigned there.
Second example is with a hand-written (or script-generated) catalog zone,
while employing configuration groups:
catz. 0 SOA invalid. invalid. 1625079950 3600 600 2147483646 0
catz. 0 NS invalid.
version.catz. 0 TXT "2"
nj2xg5bnmz2w4ltd.zones.catz. 0 PTR just-fun.com.
group.nj2xg5bnmz2w4ltd.zones.catz. 0 TXT unsigned
nvxxezjnmz2w4ltd.zones.catz. 0 PTR more-fun.com.
group.nvxxezjnmz2w4ltd.zones.catz. 0 TXT unsigned
nfwxa33sorqw45bo.zones.catz. 0 PTR important.com.
group.nfwxa33sorqw45bo.zones.catz. 0 TXT signed
mjqw42zomnxw2lq0.zones.catz. 0 PTR bank.com.
group.mjqw42zomnxw2lq0.zones.catz. 0 TXT signed
And the server in this case is configured to distinguish the groups by applying
different templates:
template:
- id: unsigned
...
- id: signed
dnssec-signing: on
dnssec-policy: ...
...
zone:
- domain: catz.
file: ...
catalog-role: interpret
catalog-template: [ unsigned, signed ]
Database zone backend
It is possible to use Redis 7.0+ or a Valkey
database for storing zone contents. The database does not need to be located on
the same machine as the Knot DNS servers. One or more servers can read from
or write to the same database. However, if multiple writers are used, they
must be configured carefully to avoid conflicts or unexpected interactions.
On the database side, the knot
module must be available (e.g. by installing
the redis-knot
package from a repository)
and loaded into the database. This is usually done by adding the following
line to the database configuration:
loadmodule /usr/lib/redis/modules/knot.so [<parameter> <value>]...
where the optional module parameters are:
default-ttl
— The default record TTL value if not specified (default is 600 seconds).
max-event-age
— The maximum age of the stored zone events (default is 1200 seconds).
max-update-depth
— The maximum depth of the zone update history (default is 20).
On the Knot DNS side, at least an address or a UNIX socket path must be configured
zone-db-listen in the database section. Optionally,
TLS communication can be enabled using zone-db-tls,
zone-db-cert-key, or zone-db-cert-hostname.
For example:
database:
zone-db-listen: /run/redis/redis-server.sock
or:
database:
zone-db-listen: 192.168.1.2@6379
zone-db-tls: on
zone-db-cert-key: s3L4U7E41DnN2tHFT8bu9DQe6eo2ySehlltyzdLbWjg=
In the database, up to 8 independent versions of zone contents — called zone instances
— can be stored per zone, numbered from 1 to 8. Whether a zone is loaded from
or store to the database is configured independently using the zone-db-input
and zone-db-output options, whose values correspond to zone instances.
For example, if a zone should be read from database instance 1 and the
DNSSEC-signed version stored in instance 2, the configuration may look like this:
zone:
- domain: example.com.
dnssec-signing: on
zone-db-input: 1
zone-db-output: 2
It is still possible to combine zone database backend with zone file or journal
operations.
For manual operations on the database, see Zone database.
DNS over QUIC
QUIC is a low-latency, encrypted, internet transport protocol.
Knot DNS supports DNS over QUIC (DoQ) (RFC 9250), including zone transfers (XoQ).
By default, the UDP port 853 is used for DNS over QUIC.
To use QUIC, a server private key and a certificate
must be available. If no key is configured, the server automatically generates one
with a self-signed temporary certificate. The key is stored in the KASP database
directory for persistence across restarts.
In order to listen for incoming requests over QUIC, at least one interface
or XDP interface must be configured.
An example of configuration of listening for DNS over QUIC on the loopback interface:
When the server is started, it logs some interface details and public key pin
of the used certificate:
... info: binding to QUIC interface ::1@853
... info: QUIC/TLS, certificate public key 0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw=
Tip
The public key pin, which isn't secret, can also be displayed via:
$ knotc status cert-key
0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw=
Or from the keyfile via:
$ certtool --infile=quic_key.pem -k | grep pin-sha256
pin-sha256:0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw=
Using kdig we can verify that the server responds over QUIC:
$ kdig @::1 ch txt version.server +quic
;; QUIC session (QUICv1)-(TLS1.3)-(ECDHE-X25519)-(EdDSA-Ed25519)-(AES-256-GCM)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 0
;; Flags: qr rd; QUERY: 1; ANSWER: 1; AUTHORITY: 0; ADDITIONAL: 1
;; EDNS PSEUDOSECTION:
;; Version: 0; flags: ; UDP size: 1232 B; ext-rcode: NOERROR
;; PADDING: 370 B
;; QUESTION SECTION:
;; version.server. CH TXT
;; ANSWER SECTION:
version.server. 0 CH TXT "Knot DNS 3.4.0"
;; Received 468 B
;; Time 2024-06-21 08:30:12 CEST
;; From ::1@853(QUIC) in 1.1 ms
In this case, opportunistic authentication was
used, which doesn't guarantee that the client communicates with the genuine server
and vice versa. For strict authentication
of the server, we can enforce certificate key pin check by specifying it
(enabled debug mode for details):
$ kdig @::1 ch txt version.server +tls-pin=0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw= +quic -d
;; DEBUG: Querying for owner(version.server.), class(3), type(16), server(::1), port(853), protocol(UDP)
;; DEBUG: TLS, received certificate hierarchy:
;; DEBUG: #1, CN=tester
;; DEBUG: SHA-256 PIN: 0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw=, MATCH
;; DEBUG: TLS, skipping certificate verification
;; QUIC session (QUICv1)-(TLS1.3)-(ECDHE-X25519)-(EdDSA-Ed25519)-(AES-256-GCM)
...
We see that a server certificate key matches the specified pin. Another possibility
is to use certificate chain validation if a suitable certificate is configured
on the server.
Zone transfers
For outgoing requests (e.g. NOTIFY and refresh), Knot DNS utilizes
session resumption, which speeds up QUIC connection
establishment.
Here are a few examples of zone transfer configurations using various
authentication mechanisms:
Opportunistic authentication:
Primary and secondary can authenticate using TSIG. Fallback to clear-text DNS
isn't supported.
Primary:
server:
listen-quic: ::1
automatic-acl: on
key:
- id: xfr_key
algorithm: hmac-sha256
secret: S059OFJv1SCDdR2P6JKENgWaM409iq2X44igcJdERhc=
remote:
- id: secondary
address: ::2
key: xfr_key # TSIG for secondary authentication
quic: on
zone:
- domain: example.com
notify: secondary
Secondary:
server:
listen-quic: ::2
automatic-acl: on
key:
- id: xfr_key
algorithm: hmac-sha256
secret: S059OFJv1SCDdR2P6JKENgWaM409iq2X44igcJdERhc=
remote:
- id: primary
address: ::1
key: xfr_key # TSIG for primary authentication
quic: on
zone:
- domain: example.com
master: primary
Strict authentication:
Note that the automatic ACL doesn't work in this case due to asymmetrical
configuration. The secondary can authenticate using TSIG.
With PIN checks:
Primary:
server:
listen-quic: ::1
key:
- id: secondary_key
algorithm: hmac-sha256
secret: S059OFJv1SCDdR2P6JKENgWaM409iq2X44igcJdERhc=
remote:
- id: secondary
address: ::2
quic: on
acl:
- id: secondary_xfr
address: ::2
key: secondary_key # TSIG for secondary authentication
action: transfer
zone:
- domain: example.com
notify: secondary
acl: secondary_xfr
Secondary:
server:
listen-quic: ::2
key:
- id: secondary_key
algorithm: hmac-sha256
secret: S059OFJv1SCDdR2P6JKENgWaM409iq2X44igcJdERhc=
remote:
- id: primary
address: ::1
key: secondary_key # TSIG for secondary authentication
quic: on
acl:
- id: primary_notify
address: ::1
cert-key: 0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw=
action: notify
zone:
- domain: example.com
master: primary
acl: primary_notify
With CA and hostname checks:
Primary
server:
listen-quic: ::1
cert-file: primary-cert.pem
key-file: primary-key.pem
key:
- id: secondary_key
algorithm: hmac-sha256
secret: S059OFJv1SCDdR2P6JKENgWaM409iq2X44igcJdERhc=
remote:
- id: secondary
address: ::2
quic: on
acl:
- id: secondary_xfr
address: ::2
key: secondary_key # TSIG for secondary authentication
action: transfer
zone:
- domain: example.com
notify: secondary
acl: secondary_xfr
Secondary:
server:
listen-quic: ::2
ca-file: ca-cert.pem
key:
- id: secondary_key
algorithm: hmac-sha256
secret: S059OFJv1SCDdR2P6JKENgWaM409iq2X44igcJdERhc=
remote:
- id: primary
address: ::1
key: secondary_key # TSIG for secondary authentication
quic: on
acl:
- id: primary_notify
address: ::1
cert-hostname: "Primary Knot"
action: notify
zone:
- domain: example.com
master: primary
acl: primary_notify
Mutual authentication:
The mutual authentication guarantees authentication
for both the primary and the secondary. In this case, TSIG would be redundant.
This mode is recommended if possible.
With PIN checks:
Primary:
server:
listen-quic: ::1
automatic-acl: on
remote:
- id: secondary
address: ::2
quic: on
cert-key: PXqv7/lXn6N7scg/KJWvfU/TEPe5BoIUHQGRLMPr6YQ=
zone:
- domain: example.com
notify: secondary
Secondary:
server:
listen-quic: ::2
automatic-acl: on
remote:
- id: primary
address: ::1
quic: on
cert-key: 0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw=
zone:
- domain: example.com
master: primary
With CA and hostname checks:
Primary:
server:
listen-quic: ::1
ca-file: ca-cert.pem
cert-file: primary-cert.pem
key-file: primary-key.pem
automatic-acl: on
remote:
- id: secondary
address: ::2
quic: on
cert-hostname: "Secondary Knot"
zone:
- domain: example.com
notify: secondary
Secondary:
server:
listen-quic: ::2
ca-file: ca-cert.pem
cert-file: secondary-cert.pem
key-file: secondary-key.pem
automatic-acl: on
remote:
- id: primary
address: ::1
quic: on
cert-hostname: "Primary Knot"
zone:
- domain: example.com
master: primary
Tip
Using GnuTLS certtool you can generate a CA certificate with its private key:
$ certtool --generate-privkey --key-type ed25519 --outfile ca-key.pem
$ echo -e "cn = \"My Example CA\"\nca\ncert_signing_key\nexpiration_days = 3650" >ca-template.info
$ certtool --generate-self-signed --load-privkey ca-key.pem \
--template ca-template.info --outfile ca-cert.pem
Then create certificates signed with this CA like so:
$ CERT_NAME="primary"
$ certtool --generate-privkey --key-type ed25519 --outfile ${CERT_NAME}-key.pem
$ echo -e "dns_name = \"${CERT_NAME} server\"\nexpiration_days = 365" >${CERT_NAME}-template.info
$ certtool --generate-certificate --load-privkey ${CERT_NAME}-key.pem \
--load-ca-certificate ca-cert.pem --load-ca-privkey ca-key.pem \
--template ${CERT_NAME}-template.info --outfile ${CERT_NAME}-cert.pem
If you want to use a wildcard DNSName in your certificate, beware that
GnuTLS, which is the TLS backend for Knot DNS, will not verify wildcard
names directly under TLDs (like *.example
).
To see a server's TLS hostnames:
$ kdig @1.1.1.1 +tls -dd
;; DEBUG: Querying for owner(.), class(1), type(2), server(1.1.1.1), port(853), protocol(TCP)
;; DEBUG: TLS, received certificate hierarchy:
;; DEBUG: #1, CN=cloudflare-dns.com,O=Cloudflare\, Inc.,L=San Francisco,ST=California,C=US
;; DEBUG: Subject Alternative Name:
;; DEBUG: DNSname: cloudflare-dns.com
;; DEBUG: DNSname: *.cloudflare-dns.com
;; DEBUG: DNSname: one.one.one.one
[...]
Knot DNS will only verify hostnames under the Subject Alternative Name
extension in compliance with RFC 8310 Section 8.1.
Note
Certificate validation by CA and hostname is more computationally expensive
than by PIN, but PIN checking has the disadvantage of relying on constantness
of the public key.
DNS over TLS
TLS is an encrypted internet transport protocol.
Knot DNS supports DNS over TLS (DoT) (RFC 7858), including zone transfers (XoT).
By default, the TCP port 853 is used for DNS over TLS.
There are the same requirements for TLS key and certificate as for DNS over QUIC.
In order to listen for incoming requests over TLS, interface
must be configured.
An example of configuration of listening for DNS over TLS on the loopback interface:
When the server is started, it logs some interface details and public key pin
of the used certificate:
... info: binding to TLS interface ::1@853
... info: QUIC/TLS, certificate public key 0xtdayWpnJh4Py8goi8cei/gXGD4kJQ+HEqcxS++DBw=
Using kdig we can verify that the server responds over TLS:
$ kdig @::1 ch txt version.server +tls
;; TLS session (TLS1.3)-(ECDHE-X25519)-(EdDSA-Ed25519)-(AES-256-GCM)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 0
;; Flags: qr rd; QUERY: 1; ANSWER: 1; AUTHORITY: 0; ADDITIONAL: 1
;; EDNS PSEUDOSECTION:
;; Version: 0; flags: ; UDP size: 1232 B; ext-rcode: NOERROR
;; PADDING: 370 B
;; QUESTION SECTION:
;; version.server. CH TXT
;; ANSWER SECTION:
version.server. 0 CH TXT "Knot DNS 3.4.0"
;; Received 468 B
;; Time 2024-06-21 08:31:13 CEST
;; From ::1@853(TLS) in 9.1 ms
Zone transfer configuration and authentication profiles are almost identical
to DNS over QUIC, with the only difference being the enabling of
tls for the corresponding remotes.
Query modules
Knot DNS supports configurable query modules that can alter the way
queries are processed. Each query requires a finite number of steps to
be resolved. We call this set of steps a query plan, an abstraction
that groups these steps into several stages.
For example, processing an Internet-class query needs to find an
answer. Then based on the previous state, it may also append an
authority SOA or provide additional records. Each of these actions
represents a 'processing step'. Now, if a query module is loaded for a
zone, it is provided with an implicit query plan which can be extended
by the module or even changed altogether.
A module is active if its name, which includes the mod-
prefix, is assigned
to the zone/template module option or to the default
template
global-module option if activating for all queries.
If the module is configurable, a corresponding module section with
an identifier must be created and then referenced in the form of
module_name/module_id
. See Modules for the list of available modules.
The same module can be specified multiple times, such as a global module and
a per-zone module, or with different configurations. However, not all modules
are intended for this, for example, mod-cookies! Global modules are executed
before per-zone modules.
Note
Query modules are processed in the order they are specified in the
zone/template configuration. In most cases, the recommended order is:
mod-synthrecord, mod-onlinesign, mod-cookies, mod-rrl, mod-dnstap, mod-stats
Operation
The Knot DNS server part knotd can run either in the foreground,
or in the background using the -d
option. When run in the foreground, it
doesn't create a PID file. Other than that, there are no differences and you
can control both the same way.
The tool knotc is designed as a user front-end, making it easier
to control a running server daemon. If you want to control the daemon directly,
use SIGINT
to quit the process or SIGHUP
to reload the configuration.
If you pass neither configuration file (-c
parameter) nor configuration
database (-C
parameter), the server will first attempt to use the default
configuration database stored in /var/lib/knot/confdb
or the
default configuration file stored in /etc/knot/knot.conf
. Both the
default paths can be reconfigured with --with-storage=path
or
--with-configdir=path
respectively.
Example of server start as a daemon:
Example of server shutdown:
$ knotc -c knot.conf stop
For a complete list of actions refer to the program help (-h
parameter)
or to the corresponding manual page.
Also, the server needs to create rundir and storage
directories in order to run properly.
Note
Avoid editing of or other manipulation with configuration file during start
or reload of knotd or start of knotc
and other utilities which use it. There is a risk of
malfunction or a crash otherwise.
Configuration database
In the case of a huge configuration file, the configuration can be stored
in a binary database. Such a database can be simply initialized:
or preloaded from a file:
$ knotc conf-import input.conf
Also the configuration database can be exported into a textual file:
$ knotc conf-export output.conf
Warning
The import and export commands access the configuration database
directly, without any interaction with the server. Therefore, any data
not yet committed to the database won't be exported. And the server won't
reflect imported configuration correctly. So it is strictly recommended to
import new configuration when the server is not running.
Dynamic configuration
The configuration database can be accessed using the server control interface
while the server is running. To get the full power of the dynamic configuration,
the server must be started with a specified configuration database location
or with the default database initialized. Otherwise all the changes to the
configuration will be temporary (until the server is stopped).
Note
The database can be imported in advance.
Most of the commands get an item name and value parameters. The item name is
in the form of section[identifier].name
. If the item is multivalued,
more values can be specified as individual (command line) arguments.
Caution
Beware of the possibility of pathname expansion by the shell. For this reason,
it is advisable to escape (with backslash) square brackets or to quote command parameters if
not executed in the interactive mode.
To get the list of configuration sections or to get the list of section items:
$ knotc conf-list
$ knotc conf-list 'server'
To get the whole configuration or to get the whole configuration section or
to get all section identifiers or to get a specific configuration item:
$ knotc conf-read
$ knotc conf-read 'remote'
$ knotc conf-read 'zone.domain'
$ knotc conf-read 'zone[example.com].master'
Warning
The following operations don't work on OpenBSD!
Modifying operations require an active configuration database transaction.
Just one transaction can be active at a time. Such a transaction then can
be aborted or committed. A semantic check is executed automatically before
every commit:
$ knotc conf-begin
$ knotc conf-abort
$ knotc conf-commit
To set a configuration item value or to add more values or to add a new
section identifier or to add a value to all identified sections:
$ knotc conf-set 'server.identity' 'Knot DNS'
$ knotc conf-set 'server.listen' '0.0.0.0@53' '::@53'
$ knotc conf-set 'zone[example.com]'
$ knotc conf-set 'zone.slave' 'slave2'
Note
Also the include operation can be performed. A non-absolute file
location is relative to the server binary path, not to the control binary
path!
$ knotc conf-set 'include' '/tmp/new_zones.conf'
To unset the whole configuration or to unset the whole configuration section
or to unset an identified section or to unset an item or to unset a specific
item value:
$ knotc conf-unset
$ knotc conf-unset 'zone'
$ knotc conf-unset 'zone[example.com]'
$ knotc conf-unset 'zone[example.com].master'
$ knotc conf-unset 'zone[example.com].master' 'remote2' 'remote5'
To get the change between the current configuration and the active transaction
for the whole configuration or for a specific section or for a specific
identified section or for a specific item:
$ knotc conf-diff
$ knotc conf-diff 'zone'
$ knotc conf-diff 'zone[example.com]'
$ knotc conf-diff 'zone[example.com].master'
An example of possible configuration initialization:
$ knotc conf-begin
$ knotc conf-set 'server.listen' '0.0.0.0@53' '::@53'
$ knotc conf-set 'remote[master_server]'
$ knotc conf-set 'remote[master_server].address' '192.168.1.1'
$ knotc conf-set 'template[default]'
$ knotc conf-set 'template[default].storage' '/var/lib/knot/zones/'
$ knotc conf-set 'template[default].master' 'master_server'
$ knotc conf-set 'zone[example.com]'
$ knotc conf-diff
$ knotc conf-commit
Secondary (slave) mode
Running the server as a secondary is very straightforward as the zone
is transfered automatically from a remote server. The received zone is
usually stored in a zone file after the zonefile-sync period
elapses. Zone differences are stored in the zone journal.
Primary (master) mode
If you just want to check the zone files before starting, you can use:
$ knotc zone-check example.com
Reading and editing zones
Knot DNS allows you to read or change zone contents online using the server
control interface.
To get contents of all configured zones, or a specific zone contents, or zone
records with a specific owner, or even with a specific record type:
$ knotc zone-read --
$ knotc zone-read example.com
$ knotc zone-read example.com ns1
$ knotc zone-read example.com ns1 NS
Note
If the record owner is not a fully qualified domain name, then it is
considered as a relative name to the zone name.
To start a writing transaction on all zones or on specific zones:
$ knotc zone-begin --
$ knotc zone-begin example.com example.net
Now you can list all nodes within the transaction using the zone-get
command, which always returns current data with all changes included. The
command has the same syntax as zone-read
.
Warning
If a zone transaction is open, all zone events that modify the zone
(e.g. DNSSEC signing) are blocked until the transaction is eithe committed
or aborted. It is therefore advisable to finish the transaction quickly and
commit without delay.
Within the transaction, you can add a record to a specific zone or to all
zones with an open transaction:
$ knotc zone-set example.com ns1 3600 A 192.168.0.1
$ knotc zone-set -- ns1 3600 A 192.168.0.1
To remove all records with a specific owner, or a specific rrset, or
specific record data:
$ knotc zone-unset example.com ns1
$ knotc zone-unset example.com ns1 A
$ knotc zone-unset example.com ns1 A 192.168.0.2
To see the difference between the original zone and the current version:
$ knotc zone-diff example.com
Finally, either commit or abort your transaction:
$ knotc zone-commit example.com
$ knotc zone-abort example.com
A full example of setting up a completely new zone from scratch:
$ knotc conf-begin
$ knotc conf-set zone.domain example.com
$ knotc conf-commit
$ knotc zone-begin example.com
$ knotc zone-set example.com @ 3600 SOA ns admin 1 86400 900 691200 3600
$ knotc zone-set example.com @ 3600 NS ns
$ knotc zone-set example.com ns 3600 A 192.168.0.1
$ knotc zone-set example.com ns 3600 AAAA 2001:DB8::1
$ knotc zone-commit example.com
Note
If quotes are necessary for record data specification, remember to escape them:
$ knotc zone-set example.com @ 3600 TXT \"v=spf1 a:mail.example.com -all\"
Reading and editing the zone file safely
It's always possible to read and edit zone contents via zone file manipulation.
It may lead to confusion or even a program crash, however, if
the zone contents are continuously being changed by DDNS, DNSSEC signing and the like.
In such a case, the safe way to modify the zone file is to freeze zone events first:
$ knotc -b zone-freeze example.com.
$ knotc -b zone-flush example.com.
After calling freeze on the zone, there still may be running zone operations (e.g. signing),
causing freeze pending. Because of this, the blocking mode is used to ensure
the operation was finished. Then the zone can be flushed to a file.
Now the zone file can be safely modified (e.g. using a text editor).
If zonefile-load is not set to difference-no-serial, it's also necessary to
increase SOA serial in this step to keep consistency. Finally, we can load the
modified zone file and if successful, thaw the zone:
$ knotc -b zone-reload example.com.
$ knotc zone-thaw example.com.
Zone loading
The process of how the server loads a zone is influenced by the configuration of the
zonefile-load and journal-content
parameters (also DNSSEC signing applies), the existence of a zone file and journal
(and their relative out-of-dateness), and whether it is a cold start of the server
or a zone reload (e.g. invoked by the knotc interface). Please note
that zone transfers are not taken into account here – they are planned after the zone
is loaded (including zone bootstrap).
If the zone file exists and is not excluded by the configuration, it is first loaded
and according to its SOA serial number, relevant journal changesets are applied.
If this is a zone reload and we have zonefile-load set to difference, the difference
between old and new contents is computed and stored in the journal like an update.
The zone file should be either unchanged since last load or changed with incremented
SOA serial. In the case of a decreased SOA serial, the load is interrupted with
an error; if unchanged, it is increased by the server.
If the procedure described above succeeds without errors, the resulting zone contents are (after potential DNSSEC signing)
used as the new zone.
The option journal-content set to all lets the server, beside better performance, keep
track of the zone contents also across server restarts. It makes the cold start
effectively work like a zone reload with the old contents loaded from the journal
(unless this is the very first start with the zone not yet saved into the journal).
Journal behaviour
The zone journal keeps some history of changes made to the zone. It is useful for
responding to IXFR queries. Also if zone file flush is disabled, the
journal keeps the difference between the zone file and the current zone in case of server shutdown.
The history is stored in changesets – differences of zone contents between two
(usually subsequent) zone versions (specified by SOA serials).
Journals of all zones are stored in a common LMDB database. Huge changesets are
split into 15-70 KiB blocks to prevent fragmentation of the DB. The
journal does each operation in one transaction to keep consistency of the DB and performance.
Each zone journal has its own occupation limits maximum usage
and maximum depth. Changesets are stored in the journal
one by one. When hitting any of the limits, the zone is flushed into the zone file
if there are no redundant changesets to delete, and the oldest changesets are deleted.
In the case of the size limit, twice the needed amount of space is purged
to prevent overly frequent deletes.
If zone file flush is disabled, then instead of flushing
the zone, the journal tries to save space by merging the changesets into a special one.
This approach is effective if the changes rewrite each other, e.g. periodically
changing the same zone records, re-signing the whole zone etc. Thus the difference between the zone
file and the zone is still preserved even if the journal deletes some older changesets.
If the journal is used to store both zone history and contents, a special changeset
is present with zone contents. When the journal gets full, the changes are merged into this
special changeset.
There is also a safety hard limit for overall
journal database size, but it's strongly recommended to set the per-zone limits in
a way to prevent hitting this one. For LMDB, it's hard to recover from the
database-full state. For wiping one zone's journal, see knotc zone-purge +journal
command.
Handling zone file, journal, changes, serials
Some configuration options regarding the zone file and journal, together with operation
procedures, might lead to unexpected results. This chapter points out
potential interference and both recommends and warns before some combinations thereof.
Unfortunately, there is no optimal combination of configuration options,
every approach has some disadvantages.
Example 1
Keep the zone file updated:
zonefile-sync: 0
zonefile-load: whole
journal-content: changes
These are default values. The user can always check the current zone
contents in the zone file, and also modify it (recommended with server turned-off or
taking the safe way). The journal serves here just as a source of
history for secondary servers' IXFR. Some users dislike that the server overwrites their
prettily prepared zone file.
Example 2
Zonefileless setup:
zonefile-sync: -1
zonefile-load: none
journal-content: all
Zone contents are stored only in the journal. The zone is updated by DDNS,
zone transfer, or via the control interface. The user might have filled the
zone contents initially from a zone file by setting zonefile-load to
whole temporarily.
It's also a good setup for secondary servers. Anyway, it's recommended to carefully tune
the journal-size-related options to avoid surprises like the journal getting full
(see Journal behaviour).
Example 3
Input-only zone file:
zonefile-sync: -1
zonefile-load: difference
journal-content: changes
The user can make changes to the zone by editing the zone file, and his pretty zone file
is never overwritten or filled with DNSSEC-related autogenerated records – they are
only stored in the journal.
Warning
The zone file's SOA serial must be properly set to a number which is higher than the
current SOA serial in the zone (not in the zone file) if manually updated!
This is important to ensure consistency of the journal and outgoing IXFR.
Note
This mode is not suitable if the zone can be modified externally (e.g. DDNS, knotc).
Example 4
Auto-increment SOA serial:
zonefile-sync: -1
zonefile-load: difference-no-serial
journal-content: all
This is similar to the previous setup, but the SOA serial is handled by the server
automatically. So the user no longer needs to care about it in the zone file.
However, this requires setting journal-content to all so that
the information about the last real SOA serial is preserved in case of server re-start.
The sizing of journal limits needs to be taken into consideration
(see Journal behaviour).
Note
This mode is not suitable if the zone can be modified externally (e.g. DDNS, knotc).
Example 5
Managing specific record types externally:
zonefile-sync: -1
zonefile-load: difference-no-serial
journal-content: all
zonefile-skip: [ "DS" ]
Most of the zone contents are still managed by editing the input-only zone file,
but some specific resource record types (e.g. DS) are managed externally
(e.g. via DDNS or knotc). The zonefile-skip option
ensures that both approaches do not interfere and that additions of these records
are not reverted when reloading the zone file that does not contain them.
Zone database
The zone database can be accessed directly through
the database CLI or API, without any interaction with a Knot DNS server. The
knot
module provides specific commands for zone data manipulation.
The KNOT.ZONE.*
commands are used for manipulating entire zones:
KNOT.ZONE.BEGIN <zone> <instance>
— Initializes a zone transaction for the
specified instance. Returns a transaction ID, which is then used as a parameter
for subsequent commands.
KNOT.ZONE.STORE <zone> <txn> "data"
— Stores zone data in a zone transaction.
The data must be provided in the form of a textual zone file and may contain
one or more records (including a full zone file content).
KNOT.ZONE.COMMIT <txn>
— Commits the specified zone transaction.
KNOT.ZONE.ABORT <txn>
— Aborts the specified zone transaction.
KNOT.ZONE.LOAD [--compact] <zone> <instance|txn> [owner] [type]
— Returns
the contents of the specified zone instance, optionally in compact format and
filtered by owner and record type.
KNOT.ZONE.PURGE <zone>
— Purges all data for the specified zone.
KNOT.ZONE.LIST [--instances]
— Lists stored zones, optionally including
available instances.
Example of a zone initialization:
$ redis-cli KNOT.ZONE.BEGIN example.com 1
(integer) 10
$ redis-cli KNOT.ZONE.STORE example.com 10 "@ SOA ns admin 1 86400 900 691200 3600"
OK
$ redis-cli KNOT.ZONE.STORE example.com 10 "@ NS ns"
OK
$ redis-cli KNOT.ZONE.STORE example.com 10 "ns 300 AAAA ::1"
OK
$ redis-cli KNOT.ZONE.LOAD example.com 10
1) 1) "example.com."
2) "600"
3) "SOA"
4) "ns.example.com. admin.example.com. 1 86400 900 691200 3600"
2) 1) "example.com."
2) "600"
3) "NS"
4) "ns.example.com."
3) 1) "ns.example.com."
2) "300"
3) "AAAA"
4) "::1"
$ redis-cli KNOT.ZONE.COMMIT example.com 10
OK
$ redis-cli KNOT.ZONE.LOAD --compact example.com 1
1) "example.com. 600 SOA ns.example.com. admin.example.com. 1 86400 900 691200 3600"
2) "example.com. 600 NS ns.example.com."
3) "ns.example.com. 300 AAAA ::1"
$ redis-cli KNOT.ZONE.LIST --instances
1) "example.com.: 1"
Tip
For storing the entire zone file, the following command works:
$ cat example.com.zone | redis-cli --raw -x KNOT.ZONE.STORE example.com 10
In the case of very large zone files, ensure that the database limits
proto-max-bulk-len
and client-query-buffer-limit
are set high enough.
The KNOT.UPD.*
commands are used to manipulate zone updates:
KNOT.UPD.BEGIN <zone> <instance>
— Initializes a zone update transaction
for the specified instance. Returns a transaction ID, which is then used as
a parameter for subsequent commands.
KNOT.UPD.ADD <zone> <txn> "data"
— Adds zone data to a zone update transaction.
The data must be provided in the form of a textual zone file and may contain
one or more records (including a full zone file content).
KNOT.UPD.REMOVE <zone> <txn> "data"
— Removes zone data from a zone
update transaction. The data must be provided in the form of a textual zone
file and may contain one or more records (including a full zone file content).
KNOT.UPD.DIFF [--compact] <zone> <txn> [owner] [type]
— Returns the contents
of the specified zone update transaction, optionally in compact format and
filtered by owner and record type.
KNOT.UPD.COMMIT <zone> <txn>
— Commits the specified zone update transaction.
If the SOA serial is not incremented explicitly, it will be incremented automatically.
KNOT.UPD.ABORT <zone> <txn>
— Aborts the specified zone update transaction.
KNOT.UPD.LOAD [--compact] <zone> <instance> <serial>
— Returns the contents
of the zone updates since the specified SOA serial, optionally in compact
format and filtered by owner and record type.
Example of a zone update:
$ redis-cli KNOT.UPD.BEGIN example.com 1
(integer) 10
$ redis-cli KNOT.UPD.ADD example.com 10 "test TXT \"Knot DNS\""
OK
$ redis-cli KNOT.UPD.DIFF example.com 10
1) 1) (empty array)
2) 1) 1) "test.example.com."
2) "600"
3) "TXT"
4) "\"Knot DNS\""
$ redis-cli KNOT.UPD.COMMIT example.com 10
OK
$ redis-cli KNOT.UPD.LOAD --compact example.com 1 1
1) 1) 1) 1) "example.com. 600 SOA ns.example.com. admin.example.com. 1 86400 900 691200 3600"
2) 1) "example.com. 600 SOA ns.example.com. admin.example.com. 2 86400 900 691200 3600"
2) 1) (empty array)
2) 1) "test.example.com. 600 TXT \"Knot DNS\""
Warning
Do not modify the zone data using native commands such as SET or ZADD,
as this may cause data corruption!
Multi-primary
In some setups, it is desirable for a secondary server to have multiple primaries configured.
An example is DNSSEC multi-signer or any other kind of fail-proof redundancy.
If the zone contents may differ among the primaries, it is necessary to avoid interchanged
IXFR zone transfers at the secondary. Applying mismatched incremental changes to different
zone versions can create inconsistencies that may be difficult to detect.
Knot DNS provides the following options, which can also be combined.
Master pinning
The master-pin-tolerance option, configured on the secondary server,
ensures that the same primary is used unless it becomes unresponsive or remains
outdated for a configured time period. Additionally, if a fallback to a different
primary occurs, it enforces a full zone transfer (AXFR).
Serial modulo
The serial-modulo option, configured on the primary servers, prevents
multiple primaries from using the same zone serial numbers. This ensures that even
secondaries without the master pinning support will use AXFR.
Serial shift
Another use case for this configuration option is mostly useful with the
unixtime
serial-policy. It specifies that one primary is slightly
"ahead" or "behind" another in zone serial numbers. This naturally enforces master
pinning, even for secondaries that do not support it.
Disabling IXFR
As a last resort, IXFR can be disabled on the primaries using provide-ixfr,
with clear performance drawbacks.
Zone bootstrapping on secondary
When zone refresh from the primary fails, the retry
value from SOA is used
as the interval between refresh attempts. In a case that SOA isn't known to the
secondary (either because the zone hasn't been retrieved from the primary yet,
or the zone has expired), a backoff is used for repeated retry attempts.
With every retry, the delay rises as a quadratic polynomial (5 * n^2, where n
represents the sequence number of the retry attempt) up to two hours, each time
with a random delay of 0 to 30 seconds added to spread the load on the primary.
In each attempt, the retry interval is subject to retry-min-interval
and retry-max-interval.
Until the refresh has been successfully completed, the backoff is restarted from
the beginning by every zone-refresh
or zone-retransfer
of the zone
triggered manually via knotc, by zone-purge
or
zone-restore
of the zone's timers, or by a restart of knotd.
Zone expiration
On a primary, zone normally never expires. On a secondary, zone expiration results
in removal of the current zone contents and a trigger of immediate zone refresh.
The zone file and zone's journal are kept, but not used for answering requests
until the refresh is successfully completed.
The zone expire timer is set according to the zone's SOA expire field. In addition
to it, Knot DNS also supports EDNS EXPIRE extension of the expire timer in both
primary and secondary roles as described in RFC 7314.
When Knot DNS is configured as a secondary, EDNS EXPIRE option present in a SOA,
IXFR, or AFXR response from the primary is processed and used to update the zone
timer when necessary. This functionality (together with requests of any other EDNS
options) for a specified primary may be disabled using the no-edns
configuration parameter.
If it's necessary, any zone may be expired manually using the zone-purge
command of the knotc utility. Manual expiration is applicable
to any zone, including a catalog zone or a zone on a primary. Beware, a manually
expired zone on a primary or a manually expired catalog zone becomes valid again
after a server configuration is reloaded or the knotd process
is restarted, provided that the zone data hasn't been removed.
DNSSEC key states
During its lifetime, a DNSSEC key finds itself in different states. Most of the time it
is used for signing the zone and published in the zone. In order to exchange
the key, one type of a key rollover is necessary, and during this rollover,
the key goes through various states with respect to the rollover type and also the
state of the other key being rolled-over.
First, let's list the states of the key being rolled-in.
Standard states:
active
— The key is used for signing.
published
— The key is published in the zone, but not used for signing. If the key is
a KSK or CSK, it is used for signing the DNSKEY RRSet.
ready
(only for KSK) — The key is published in the zone and used for signing. The
old key is still active, since we are waiting for the DS records in the parent zone to be
updated (i.e. "KSK submission").
Special states for algorithm rollover:
pre-active
— The key is not yet published in the zone, but it's used for signing the zone.
published
— The key is published in the zone, and it's still used for signing since the
pre-active state.
Second, we list the states of the key being rolled-out.
Standard states:
retire-active
— The key is still used for signing, and is published in the zone, waiting for
the updated DS records in parent zone to be acked by resolvers (KSK case) or synchronizing
with KSK during algorithm rollover (ZSK case).
retired
— The key is no longer used for signing. If ZSK, the key is still published in the zone.
removed
— The key is not used in any way (in most cases such keys are deleted immediately).
Special states for algorithm rollover:
Special states for RFC 5011 trust anchor roll-over
Note
Trust anchor roll-over is not implemented with automatic key management.
The revoke
state can only be established using keymgr when using
Manual key management.
The states listed above are relevant for keymgr operations like generating
a key, setting its timers and listing KASP database.
Note that the key "states" displayed in the server log lines while zone signing
are not according to those listed above, but just a hint as to what the key is currently used for
(e.g. "public, active" = key is published in the zone and used for signing).
DNSSEC key rollovers
This section describes the process of DNSSEC key rollover and its implementation
in Knot DNS, and how the operator might watch and check that it's working correctly.
The prerequisite is automatic zone signing with enabled
automatic key management.
The KSK and ZSK rollovers are triggered by the respective zone key getting old according
to the settings (see KSK and ZSK lifetimes).
The algorithm rollover starts when the policy algorithm
field is updated to a different value.
The signing scheme rollover happens when the policy signing scheme
field is changed.
It's also possible to change the algorithm and signing scheme in one rollover.
The operator may check the next rollover phase time by watching the next zone signing time,
either in the log or via knotc zone-status
. There is no special log for finishing a rollover.
Note
There are never two key rollovers running in parallel for one zone. If
a rollover is triggered while another is in progress, it waits until the
first one is finished. Note that a rollover might be considered finished
when the old key is retired or waiting to be deleted.
The ZSK rollover is performed with Pre-publish method, KSK rollover uses Double-Signature
scheme, as described in RFC 6781.
Automatic KSK and ZSK rollovers example
Let's start with the following set of keys:
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, key, tag 53594, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, key, tag 36185, algorithm ECDSAP256SHA256, public, active
The last fields hint the key state: public
denotes a key that will be presented
as the DNSKEY record, ready
means that CDS/CDNSKEY records were created,
active
tells us that the key is used for signing, while active+
is an
active key undergoing a roll-over or roll-in.
For demonstration purposes, the following configuration is used:
submission:
- id: test_submission
check-interval: 2s
parent: dnssec_validating_resolver
policy:
- id: test_policy
ksk-lifetime: 5m
zsk-lifetime: 2m
propagation-delay: 2s
dnskey-ttl: 10s
zone-max-ttl: 15s
ksk-submission: test_submission
Upon the zone's KSK lifetime expiration, a new KSK is generated and the rollover
continues along the lines of RFC 6781 Section 4.1.2:
# KSK Rollover (53594 -> 3375)
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, KSK rollover started
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, next key action, KSK tag 3375, submit at 2024-02-14T15:20:12+0100
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, key, tag 53594, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, key, tag 36185, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active+
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111204, new RRSIGs 3
2024-02-14T15:20:00+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:20:12+0100
... (propagation-delay + dnskey-ttl) ...
2024-02-14T15:20:12+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:20:12+0100 notice: [example.com.] DNSSEC, KSK submission, waiting for confirmation
2024-02-14T15:20:12+0100 info: [example.com.] DNSSEC, key, tag 53594, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:20:12+0100 info: [example.com.] DNSSEC, key, tag 36185, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:20:12+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, ready, active+
2024-02-14T15:20:12+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:20:12+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111205, new RRSIGs 6
2024-02-14T15:20:12+0100 info: [example.com.] DNSSEC, next signing at 2024-02-28T15:19:37+0100
At this point the new KSK has to be submitted to the parent zone. Knot detects the updated parent's DS
record automatically (and waits for additional period of the DS's TTL before retiring the old key)
if parent DS check is configured, otherwise the
operator must confirm it manually (using knotc zone-ksk-submitted
)
Note
A DS record for the new KSK can be generated using:
$ keymgr example.com ds 3375
2024-02-14T15:20:12+0100 info: [example.com.] DS check, outgoing, remote 127.0.0.1@5300 TCP, KSK submission check: negative
2024-02-14T15:20:14+0100 info: [example.com.] DS check, outgoing, remote 127.0.0.1@5300 TCP/pool, KSK submission check: negative
2024-02-14T15:20:16+0100 info: [example.com.] DS check, outgoing, remote 127.0.0.1@5300 TCP/pool, KSK submission check: positive
2024-02-14T15:20:16+0100 notice: [example.com.] DNSSEC, KSK submission, confirmed
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, next key action, KSK tag 53594, remove at 2024-02-14T15:20:23+0100
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, key, tag 53594, algorithm ECDSAP256SHA256, KSK, public, active+
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, key, tag 36185, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111206, new RRSIGs 2
2024-02-14T15:20:16+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:20:23+0100
... (parent's DS TTL is 7 seconds) ...
2024-02-14T15:20:23+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:20:23+0100 info: [example.com.] DNSSEC, next key action, ZSK, generate at 2024-02-14T15:21:54+0100
2024-02-14T15:20:23+0100 info: [example.com.] DNSSEC, key, tag 36185, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:20:23+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:20:23+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:20:23+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111207, new RRSIGs 2
2024-02-14T15:20:23+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:21:54+0100
Upon the zone's ZSK lifetime expiration, a new ZSK is generated and the rollover
continues along the lines of RFC 6781 Section 4.1.1:
# ZSK Rollover (36185 -> 38559)
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, ZSK rollover started
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, next key action, ZSK tag 38559, replace at 2024-02-14T15:22:06+0100
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, key, tag 36185, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, key, tag 38559, algorithm ECDSAP256SHA256, public
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111208, new RRSIGs 2
2024-02-14T15:21:54+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:22:06+0100
... (propagation-delay + dnskey-ttl) ...
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, next key action, ZSK tag 36185, remove at 2024-02-14T15:22:23+0100
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, key, tag 36185, algorithm ECDSAP256SHA256, public
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, key, tag 38559, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111209, new RRSIGs 14
2024-02-14T15:22:06+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:22:23+0100
... (propagation-delay + zone-max-ttl) ...
2024-02-14T15:22:23+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:22:23+0100 info: [example.com.] DNSSEC, next key action, ZSK, generate at 2024-02-14T15:24:06+0100
2024-02-14T15:22:23+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:22:23+0100 info: [example.com.] DNSSEC, key, tag 38559, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:22:23+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:22:23+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111210, new RRSIGs 2
2024-02-14T15:22:23+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:24:06+0100
Further rollovers:
... (zsk-lifetime - propagation-delay - zone-max-ttl) ...
# Another ZSK Rollover (38559 -> 59825)
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, ZSK rollover started
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, next key action, ZSK tag 59825, replace at 2024-02-14T15:24:18+0100
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, key, tag 38559, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, key, tag 59825, algorithm ECDSAP256SHA256, public
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111211, new RRSIGs 2
2024-02-14T15:24:06+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:24:18+0100
...
# Another KSK Rollover (3375 -> 50822)
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, signing zone
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, KSK rollover started
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, next key action, KSK tag 50822, submit at 2024-02-14T15:25:12+0100
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, key, tag 3375, algorithm ECDSAP256SHA256, KSK, public, active
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, key, tag 59825, algorithm ECDSAP256SHA256, public, active
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, key, tag 50822, algorithm ECDSAP256SHA256, KSK, public, active+
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, signing started
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, successfully signed, serial 2010111214, new RRSIGs 3
2024-02-14T15:25:00+0100 info: [example.com.] DNSSEC, next signing at 2024-02-14T15:25:12+0100
...
Tip
If systemd is available, the KSK submission event is logged into journald
in a structured way. The intended use case is to trigger a user-created script.
Example:
journalctl -f -t knotd -o json | python3 -c '
import json, sys
for line in sys.stdin:
k = json.loads(line);
if "KEY_SUBMISSION" in k:
print("%s, zone=%s, keytag=%s" % (k["__REALTIME_TIMESTAMP"], k["ZONE"], k["KEY_SUBMISSION"]))
'
Alternatively, the D-Bus signaling can be utilized for the same use.
DNSSEC shared KSK
Knot DNS allows, with automatic DNSSEC key management, to configure a shared KSK for multiple zones.
By enabling ksk-shared, we tell Knot to share all newly-created KSKs
among all the zones with the same DNSSEC signing policy assigned.
The feature works as follows. Each zone still manages its keys separately. If a new KSK shall be
generated for the zone, it first checks if it can grab another zone's shared KSK instead -
that is the last generated KSK in any of the zones with the same policy assigned.
Anyway, only the cryptographic material is shared, the key may have different timers
in each zone.
Consequences:
If we have an initial setting with brand new zones without any DNSSEC keys,
the initial keys for all zones are generated. With shared KSK, they will all have the same KSK,
but different ZSKs. The KSK rollovers may take place at slightly different times for each of the zones,
but the resulting new KSK will be shared again among all of them.
If we have zones which already have their keys, turning on the shared KSK feature triggers no action.
But when a KSK rollover takes place, they will use the same new key afterwards.
Warning
Changing the policy id must be done carefully if shared
KSK is in use.
DNSSEC delete algorithm
This is how to "disconnect" a signed zone from a DNSSEC-aware parent zone.
More precisely, we tell the parent zone to remove our zone's DS record by
publishing a special formatted CDNSKEY and CDS record. This is mostly useful
if we want to turn off DNSSEC on our zone so it becomes insecure, but not bogus.
With automatic DNSSEC signing and key management by Knot, this is as easy as
configuring cds-cdnskey-publish option and reloading the configuration.
We check if the special CDNSKEY and CDS records with the rdata "0 3 0 AA==" and "0 0 0 00",
respectively, appeared in the zone.
After the parent zone notices and reflects the change, we wait for TTL expire
(so all resolvers' caches get updated), and finally we may do anything with the
zone, e.g. turning off DNSSEC, removing all the keys and signatures as desired.
DNSSEC Offline KSK
Knot DNS allows a special mode of operation where the private part of the Key Signing Key is
not available to the daemon, but it is rather stored securely in an offline storage. This requires
that the KSK/ZSK signing scheme is used (i.e. single-type-signing is off).
The Zone Signing Key is always fully available to the daemon in order to sign common changes to the zone contents.
The server (or the "ZSK side") only uses ZSK to sign zone contents and its changes. Before
performing a ZSK rollover, the DNSKEY records will be pre-generated and signed by the
signer (the "KSK side"). Both sides exchange keys in the form of human-readable messages with the help
of the keymgr utility.
Prerequisites
For the ZSK side (i.e. the operator of the DNS server), the zone has to be configured with:
For the KSK side (i.e. the operator of the KSK signer), the zone has to be configured with:
Generating and signing future ZSKs
Use the keymgr pregenerate
command on the ZSK side to prepare the ZSKs for a specified period of time in the future. The following example
generates ZSKs for the example.com zone for 6 months ahead starting from now:
$ keymgr -c /path/to/ZSK/side.conf example.com. pregenerate +6mo
If the time period is selected as e.g. 2 x zsk-lifetime + 4 x propagation-delay, it will
prepare roughly two complete future key rollovers. The newly-generated
ZSKs remain in non-published state until their rollover starts, i.e. the time
they would be generated in case of automatic key management.
Use the keymgr generate-ksr
command on the ZSK side to export the public parts of the future ZSKs in a form
similar to DNSKEY records. You might use the same time period as in the first step:
$ keymgr -c /path/to/ZSK/side.conf example.com. generate-ksr +0 +6mo > /path/to/ksr/file
Save the output of the command (called the Key Signing Request or KSR) to a file and transfer it to the KSK side e.g. via e-mail.
Use the keymgr sign-ksr
command on the KSK side with the KSR file from the previous step as a parameter:
$ keymgr -c /path/to/KSK/side.conf example.com. sign-ksr /path/to/ksr/file > /path/to/skr/file
This creates all the future forms of the DNSKEY, CDNSKEY and CSK records and all the respective RRSIGs and prints them on output. Save
the output of the command (called the Signed Key Response or SKR) to a file and transfer it back to the ZSK side.
Use the keymgr import-skr
command to import the records and signatures from the SKR file generated in the last step
into the KASP DB on the ZSK side:
$ keymgr -c /path/to/ZSK/side.conf example.com. import-skr /path/to/skr/file
Use the knotc zone-keys-load
command to trigger a zone re-sign on the ZSK-side and set up the future re-signing events correctly.:
$ knotc -c /path/to/ZSK/side.conf zone-keys-load example.com.
Now the future ZSKs and DNSKEY records with signatures are ready in KASP DB for later usage.
Knot automatically uses them at the correct time intervals.
The entire procedure must be repeated before the time period selected at the beginning passes,
or whenever a configuration is changed significantly. Importing new SKR over some previously-imported
one leads to deleting the old offline records.
Offline KSK and manual ZSK management
If the automatically preplanned ZSK roll-overs (first step) are not desired, just set the zsk-lifetime
to zero, and manually pregenerate ZSK keys and set their timers. Then follow the steps
generate-ksr — sign-ksr — import-skr — zone-keys-load
and repeat the ceremony when necessary.
Offline KSK roll-over
The KSKs (on the KSK side) must be managed manually, but manual KSK roll-over is possible. Just plan the steps
of the KSK roll-over in advance, and whenever the KSK set or timers are changed, re-perform the relevant rest of the ceremony
sign-ksr — import-skr — zone-keys-load
.
Emergency SKR
A general recommendation for large deployments is to have some backup pre-published keys, so that if the current ones are
compromised, they can be rolled-over to the backup ones without any delay. But in the case of Offline KSK, according to
the procedures above, both ZSK and KSK immediate rollovers require the KSR-SKR ceremony.
However, a trick can be done to achieve really immediate key substitution. This is no longer about Knot DNS functionality,
just a hint for the operator.
The idea is to perform every KSR-SKR ceremony twice: once with normal state of the keys (the backup key is only published),
and once with the keys already exchanged (the backup key is temporarily marked as active and the standard key temporarily
as public only). The second (backup) SKR should be saved for emergency key replacement.
Summary of the steps:
Prepare KSK and ZSK side as usual, including public-only emergency key
Perform normal Offline KSK ceremony:
Freeze the zone on the ZSK side
Temporarily set the backup key as active and the normal key as publish-only
Perform backup Offline KSK ceremony:
Generate KSR (only if the backup key is a replacement for ZSK)
Sign the KSR on the KSK side
Save the SKR to a backup storage, don't import it yet
Return the keys to the previous state
Thaw the zone on the ZSK side
Emergency key replacement:
DNSSEC multi-signer
Multi-signer is a general term that refers to any mode of operation in which
a DNS zone is signed by multiple servers (usually two) in parallel.
Knot DNS offers various multi-signer modes, which are recommended for redundancy
within an organization. For multi-signer operations involving multiple
"DNSSEC providers" and the ability to switch between them, you can also refer to
MUSIC.
Regardless of the chosen mode from the following options, any secondary that has multiple signers
configured as primaries must prevent interchanged IXFR from them. See
the relevant Multi-primary capter on how to achieve it.
In order to prevent keytag conflicts, it is recommended that the keytags of keys
generated by each signer are from distinct subset of possible values. With Knot DNS, this
can be achieved using keytag-modulo option (e.g. for three signers, setting
0/3
on the first one, 1/3
on the second, and 2/3
on the third of them).
Sharing private keys, manual policy
When DNSSEC keys are shared among zone signing servers (signers), one challenge
is automatic key management (roll-overs) and synchronization among the signers.
In this example mode of operation, it is expected that key management is maintained
outside of Knot, and the generated keys, including private keys and metadata
(timers), are available in Bind9 format.
Every new key is then imported into each Knot using the keymgr
import-bind
command, after which knotc zone-keys-load
is invoked. With manual policy configured, the signers simply
follow prescribed key timers, maintaining the same key set at each signer.
For more useful commands like list
, set
, and delete
, refer
to keymgr.
Sharing private keys, automatic policy
Knot handles automatic key management very well, but enabling it on multiple
instances would lead to redundant key generation. However, it's possible to enable it on one signer
and keep synchronizing the keys to all others. The managing signer shall be configured with
automatic ZSK/KSK management, all others
with manual policy.
The key set changes on the managing signer can be monitored by periodic queries
with keymgr list
, or by listening to
D-Bus interface and watching for the keys_updated
event.
Whenever the key set is changed, key synchronization can be safely performed with
Data and metadata backup feature. Dump the KASP
database on the managing signer with knotc zone-backup +kaspdb
,
transfer the backup directory to each other signer, and import the keys by
knotc zone-restore +kaspdb
, followed by zone-keys-load
on them.
This way, the full key set, including private keys and all metadata, is always
synchronized between signers. The method of transporting the backup directory
is beyond the scope of Knot and this documentation. An eventual loss of the managing
signer results in the automatic key management being halted, but DNSSEC signing continues
to function. The synchronization delay for keys between the managing signer and other
signers must be accounted for in propagation-delay.
Distinct keys, DNSKEY record synchronization
When the DNSSEC keys are not shared among signers, each server can manage its own keys separately.
However, the DNSKEY (including CDNSKEY and CDS) records (with public keys) must be synchronized
for full validity of the signed zone. Dynamic updates can be used to achieve this sharing.
The following configuration options should be used:
Set dnskey-management to incremental
on each signer to ensure
it retains the other's DNSKEY records in the zone during signing.
Set delete-delay to a reasonable time interval, which ensures that
all signers get synchronized during this period.
Set cds-cdnskey-publish to either none
or always
, otherwise
the parent DS record might configure itself to point only to one signer's KSK.
Configure dnskey-sync to all other signers so that this signer's
public keys appear in each other's DNSKEY (also applies to CDNSKEY and CDS) RRSets.
Configure Access control list (ACL) so that DDNS from all other signers is allowed.
Set ddns-master to empty value ("") so that DDNS from other signers
is not forwarded to the primary master if any.
Additionally, the synchronization delay between all signers must be accounted
for in propagation-delay.
With careful configuration, all signers automatically synchronize their DNSKEY (and eventually
CDNSKEY and CDS) RRSets, keeping them synchronized during roll-overs. Nevertheless,
it is recommended to monitor their logs.
An illustrative example of the second of three signers:
remote:
- id: signer1
address: 10.20.30.1
- id: signer3
address: 10.20.30.3
acl:
- id: signers
remote: [ signer1, signer3 ]
action: [ query, update ]
# TODO configure TSIGs!
dnskey-sync:
- id: sync
remote: [ signer3, signer1 ]
policy:
- id: multisigner
single-type-signing: on
ksk-lifetime: 60d
ksk-submission: ... # TODO see Automatic KSK management
propagation-delay: 14h
delete-delay: 2h
cds-cdnskey-publish: always
dnskey-management: incremental
dnskey-sync: sync
zone:
- domain: example.com.
# TODO configure zonefile and journal
# TODO configure transfers in/out: master, NOTIFY, ACLs...
dnssec-signing: on
dnssec-policy: multisigner
ddns-master: ""
serial-modulo: 1/3
acl: signers
Distinct keys, DNSKEY at common unsigned primary
The same approach and configuration can be used, with the difference that the signers
do not send updated DNSKEYs (along with CDNSKEYs and CDSs) to each other. Instead, they
send the updates to their common primary, which holds the unsigned version of zone.
The only configuration change involves redirecting
dnskey-sync to the common primary and adjusting its ACL to allow DDNS
from the signers.
It is also necessary to configure ixfr-benevolent on each signer so that
they accept incremental zone transfers from the primary with additions (or removals)
of their own's DNSKEYs.
This setup should work nicely with any number of signers, however, due to the size
of DNSKEY RRSet, at most three are advisable.
DNSSEC keys import to HSM
Knot DNS stores DNSSEC keys in textual PEM format (RFC 7468),
while many HSM management software require the keys for import to be in binary
DER format (Rec. ITU-T X.690).
Keys can be converted from one format to another by software tools such as
certtool
from GnuTLS suite or
openssl
from OpenSSL suite.
In the examples below, c4eae5dea3ee8c15395680085c515f2ad41941b6
is used as the key ID,
c4eae5dea3ee8c15395680085c515f2ad41941b6.pem
represents the filename of the key in PEM format
as copied from the Knot DNS zone's KASP database directory,
c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der
represents the file containing the private
key in DER format as generated by the conversion tool, and
c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der
represents the file containing the public
key in DER format as generated by the conversion tool.
$ certtool -V -k --outder --infile c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
--outfile c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der
$ certtool -V --pubkey-info --outder --load-privkey c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
--outfile c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der
As an alternative, openssl
can be used instead. It is necessary to specify either rsa
or ec
command according to the algorithm used by the key.
$ openssl rsa -outform DER -in c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
-out c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der
$ openssl rsa -outform DER -in c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
-out c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der -pubout
Actual import of keys (both public and private keys from the same key pair) to an HSM can be done
via PKCS #11 interface, by pkcs11-tool
from OpenSC toolkit
for example. In the example below, /usr/local/lib/pkcs11.so
is used as a name of the PKCS #11 library
or module used for communication with the HSM.
$ pkcs11-tool --module /usr/local/lib/pkcs11.so --login \
--write-object c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der --type privkey \
--usage-sign --id c4eae5dea3ee8c15395680085c515f2ad41941b6
$ pkcs11-tool --module /usr/local/lib/pkcs11.so -login \
--write-object c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der --type pubkey \
--usage-sign --id c4eae5dea3ee8c15395680085c515f2ad41941b6
DNSSEC multiple keystores
Private keys for signing a single zone can be stored in more than one keystore
with two main use-cases:
Keystore migration
Some keystores (HSMs) do not allow exporting private key material, so it is not
possible to migrate away from them by copying the private keys to a new keystore
(another HSM or a directory with PEM files). In such cases, the migration must
be performed by key rollover(s), where new keys are generated in the target
keystore, while the old keys remain in the original one and are eventually
retired and removed.
During the transition period (i.e. rollovers), both keystores must be
configured simultaneously. This is achieved by configuring them both in the
keystore section and referencing them in the keystore
option in policy section. The order matters: new keys are generated
in the first referenced keystore, so in this case, the target keystore must be
listed first.
KSK more secured than ZSK
When desirable, the Key Signing Key (KSK) can be stored in an HSM, while the Zone
Signing Key (ZSK) is kept in a simple PEM file. This enhances the security of
the KSK while preserving zone signing performance (as HSM signing operations are
usually much slower).
To achieve this, both keystores must be configured in the
keystore section, while the PKCS#11 keystore (the HSM) has the
ksk-only option enabled and being listed first in the
keystore option in policy section. With this configuration,
new KSKs are generated in the HSM, while ZSKs are generated in the second
keystore.
Daemon controls
Knot DNS was designed to allow server reconfiguration on-the-fly
without interrupting its operation. Thus it is possible to change
both configuration and zone files and also add or remove zones without
restarting the server. This can be done with:
If you want to refresh the secondary zones, you can do this with:
Logging
Knot DNS supports logging to syslog or systemd-journald
facility, to a specified file, to standard output, or to standard error output.
Several different logging targets may be used in parallel.
If syslog or systemd-journald is used for logging, log rotation is handled
by that logging facility. When logging to a specified file, log rotation should
be done by moving the current log file followed by reopening of the log file with
either knotc -b reload
or by sending SIGHUP
to the knotd
process (see the
pidfile).
External validation
This feature enables automatic validation of every zone update using third-party
zone-checking tools, as well as the enforcement of user-defined policies on zone
contents and updates (for example, ensuring that an update does not modify too
many records of specific RRtypes).
External validation can be combined with, but does not depend on,
dnssec-validation and zonemd-verify. It behaves similarly
in that, if validation fails, the update is discarded and the server continues
responding (including outgoing zone transfers) from the last known good version
of the zone.
Specifically, once external-validation is configured, any time
a zone is being modified, the update process is paused just before
publishing the new version. At this point, it waits for verification and
either confirmation or rejection. The user or script can retrieve the status
via knotc zone-status
or will be notified via dbus-event.
A response should be issued by calling knotc zone-commit
to approve the update or
knotc zone-abort
to reject it. As an alternative to using knotc,
the command can be sent directly to the server's control socket.
Note that the background worker processing the update is blocked
during the external validation and is not able to handle updates to
other zones at that time. Therefore, it is recommended to finish the validation
as fast as possible and/or configure a sufficient number of
background-workers relative to the number of zones.
Additional options can be configured in the external section. This
includes settings to dump the full new zone and/or the zone differences to
specified files in textual (zone file) format before external validation begins.
Also note that external validation may be rejected automatically
(without an explicit call to knotc zone-abort
) due to timeout.
This mechanism prevents the server from becoming locked.
See a simple demonstration script.
showing how to implement external validation using the combined D-Bus and knotc
interface.
Statistics
The server provides some general statistics and optional query module statistics
(see mod-stats).
Server statistics or global module statistics can be shown by:
$ knotc stats
$ knotc stats server # Show all server counters
$ knotc stats mod-stats # Show all mod-stats counters
$ knotc stats server.zone-count # Show specific server counter
Per zone statistics can be shown by:
$ knotc zone-stats example.com. # Show all zone counters
$ knotc zone-stats example.com. mod-stats # Show all zone mod-stats counters
$ knotc zone-stats example.com. mod-stats.query-type # Show specific zone counter
$ knotc zone-stats -- # Show all zone counters for all zones
$ knotc zone-stats -- mod-stats.request-protocol # Show specific zone counter for all zones
To show all supported counters even with 0 value, use the force option.
A simple periodic statistic dump to a YAML file can also be enabled. See
statistics section for the configuration details.
As the statistics data can be accessed over the server control socket,
it is possible to create an arbitrary script (Python is supported at the moment)
which could, for example, publish the data in JSON format via HTTP(S)
or upload the data to a more efficient time series database. Take a look into
the python folder of the project for these scripts.
Mode XDP
Thanks to recent Linux kernel capabilities, namely eXpress Data Path and AF_XDP
address family, Knot DNS offers a high-performance packet processing mode for
UDP, TCP, and QUIC protocols. The basic idea is to filter DNS messages close to
the network device and effectively forward them to the nameserver without touching
the network stack of the operating system. Messages in protocols, for which XDP
hasn't been enbled, are processed as usual.
If listen is configured, the server creates additional XDP workers,
listening on specified interface(s) and port(s) for DNS queries. Each XDP worker
handles one RX and TX network queue pair.
Pre-requisites
Linux kernel 4.18+ (5.x+ is recommended for optimal performance) compiled with
the CONFIG_XDP_SOCKETS=y option. The XDP mode isn't supported in other operating systems.
A multiqueue network card, which offers enough Combined RX/TX channels, with
native XDP support is highly recommended. Successfully tested cards:
NVIDIA (Mellanox) ConnectX-6 Dx (driver mlx5_core), the maximum number of channels
per interface is 127.
Intel series E810 (driver ice), the maximum number of channels per interface must
be limited to 127 or fewer. Linux kernel 6.10.4 or later, along with its driver,
is required for stability.
Intel series 700 (driver i40e), the maximum number of channels per interface is 64.
Linux kernel drivers are recommended.
Cards with known instability issues:
If the knotd service is not directly executed in the privileged mode, some
additional Linux capabilities have to be set:
Execute command:
And insert these lines:
[Service]
CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN CAP_IPC_LOCK CAP_SYS_RESOURCE
AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN CAP_IPC_LOCK CAP_SYS_RESOURCE
The CAP_SYS_RESOURCE is needed on Linux < 5.11.
All the capabilities are dropped upon the service is started.
For proper processing of VLAN traffic, VLAN offloading should be disabled. E.g.:
ethtool -K <interface> tx-vlan-offload off rx-vlan-offload off
Optimizations
Some helpful commands:
ethtool -N <interface> rx-flow-hash udp4 sdfn
ethtool -N <interface> rx-flow-hash udp6 sdfn
ethtool -L <interface> combined <?>
ethtool -G <interface> rx <?> tx <?>
renice -n 19 -p $(pgrep '^ksoftirqd/[0-9]*$')
Limitations
Request and its response must go through the same physical network device.
Dynamic DNS over XDP is not supported.
MTU higher than 1790 bytes is not supported.
Multiple BPF filters per one network device are not supported.
Systems with big-endian byte ordering require special recompilation of the nameserver.
IPv4 header and UDP checksums are not verified on received DNS messages.
DNS over XDP traffic is not visible to common system tools (e.g. firewall, tcpdump etc.).
BPF filter is not automatically unloaded from the network device. Manual filter unload:
ip link set dev <interface> xdp off
Comments¶
A comment begins with a
#
character and is ignored during processing. Also each configuration section or sequence block allows a permanent comment using thecomment
item which is stored in the server beside the configuration.