Whenever a DNSSEC-signed zone changes their trust anchor (i.e. typically a KSK), the delegation signer (DS) record has to be communicated to the parent zone via some API. RFC7344 describes how this DNSSEC Delegation Trust Maintenance can be automated via the DNS itself:

a technique in which the Parent periodically (or upon request) polls its signed Children and automatically publishes new DS records.

Two record types (CDS and CDNSKEY, henceforth written as CDS) are used to convey the desired DS state from child zone to its parent. The records are published in the child zone (manually or automatically) and indicate what the child would like the DS RRset to look like after the change. A parent consumes the child DS (CDS) records and replaces by whichever means it needs to, the DS RRset in the parent zone.

There are typically three operations that a child zone wishes its parent to perform:

  1. Enable validation, i.e. store an initial DS RRset in the parent
  2. Roll over the zone trust anchor (typically a KSK), which means updating the DS in the parent or change hashing algorithms, e.g. as during the SHA-1 deprecation
  3. Disable DNSSEC validation entirely, i.e. delete all DS records for the child, in the parent

I first worked with CDS/CDNSKEY in 2017, and I think it was in 2018 that I visited SWITCH and met Michael, Daniel, and Oli who explained they were working on a mechanism to permit holders of .ch and .li domains to use CDS/CDNSKEY to upload DS to the parent, something which was already possible in .cz. I thought this quite exciting and have since several times mentioned SWITCH doing this.

It occurred to me a fortnight ago, when I was again explaining the benefits of CDS, that I’d never actually experienced the SWITCH system in operation. Instead of going out for a quick meal I spent the equivalent on a Swiss domain name with which to experiment with the added benefit that it’s calorie-free.

SWITCH have laid out exact acceptance criteria in well-written guidelines for CDS processing at SWITCH, and they include criteria for bootstrapping a first CDS upload:

  • CDS records must be consistently published for three consecutive days, and any change in the RRset resets the counter.
  • all name servers must be reachable over TCP and deliver a consistent CDS RRset

Being an impatient person, I thought I’d first make sure CDS is published and then register the domain, hoping that the initial query of the registry would detect CDS and therewith automatically begin the 3-day cycle.

So I create a trust anchor key for my zone and set CDS publishing to ten minutes hence in order to test the idea a user suggested during a BIND webinar: log CDS publication so external programs can react to it.

$ dnssec-keygen -a 13 -P sync now+10mi tcp53.ch
Generating key pair.
Ktcp53.ch.+013+02132

$ cat Ktcp53.ch.+013+02132.key
; This is a zone-signing key, keyid 2132, for tcp53.ch.
; Created: 20210918195720 (Sat Sep 18 19:57:20 2021)
; Publish: 20210918195720 (Sat Sep 18 19:57:20 2021)
; Activate: 20210918195720 (Sat Sep 18 19:57:20 2021)
; SyncPublish: 20210918200925 (Sat Sep 18 20:09:25 2021)
tcp53.ch. IN DNSKEY 256 3 13 1jv8eUuJ+alGvnAh2aQjxm27pez3aR62DTmDMwDxkcqJvpkCP4FGhrLj 4E+21hqUSa50XJ2VcimQqFL5RyPlLA==

(If you’re wondering why that’s a ZSK with flags 256, no need to wonder – it’s perfectly legal; it’s a single signing key, and there’s a well-known precedent for doing this: co.uk. :-)

Minutes later I observe BIND logging CDS publication:

18-Sep-2021 20:09:25.641 CDS for key tcp53.ch/ECDSAP256SHA256/2132 is now published
18-Sep-2021 20:09:25.641 CDNSKEY for key tcp53.ch/ECDSAP256SHA256/2132 is now published

and I can query CDS (and CDNSKEY) in the zone:

$ dig @::1 tcp53.ch CDS +norec
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; ANSWER SECTION:
tcp53.ch.     3600  IN  CDS  2132 13 2 7B895D35CC8F5A6EBD7C2BBED8738487084322DE22622E13FBF99FE6 5FEA20BB

I register the domain via my registrar, and as soon as that’s done, I impatiently check the status of CDS publication:

status of publication

I’m in for a wait so I call it an evening and relax, assuming the robot does its thing at 06:00Z. Sunday morning at about 08:00Z, excited and refreshed, I check again and am confronted with the same message. Does the Swiss robot not work on Sundays? I wake Oli to ask him. It turns out there are two robots: the first rises early to collect CDS/CDNSKEY and RRSIG from distinct vantage points, and the second robot sleeps in a bit and then compares the scan results and performs required checks, tracks the state of each domain, and updates the information presented on the Web page.

CDS status found at switch

That looks quite good to me. They’ve noticed we’re bootstrapping, the state is PENDING which likely means no DS copied to parent yet, and we’re on the first verification.

