DNSSEC
DNSSEC lets a resolver prove that an answer really came from the zone's owner and wasn't changed on the way. Each zone signs its records, and each parent vouches for its child's key, all the way up to the root. PowerDNS makes the signing side almost automatic: it signs answers as it serves them, from keys kept in the backend.
The backend has to allow it
The first secure-zone fails: No backend was able to secure 'example.net'. PowerDNS's SQL backends only store DNSSEC data when told to, with gsqlite3-dnssec=yes. All three authoritative servers need it, the secondary included, because it stores the signed records it receives.
Signing a zone
pdnsutil secure-zone example.net creates a key and starts signing. PowerDNS's default is a single combined signing key (CSK) using ECDSA P-256 (algorithm 13): small, fast signatures, and one key to manage instead of two. rectify-zone then adds the ordering information PowerDNS needs to prove that names don't exist.
show-zone lists the key, its DNSKEY record (the public key, published in the zone) and the DS record: a hash of that key, which the parent zone must publish. The DS is the link in the chain of trust. Until the parent has it, the zone is signed but nobody can validate it.
The chain of trust
The lab builds the full chain:
- The DS for
example.netgoes into thenetzone, andnetis signed. - The DS for
netgoes into the root zone, and the root is signed. - The root's own DS becomes the resolver's trust anchor: the one key it trusts without asking anyone, just as real resolvers ship with the real root's key.
In real life, step 1 is done through your domain registrar, who passes the DS to the registry. One more step matters: signing doesn't change the zone's serial, so increase it and notify, or the secondary keeps serving the unsigned copy.
root@ns1:~# pdnsutil secure-zone example.net && pdnsutil rectify-zone example.net
Securing zone with default key size
Adding CSK (257) with algorithm ecdsa256
No backend was able to secure 'example.net', most likely because no DNSSEC
capable backends are loaded, or because the backends have DNSSEC disabled.
For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or
'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!
root@ns1:~# pdnsutil show-zone example.net
No keys for zone 'example.net'.
This is a Master zone
Last SOA serial number we notified: 2026102104 == 2026102104 (serial in the database)
Zone is not actively secured
Metadata items:
SOA-EDIT-API DEFAULT
root@ns1:~# pdnsutil export-zone-ds example.net
Zone is not actively secured- Lines 4–7: The backend must be told to store DNSSEC data.
- Line 13: Nothing was signed.
root@ns1:~# grep dnssec /etc/powerdns/pdns.d/ns1.conf && systemctl restart pdns
gsqlite3-dnssec=yes
root@ns1:~# pdnsutil secure-zone example.net && pdnsutil rectify-zone example.net
Securing zone with default key size
Adding CSK (257) with algorithm ecdsa256
Zone example.net secured
Adding NSEC ordering information for zone 'example.net', 8 updates
Adding NSEC ordering information for zone 'example.net', 1 updates
root@ns1:~# pdnsutil show-zone example.net
This is a Master zone
Last SOA serial number we notified: 2026102104 == 2026102104 (serial in the database)
Metadata items:
SOA-EDIT-API DEFAULT
Zone has NSEC semantics
keys:
ID = 1 (CSK), flags = 257, tag = 18425, algo = 13, bits = 256 Active Published ( ECDSAP256SHA256 )
CSK DNSKEY = example.net. IN DNSKEY 257 3 13 JwbFcWWEpxje9623ndQwMircZ8Mvk709HHNW+wgpADQC8q3QBMOM5jAHLk2JHFQAUJi/M3OgZ9xFh1OXuwQ3mg== ; ( ECDSAP256SHA256 )
DS = example.net. IN DS 18425 13 2 9b98e79540d98caeb43674c9a6530ecbaa8ea151d501fb8c99819c15154bdd92 ; ( SHA256 digest )
DS = example.net. IN DS 18425 13 4 8ae2e2ff3c30eae3775b6922d6942d698c85d8101467aecc9c5236933278bf3f2a09d14de76db2489c537c238b92563d ; ( SHA-384 digest )
root@ns1:~# pdnsutil export-zone-ds example.net
example.net. IN DS 18425 13 2 9b98e79540d98caeb43674c9a6530ecbaa8ea151d501fb8c99819c15154bdd92 ; ( SHA256 digest )
example.net. IN DS 18425 13 4 8ae2e2ff3c30eae3775b6922d6942d698c85d8101467aecc9c5236933278bf3f2a09d14de76db2489c537c238b92563d ; ( SHA-384 digest )- Lines 1–2:
gsqlite3-dnssec=yes, then a restart. - Lines 5–7: One combined signing key (CSK), ECDSA P-256.
- Lines 8–9: Ordering information, used to prove names don't exist.
- Line 18: The key: tag 18425, algorithm 13.
- Line 19: The public key, published in the zone.
- Lines 20–21: The DS record the parent must publish (SHA-256; SHA-384 as an alternative).
root@rootns:~# pdnsutil add-record net example DS '18425 13 2 9b98e79540d98caeb43674c9a6530ecbaa8ea151d501fb8c99819c15154bdd92' && pdnsutil secure-zone net && pdnsutil secure-zone . && pdnsutil rectify-all-zones
New rrset:
example.net. 3600 IN DS 18425 13 2 9b98e79540d98caeb43674c9a6530ecbaa8ea151d501fb8c99819c15154bdd92
Securing zone with default key size
Adding CSK (257) with algorithm ecdsa256
Zone net secured
Adding NSEC ordering information for zone 'net', 11 updates
Securing zone with default key size
Adding CSK (257) with algorithm ecdsa256
Zone . secured
Adding NSEC ordering information for zone '.', 9 updates
Rectifying .: Adding NSEC ordering information for zone '.', 6 updates
Rectifying net: Rectified 2 zones.
Adding NSEC ordering information for zone 'net', 11 updates
root@rootns:~# pdnsutil add-record . net DS '53664 13 2 a5e8adbc0be2c16c3f5ec9e93ac1c0355414c57a02938625531d01742dc23670' && pdnsutil rectify-zone . && systemctl restart pdns
New rrset:
net. 3600 IN DS 53664 13 2 a5e8adbc0be2c16c3f5ec9e93ac1c0355414c57a02938625531d01742dc23670
Adding NSEC ordering information for zone '.', 8 updates
root@rootns:~# pdnsutil export-zone-ds . | grep SHA256
. IN DS 49430 13 2 79ec0fb185cf6f5be1df5936655d08fb7dc4e280694535ab8d819da9cccb7757 ; ( SHA256 digest )
root@client:~# dig @203.0.113.1 net DS +dnssec +norecurse | grep -E "IN\s+(DS|RRSIG)"
;net. IN DS
net. 3600 IN RRSIG DS 13 1 3600 20261029000000 20261008000000 49430 . +pS6XfP+cKLJUeWgookIAa7O9ZSj9dvFrcewzQt9Uh6L+2uFxsRjfNZR 3RfEMzGscllmsoJQduhuaUdO9uUaHA==
net. 3600 IN DS 53664 13 2 A5E8ADBC0BE2C16C3F5EC9E93AC1C0355414C57A02938625531D0174 2DC23670
root@client:~# dig @203.0.113.1 example.net DS +dnssec +norecurse | grep -E "IN\s+(DS|RRSIG)"
;example.net. IN DS
example.net. 3600 IN DS 18425 13 2 9B98E79540D98CAEB43674C9A6530ECBAA8EA151D501FB8C99819C15 154BDD92
example.net. 3600 IN RRSIG DS 13 2 3600 20261029000000 20261008000000 53664 net. GfYaqMVw6dN7eEdz83lw51lIxTbSAj92Pzm4K6k79gZcNcnGjo2ZmDPF M/BrEai8QxIlITP71R424fgYY+KLvQ==- Lines 1–3: example.net's DS goes into net…
- Lines 4–10: …net and the root are signed…
- Lines 16–18: …net's DS goes into the root…
- Lines 21–22: …and the root's DS becomes the resolver's trust anchor.
- Lines 25–27: The root vouches for net (signed by key 49430, the root's).
- Lines 30–32: net vouches for example.net (signed by key 53664, net's).
Check your understanding
You've just run pdnsutil secure-zone on your zone. What else has to happen before validating resolvers trust its signatures?
- The DS record has to be published in the parent zone, through your registrar.
- Every resolver needs your DNSKEY configured by hand.
- Nothing: signing is enough.
- The zone has to be converted to NSEC3.