DNSSEC ZSK and KSK Key Rollover with BIND 9 - 夜莺博客

DNSSEC ZSK and KSK Key Rollover with BIND 9

Every DNSSEC outage on the public internet has the same shape: keys were rolled, the signature chain momentarily disagreed, and validating resolvers answered SERVFAIL for the whole zone. Rolling keys is not optional — it is the whole point of having a key lifetime — but it is a procedure where the timing rules matter more than the cryptography. This article explains the two rollover strategies BIND 9 implements, how dnssec-policy automates them, and the verification steps that prove a rollover completed before you remove the old key.

Two Key Roles, Two Strategies

  • ZSK (flags 256) signs the zone data. It is not referenced by the parent, so it can be rotated entirely within your zone — and therefore automated.
  • KSK (flags 257) signs the DNSKEY RRset, and its hash (the DS record) is published in the parent zone. Any KSK change requires coordination with the parent, which is why KSK rollovers are usually manual.
  • CSK (combined signing key) does both, which simplifies configuration at the cost of forcing parent coordination on every roll.

BIND 9 applies Pre-Publish for ZSK rollovers and Double-KSK (also called double-DS or double-signature depending on the variant) for KSK rollovers, exactly as analysed in RFC 7583. In both cases the new DNSKEY is published next to the old one, time is allowed to propagate, and only then is the old key retired.

Declarative Configuration with dnssec-policy

dnssec-policy "default-roll" {
    keys {
        ksk lifetime unlimited algorithm ecdsap256sha256;
        zsk lifetime 90d algorithm ecdsap256sha256;
    };
    dnskey-ttl 3600;
    publish-safety 1h;
    retire-safety 1h;
    purge-keys 90d;
    signatures-refresh 5d;
    signatures-validity 14d;
    signatures-validity-dnskey 14d;
    zone-propagation-delay 300;
    parent-ds-ttl 86400;
};

zone "example.com" {
    type primary;
    file "/var/lib/bind/db.example.com";
    dnssec-policy "default-roll";
    inline-signing yes;
    key-directory "/var/lib/bind/keys";
};

Everything that makes rollovers safe lives in those timers. zone-propagation-delay must reflect how long your secondaries and the parent actually take to see new records, and parent-ds-ttl must match the DS TTL your parent publishes. Setting them optimistically is the single most common cause of a transient validation failure.

When migrating an old installation that used dnssec-keygen and manual resigning, import the existing keys into the policy key directory with their real timestamps and let BIND take over, rather than generating fresh keys and re-signing in one step. After the policy is active, auto-dnssec and dnssec-dnskey-kskonly are legacy switches you should remove.

Manual Control and Status Checks

# What is the zone actually signing with?
rndc dnssec -status example.com

dnssec-policy: default-roll
current time:  Thu Feb  2 12:00:27 2023

key: 2295 (ECDSAP256SHA256), CSK
  published:   yes - since Sat Dec  3 18:45:40 2022
  key signing: yes - since Sat Dec  3 18:45:40 2022
  Key will retire on Fri Feb  3 09:05:00 2023
  - goal: hidden | dnskey: omnipresent | ds: omnipresent | zone rrsig: omnipresent

key: 13297 (ECDSAP256SHA256), CSK
  published:   yes - since Thu Feb  2 11:58:46 2023
  key signing: yes - since Thu Feb  2 14:03:46 2023
  No rollover scheduled
  - goal: omnipresent | dnskey: rumoured | ds: hidden | zone rrsig: rumoured

Read the status output as a state machine. Two keys with the same role visible means a roll is in progress: the new key reaches omnipresent first, then starts signing, and only after that does the old key move to hidden and retire. Do not manually delete anything in that window.

Scheduling a roll

when=$(TZ=UTC date -d "now + 60 days" +"%Y%m%d%H%M%S")
rndc dnssec -rollover -key 12345 -when $when example.com
# immediate roll of a specific key
rndc dnssec -rollover -key 12345 example.com

KSK rollover and the parent DS

dnssec-dsfromkey -2 Kexample.com.+013+13297
# submit the resulting DS to the parent (registry UI or your parent's API)
# then verify from an external resolver
dig +dnssec DS example.com @a.gtld-servers.net
dig +dnssec DNSKEY example.com @1.1.1.1

The KSK cannot be automated end to end because the DS must be published by the parent. Publish the new DS before you retire the old one, wait at least the parent DS TTL plus registry propagation, and only then withdraw the old DS and let the old key retire. Validators that cached the old DS will roll over to the new one on TTL expiry.

Verification Before You Declare Success

  1. rndc dnssec -status <zone> shows the expected goal state for every key.
  2. dig +dnssec SOA <zone> @<your-ns> returns an RRSIG signed by a key in the current DNSKEY set.
  3. dig +dnssec DNSKEY <zone> plus dnssec-verify -o <zone> <zonefile> reports no errors.
  4. An external validating resolver (1.1.1.1, 8.8.8.8, or a local Unbound with DNSSEC enabled) returns NOERROR with the AD flag set.
  5. delv @localhost example.com +rtrace shows the full chain of trust being built locally.

One caveat worth knowing: resolvers cache aggressively, so a "broken" zone immediately after a rollover may simply be a negative cache entry. Monitor SERVFAIL rates externally (not from your own recursive resolver) during the change window, and watch the same metrics for at least two TTL periods after.

Operational Notes

  • Use ECDSAP256SHA256 unless a specific validator requires RSA; it makes signatures and key files small enough that DNSKEY responses stop hitting UDP size limits.
  • Keep purge-keys generous — purging a retired private key early makes a rollback impossible.
  • Back up key-directory including KSK private keys with the same rigour as your zone data, and store them offline.
  • Rotate on a schedule that is faster than "when someone remembers"; a 90-day ZSK lifetime with automated policy is the practical default.

Related reading: BIND 9 DNSSEC signing and validation, authoritative zone configuration and Unbound DNSSEC validation troubleshooting.

原文链接:https://kb.isc.org/docs/dnssec-key-and-signing-policy