The disadvantage of this initial bootstrapping is that it’s going to take at least three days before the DS lands in the parent. It’s not a problem in this case, but if I want a zone signed immediately, I’d have to bootstrap via my registrar which, for me, is a copy/paste activity and an email.

While waiting, I also want to remind you that CDS automation with Knot-DNS is also possible: when a zone is signed, Knot publishes CDS records and uploads the hashes as DS records to the parent’s server via a dynamic DNS update

So, after waiting the required 72 hours, I finally get to see the result on the CDS status page, but I have to wait a publishing cycle for the DS to actually show up in the parent .CH zone.

CDS final status

;; ANSWER SECTION:
tcp53.ch.		3600 IN	DS 2132 13 2 (
				7B895D35CC8F5A6EBD7C2BBED8738487084322DE2262
				2E13FBF99FE65FEA20BB )

The zone now validates:

;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1


;; ANSWER SECTION:
tcp53.ch.		3567 IN	RP . jpm.people.dnslab.org.
tcp53.ch.		3567 IN	RRSIG RP 13 2 3600 (
				20211006101141 20210922052438 2132 tcp53.ch.
				[omitted] )

Rollover

I’m not a great believer in DNSSEC key rollovers (don’t don’t typically roll my SSH or house keys either), but I want to see how this works at .CH, so I generate a new key and kick the signer.

$ dnssec-keygen -K . -a 13 -P sync now+2mi tcp53.ch
Generating key pair.
Ktcp53.ch.+013+12909

$ cat Ktcp53.ch.+013+12909.key
; This is a zone-signing key, keyid 12909, for tcp53.ch.
; Created: 20210922082733 (Wed Sep 22 08:27:33 2021)
; Publish: 20210922082733 (Wed Sep 22 08:27:33 2021)
; Activate: 20210922082733 (Wed Sep 22 08:27:33 2021)
; SyncPublish: 20210922082933 (Wed Sep 22 08:29:33 2021)
tcp53.ch. IN DNSKEY 256 3 13 MGNbmcOycYHQpbQtli+VAIZwMkYCLBSSrStl5WjqBsV3VZBugy4a71SL FSKlkstj/h4OKjqBBkwAin6DCNUVeA==

$ rndc sign tcp53.ch

$ tail -5 dnssec.log
22-Sep-2021 08:29:47.105 Fetching tcp53.ch/ECDSAP256SHA256/12909 (ZSK) from key repository.
22-Sep-2021 08:29:47.105 DNSKEY tcp53.ch/ECDSAP256SHA256/12909 (ZSK) is now published
22-Sep-2021 08:29:47.105 DNSKEY tcp53.ch/ECDSAP256SHA256/12909 (ZSK) is now active
22-Sep-2021 08:29:47.105 CDS for key tcp53.ch/ECDSAP256SHA256/12909 is now published
22-Sep-2021 08:29:47.105 CDNSKEY for key tcp53.ch/ECDSAP256SHA256/12909 is now published

$ dig @::1 tcp53.ch CDS +nocrypto

;; ANSWER SECTION:
tcp53.ch.               3600    IN      CDS     2132 13 2 [omitted]
tcp53.ch.               3600    IN      CDS     12909 13 2 [omitted]

At 08:50 on the morning after creating the 2nd key and publishing its CDS, I check the status, and the robot has already obtained the set:

2x CDS found upon key rollover

At 09:12 CEST I notice a change in .CH SOA serial number and query them to find both our DS records in the parent. As the zone is signed and as SWITCH can validate the CDS as it already trusts the child, the import of the additional DS can be done without further ado.

I will now remove the first key (2132) from the zone and unpublish its CDS, and expect no problems with that. The result will be that the parent reflects the deleted CDS in their DS RRset.

$ dnssec-settime -P ds now -D sync now -D now+3600 Ktcp53.ch.+013+02132.
./Ktcp53.ch.+013+02132.key
./Ktcp53.ch.+013+02132.private

$  grep ';' Ktcp53.ch.+013+02132.key
; This is a zone-signing key, keyid 2132, for tcp53.ch.
; Created: 20210918195720 (Sat Sep 18 19:57:20 2021)
; Publish: 20210918195720 (Sat Sep 18 19:57:20 2021)
; Activate: 20210918195720 (Sat Sep 18 19:57:20 2021)
; Delete: 20210923081832 (Thu Sep 23 08:18:32 2021)
; SyncPublish: 20210918200925 (Sat Sep 18 20:09:25 2021)
; SyncDelete: 20210923071832 (Thu Sep 23 07:18:32 2021)

$ rndc sign tcp53.ch

