Knot DNS automatic DNSSEC signing, the secure way
Turning on DNSSEC in Knot is one line, which is exactly why so many zones are signed, look green, and are not validated by anyone. The keys exist, the signatures exist, and the parent has never heard of either.
The short answer
Assign a Knot policy with automatic key management: ECDSA P-256, NSEC3 with zero iterations and no salt, explicit TTLs, and a signature refresh window of days, not hours. Keep the KASP database readable only by knotd, keep the zone file unsigned, publish CDS, put the DS at the parent, then confirm the submission.
On this page
What goes wrong
DNSSEC protects a zone only when a resolver can build a chain of trust from the root to your records. Signing is half of that chain. The other half is a DS record in the parent zone, which only your registrar can publish.
Knot makes the signing half easy. dnssec-signing: on generates keys, signs
every record and keeps the signatures fresh. Many operators stop there. The
zone serves RRSIG records, a quick dig +dnssec looks right, and no resolver
on the internet validates any of it, because there is no DS at the parent.
The zone is still open to spoofed answers.
The second failure is quieter. The KASP database holds the private keys. If it is world-readable, sits on a shared volume or is copied into a backup that many people can read, anyone with a copy can sign records for your zone.
The third failure is the default denial of existence. Plain NSEC lets anyone list every name in the zone. Many guides then enable NSEC3 with a salt and many iterations, which RFC 9276 now forbids because it costs resolvers CPU and gives no real protection.
What the docs say
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.
Source: Knot DNS 3.5 docs, Automatic DNSSEC signing
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.
Source: Knot DNS 3.5 docs, Automatic KSK management
If NSEC3 must be used, then an iterations count of 0 MUST be used to alleviate computational burdens.
Source: RFC 9276, Section 3.1
The Knot docs describe the signing side in detail. They do not tell you that
nothing is secure until a DS record exists at the registrar, and the "ready"
state in the log is easy to read as "done". The server logs KSK submission, waiting for confirmation and keeps waiting until you either configure a
parent check or run knotc zone-ksk-submitted.
The secure configuration
/etc/knot/knot.conf:
server:
rundir: "/run/knot"
user: knot:knot # drop root after binding port 53
listen: [ 0.0.0.0@53, ::@53 ]
log:
- target: syslog
any: info
database:
storage: "/var/lib/knot" # KASP DB (private keys) is /var/lib/knot/keys
remote:
- id: parent_ns # a validating resolver, or the parent's servers
address: 198.51.100.53@53
submission:
- id: parent_check
parent: [ parent_ns ]
check-interval: 1h # poll the parent for the new DS
parent-delay: 1d # wait for the parent's DS TTL before moving on
policy:
- id: secure
algorithm: ecdsap256sha256 # algorithm 13: small signatures, universal support
ksk-lifetime: 365d # a finite KSK lifetime; each roll needs a new DS
zsk-lifetime: 30d
rrsig-lifetime: 14d
rrsig-refresh: 7d # re-sign with a week left: survives a long outage
propagation-delay: 1h # time for every secondary to serve a change
dnskey-ttl: 3600
zone-max-ttl: 86400 # set explicitly; rollover timing depends on it
nsec3: on # no zone walking by plain NSEC
nsec3-iterations: 0 # RFC 9276: zero extra iterations
nsec3-salt-length: 0 # RFC 9276: no salt
cds-cdnskey-publish: always # parents that scan CDS (RFC 8078) can pick it up
ksk-submission: parent_check
zone:
- domain: example.com
file: "/etc/knot/zones/example.com.zone"
dnssec-signing: on
dnssec-policy: secure
zonefile-sync: -1 # never write signatures back into the source file
zonefile-load: difference-no-serial
journal-content: all # the signed zone survives restarts via the journal
semantic-checks: on
# no acl: zone transfers and updates are refused by defaultFile permissions for the key store:
# The KASP DB holds private keys. Only knotd may read or write it.
chown -R knot:knot /var/lib/knot
chmod 0770 /var/lib/knot/keys
chmod 0660 /var/lib/knot/keys/*.mdb
# Back it up encrypted, and exclude it from any shared or cloud-synced volume.Put the DS at the parent, then tell Knot:
keymgr example.com. ds # prints DS records for the KSK (digest 2 = SHA-256)
# Submit the digest type 2 DS at the registrar. Wait until the parent serves it.
knotc zone-ksk-submitted example.com # only needed without a working parent checkProve it
The test (secure-tests/knot-dns-automatic-dnssec-signing/run.sh) runs Knot
3.5.4 in one container with the policy above and lab paths. It shows the zone
before and after signing, validates with delv, confirms the KSK submission,
and checks the key store and the transfer ACL.
Before, with dnssec-signing: off, there is no DNSKEY at the apex:
kdig @127.0.0.1 example.com DNSKEY +dnssec +norecurse | grep -E "status|ANSWER:";; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 3091
;; Flags: qr aa; QUERY: 1; ANSWER: 0; AUTHORITY: 1; ADDITIONAL: 1After dnssec-signing: on and knotc reload:
knotc zone-status example.com
keymgr example.com list -e[example.com.] role: master | serial: 2026092402 | re-sign: +6D23h59m57s
4c6c34a7e34628c1273462baeba11ad6afde8cef ksk=no zsk=yes tag=58227 algorithm=13 size=256 public-only=no for-later=no missing=no created=1790281015 pre-active=0 publish=1790281015 ready=0 active=1790281015 retire-active=0 retire=0 post-active=0 revoke=0 remove=0
c3690a5d00ae3edc9e0b8a854b2212ce2b31b88d ksk=yes zsk=no tag=44598 algorithm=13 size=256 public-only=no for-later=no missing=no created=1790281015 pre-active=0 publish=1790281015 ready=1790281015 active=0 retire-active=0 retire=0 post-active=0 revoke=0 remove=0The next re-sign is seven days out: rrsig-lifetime 14d minus
rrsig-refresh 7d. The KSK is ready, not active. It signs the DNSKEY set,
but Knot is still waiting for the DS.
kdig @127.0.0.1 www.example.com A +dnssec +norecurse +noall +answerwww.example.com. 3600 IN A 192.0.2.10
www.example.com. 3600 IN RRSIG A 13 3 3600 20261008201655 20260924184655 58227 example.com. pAB9UpNePsNEQMSukwe/QB35FNs2ywKBHb7pfkusHrM4D0sCPsg7IqdocPv9rpBUOJnb4xYytm5dsTGO/zq/jw==Denial of existence uses NSEC3 with the RFC 9276 parameters 1 0 0 -
(SHA-1, zero iterations, no salt):
kdig @127.0.0.1 nothere.example.com A +dnssec +norecurse +noall +authorityexample.com. 3600 IN SOA ns1.example.com. hostmaster.example.com. 2026092402 7200 3600 1209600 3600
onib9mgub9h0rml3cdf5bgrj59dkjhvk.example.com. 3600 IN NSEC3 1 0 0 - gufvra2sfio8rsfp7uo41e8ad1kr41fh NS SOA RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
example.com. 3600 IN RRSIG SOA 13 2 3600 20261008201655 20260924184655 58227 example.com. W6Plo+JA4yiOwRNuJGykVaog05LSse5JA8zLkRrhcCr6o3gruYj41KD4SrJHEh39G91gEavG+rNmcS8qozb1Ow==
onib9mgub9h0rml3cdf5bgrj59dkjhvk.example.com. 3600 IN RRSIG NSEC3 13 3 3600 20261008201655 20260924184655 58227 example.com. zLnPH1KbIVZMHHBUD7sPTtYSBhanANnIITyRi7mYfAJrPIoC0TsrNg5z4L4T02d8q3HWDwhqA4rKQueSV5hoGA==The published CDS record and the DS for the registrar match:
kdig @127.0.0.1 example.com CDS +norecurse +short
keymgr example.com ds 4459844598 13 2 142AE6815A3147C375BB7120CC9878DDAC147D99B560541C4F5BB668EA83BE45
example.com. DS 44598 13 2 142ae6815a3147c375bb7120cc9878ddac147d99b560541c4f5bb668ea83be45
example.com. DS 44598 13 4 6e3ba9eb62f18e9342888f13ae7fdff3870fa3db2dc5c364036adc377ecdb7afa8e7ade03941a192c4b54c1ce257321fWith that DS as the trust anchor (standing in for the parent), a validator accepts the answer:
delv @127.0.0.1 -a anchor.conf +root=example.com www.example.com A; fully validated
www.example.com. 3600 IN A 192.0.2.10
www.example.com. 3600 IN RRSIG A 13 3 3600 20261008201655 20260924184655 58227 example.com. pAB9UpNePsNEQMSukwe/QB35FNs2ywKBHb7pfkusHrM4D0sCPsg7Iqdo cPv9rpBUOJnb4xYytm5dsTGO/zq/jw==Once the DS is live, confirm the submission. The KSK gets an active time:
knotc zone-ksk-submitted example.com
keymgr example.com listOK
4c6c34a7e34628c1273462baeba11ad6afde8cef 58227 ZSK ECDSAP256SHA256 created=1790281015 publish=1790281015 active=1790281015
c3690a5d00ae3edc9e0b8a854b2212ce2b31b88d 44598 KSK ECDSAP256SHA256 created=1790281015 publish=1790281015 ready=1790281015 active=1790281019The server log for the whole run:
notice: [example.com.] DNSSEC, KSK submission, waiting for confirmation
info: [example.com.] DNSSEC, key, tag 44598, algorithm ECDSAP256SHA256, KSK, public, ready, active+
info: [example.com.] DNSSEC, key, tag 58227, algorithm ECDSAP256SHA256, public, active
info: [example.com.] DNSSEC, successfully signed, serial 2026092402, new RRSIGs 12
info: [example.com.] DNSSEC, next signing at 2026-10-01T20:16:55+0000
notice: [example.com.] DNSSEC, KSK submission, confirmed
info: [example.com.] DNSSEC, key, tag 58227, algorithm ECDSAP256SHA256, public, active
info: [example.com.] DNSSEC, key, tag 44598, algorithm ECDSAP256SHA256, KSK, public, activeThe key store is closed to other users, the source file stays unsigned, and a transfer without an ACL is refused:
ls -l /storage/keys
grep -c RRSIG /config/example.com.zone
kdig @127.0.0.1 example.com AXFR +tcptotal 52
-rw-rw---- 1 knot knot 45056 Sep 24 20:16 data.mdb
drwxr-x--- 2 knot knot 4096 Sep 24 20:16 keys
-rw-rw---- 1 knot knot 1856 Sep 24 20:17 lock.mdb
0
;; ERROR: server replied with error 'NOTAUTH'
;; ERROR: failed to query server 127.0.0.1@53(TCP)Mistakes people make
Signed, served, and never chained
A signed zone without a DS at the parent is "insecure" to every resolver.
It behaves exactly like an unsigned zone. Check the parent with
kdig example.com DS @<a parent server> and validate with delv from a
machine that uses the real root trust anchor.
Reading "ready" as "done"
The first KSK starts in the ready state and Knot logs KSK submission, waiting for confirmation. The zone is signed, so it is tempting to move on.
Until you confirm, the KSK has no active time and the submission stays
open. Confirm it once the DS is live, or configure a submission parent
check so Knot confirms it by asking the parent.
NSEC3 with a salt and 100 iterations
Old guides copied from BIND tutorials set salts and high iteration counts. RFC 9276 requires 0 iterations and recommends no salt. Many resolvers now treat high iteration counts as insecure or return SERVFAIL. Knot's defaults are already 0 and 0; do not override them upward.
Letting knotd write signatures into the zone file
With zonefile-sync at its default, Knot flushes the signed zone back to the
file. Your git-managed source then fills with RRSIG records, and the next
commit either loses them or fights with them. Keep zonefile-sync: -1 and
journal-content: all.
Leaving zone-max-ttl and dnskey-ttl implicit
Knot computes them from the zone if you do not set them. One record added
later with a long TTL silently stretches every rollover step. Set both, and
keep the zone's largest TTL at or under zone-max-ttl.
Checklist
- Assign an explicit
policywithecdsap256sha256(ored25519) to every signed zone. - Set
nsec3-iterations: 0andnsec3-salt-length: 0if you use NSEC3. - Set
dnskey-ttl,zone-max-ttlandpropagation-delayexplicitly. - Set
rrsig-refreshto several days so a stopped signer does not break the zone overnight. - Set
zonefile-sync: -1andjournal-content: all. - Make the KASP database directory
0770 knot:knotand exclude it from shared volumes. - Keep backups of the KASP database encrypted.
- Submit the digest type 2 DS from
keymgr <zone> dsat the registrar. - Confirm the DS with
kdig DSagainst the parent, then runknotc zone-ksk-submittedor configure asubmissionparent check. - Validate from outside with
delvusing the real root trust anchor. - Confirm
kdig AXFRfrom an unlisted address returnsNOTAUTH.
Knot will sign anything you point it at. Whether anyone believes those signatures is decided one level up, at the parent, so finish the job there.
H2-CPQE
Learn it on a live range
DNSSEC with Knot, in Edge and Post-Quantum Networking: a real host in your browser, and every objective checked on the machine.
Start freeThe Dome
Want it run for you?
The Dome puts post-quantum TLS, a WAF that blocks, signed DNS and a zero-trust mesh in front of your application. Tell us what you run.
See the Dome