$ tail -f dnssec.log
23-Sep-2021 07:19:24.189 zone tcp53.ch/IN (signed): reconfiguring zone keys
23-Sep-2021 07:19:24.189 CDS (SHA-256) for key tcp53.ch/ECDSAP256SHA256/2132 is now deleted
23-Sep-2021 07:19:24.189 CDNSKEY for key tcp53.ch/ECDSAP256SHA256/2132 is now deleted

And this is the point at which I notice I’ve been mixing time zones, and I’m sorry for the confusion. Times in consoles on the server are UTC (as they ought to be), and times you see in most dig(1) queries are in CEST, because that’s how my workstation is set up.

Delete

Change of DNS operator for a domain can be facilitated by turning off DNSSEC for it, and I want to see how this functions.

RFC8078 section 4 describes the Delete Algorithm which basically entails publishing a signed CDS (and/or CDNSKEY) resource record (the RRset MUST contain just the one record) with the following rdata:

CDS 0 0 0 00
CDNSKEY 0 3 0 AA==

This signed CDS is validated via the DS which is already in the parent, i.e. as SWITCH’ Guidelines also remind me, DNSSEC validation for my zone must succeed in order for them to remove its trust anchor from the parent.

I tune the DNSSEC key to delete CDS/CDNSKEY records from the zone and add the “deletion” CDS to it:

$ dnssec-settime -D sync now Ktcp53.ch.+013+12909.
./Ktcp53.ch.+013+12909.key
./Ktcp53.ch.+013+12909.private

$ grep ';' Ktcp53.ch.+013+12909.key
; This is a zone-signing key, keyid 12909, for tcp53.ch.
; Created: 20210922082733 (Wed Sep 22 08:27:33 2021)
; Publish: 20210922082733 (Wed Sep 22 08:27:33 2021)
; Activate: 20210922082733 (Wed Sep 22 08:27:33 2021)
; SyncPublish: 20210922082933 (Wed Sep 22 08:29:33 2021)
; SyncDelete: 20210924170318 (Fri Sep 24 17:03:18 2021)

$ tail dnssec.log
24-Sep-2021 17:04:52.825 CDS (SHA-256) for key tcp53.ch/ECDSAP256SHA256/12909 is now deleted
24-Sep-2021 17:04:52.825 CDNSKEY for key tcp53.ch/ECDSAP256SHA256/12909 is now deleted

$ nsupdate -l <<E!
add tcp53.ch. 60 CDS 0 0 0 00
send
E!

The result is a signed Delete CDS in the zone, so I expect tomorrow’s verification to have removed DS records from the parent, effectively “unsigning” the zone. (DNSSEC keys and signatures remain in the zone, but as there’s no chain of trust from the parent, validation will be skipped for my zone.)

It turns out this becomes a bit more complicated (on my side) due to a hiccup with the manually added CDS, but I will report on that a bit later when I’ve had the time to reflect what went wrong. To cut a story a bit short, I had to manually sign the zone for the manually added Delete CDS to “stay” in it.

Summary

CDS works, and it’s a good method to communicate changes to a parent zone, particularly if both parent and child are under my control: I can then use automatic DS submission like Knot provides, or use dnssec-cds provided by BIND.

I think I understand SWITCH’ motivation for waiting three full days for bootstrapping new zones, i.e. before accepting the CDS/CDNSKEY records and copying the DS into the parent zone, but it is quite a long time to wait for. It’s an “are you really really sure you want to do this, or did you sign by mistake” kind of period. For all intents and purposes the zone does not validate during this 72 hour period. (Note, that I’m discussing the DNS method; SWITCH also utilizes EPP.) What also works (I tested it) is that SWITCH reset the 3 day counter as soon as one of the zone’s name servers doesn’t respond. The motivation for the three days was to avoid BGP hijacking issues, and this duration was chosen (instead of an even longer one) as a compromise – if my name server IP addresses are hijacked for three days without me noticing, I’m likely having much bigger issues than DNSSEC not being enabled on a zone.

I had briefly hoped that RFC8078 section 3.5 had secretly been implemented. Accept from Inception enables a parent adding a domain which is not yet delegated at all to use the child CDS RRset to immediately publish a DS along with the new NS RRset: it’s delegated in a secure state, so to speak. It seems to me as though this would be beneficial for those domains which can actually create keys and publish CDS/CDNSKEY just before registering the domain, but I’m clueless as to how many those would be, percentage-wise, so whether the effort is at all worthwhile.

RFC8078’s 3.4 Accept with Challenge might also be a viable method. I’m thinking along the lines of an HTTPS request to a well-known URL which produces a challenge which must be added to the DNS within, say, 15 minutes, before the robot queries whether it’s actually there before consuming and processing CDS.

Further reading

  • DNSSEC Bootstrapping describes an authenticated in-band method for automatic signaling of a DNS zone’s delegation signer information from the zone’s DNS operator. I understand SWITCH is considering this for a future iteration.