The DNSSEC chain of trust starts at the root of the DNS with a resolver typically trusting said root by the fact that it’s got the root key (or hash thereof, called a Delegation Signer – DS record) built-in or configured into it. From there, a resolver chases delegation signer records (DS) which indicate to it, that a child zone is signed. We can compare this to how a resolver chases name server (NS) records to find delegations. The hash of a child zone’s DNSKEY is a DS record which is located in it’s parent zone and which has therefore been signed by the parent.
In the case of example.net, we know that net is signed, so the root zone contains a DS record for net. If example.net is signed, its parent zone (net) contains a DS record for example.net, and so forth.
Any child zone which is signed must have a hash of its secure entry point as a DS record in its parent zone.
Uploading DS from a child to a parent zone can be an entertaining proposition. Anything from copy/paste into some (often lousy) Web form to sending an email might be available. Unfortunately there’s no real standard to accomplish this as some parent zones want DS records whereas others insist on DNSKEY records (from which they calculate the DS themselves). Be that as it may, what we typically do is to obtain the DS. For utilities provided by BIND or PowerDNS:
With a bit of zone name mangling and TTL adding we can use pdnsutil with dnssec-dsfromkey, but pdnsutil has its own subcommand as well:
Generally speaking the story stops here, and I’d leave you in charge of getting that DS-set to your parent zone somehow. Digressing only slightly, OpenDNSSEC has for ages, had a DelegationSignerSubmitCommand
program in its configuration which can upload DS/DNSKEY to a parent via a program you create; the script you write and configure gets new keys via stdin and you can then automate submission to a parent zone to your heart’s content.
Can I haz automatik?
What we really want is automatic DS submission such as that the child zone uploads the DS directly to the parent zone where it is then signed. Unless the parent and the child zone are both under my administrative charge, that’s easier said than done: it’s unlikely the parent will allow me to do that.
Enter RFC 7344 which allows me to indicate, in my child’s zone, that I have a new DS record for submission. (This also works for DNSKEY records for those parents which prefer DNSKEY.) The fact that the child zone has a new DS for submission is indicated with a CDS record (child DS) and/or CDNSKEY (child DNSKEY) respectively. What will actually happen is that the parent will “consume” CDS/CDNSKEY records instead of the child “pushing” them somewhere. Hereunder I will be using CDS because they’re shorter, but CDNSKEYs work equally well.
As per section 4 of RFC 7344, if a child publishes either CDS or CDNSKEY it should publish both, unless the child knows the parent will use one of a kind only.
Using PowerDNS, I can configure the Authoritative server to automatically publish CDS and/or CDNSKEY records:
The process for BIND is a bit more involved. What I do here is to set a timing parameter on a key when I create a new key (or just after having created it).
When running as an in-line signer, BIND will publish CDS and CDNSKEY records for the particular key until I use dnssec-settime to have it remove such records from the zone. (Note that BIND as smart signer (dnssec-signzone -S
) does not add CDS or CDNSKEY records to the signed zone. Why? Good question; IMO an omission.)
So, ideally, what we then need is a mechanism by which a server checks for CDS/CDNSKEY records in a child zone and then updates the corresponding parent zone.
dnssec-cds
A combination of dig and a new utility will allow me to automate the process.
Tony Finch has written such a beast. It’s called dnssec-cds and it’s currently in a git tree he maintains and has meanwhile been brought into the BIND source tree. What this program does is to change DS records at a delegation point based on CDS or CDNSKEY records published in the child zone. By default CDS records are used if both CDS and CDNSKEY records are present.
What we’ll actually be doing in order to add a new signed child zone is:
- Create and sign the zone.
- Obtain the DS-set, copy that securely to the parent, and sign the result. We do this step once and we do it securely because this is how we affirm trust between parent and child.
- Once in the parent zone, the DS records of the child indicate the child zone’s secure entry point: validation can be chased down into the child zone.
- When the child’s KSK rolls, ensure child zone contains CDS/CDNSKEY records.
- Parent will periodically query for child’s CDS/CDNSKEY records; if there are none, processing stops.
- As soon as CDS/CDNSKEY records are visible in the child, dnssec-cds validates these by affirming, using the original DS-set obtained in 2, that they’re valid and not being replayed.
- A dynamic (or other) update can be triggered on the parent to add the child’s new DS-set.
dnssec-cds protects against replay attacks by requiring that signatures on the child’s CDS are not older than they were on a previous run of the program. (This time is obtained by the modification time of the dsset-
file or from the -s
option. Note below that I touch the dsset-
file to ensure this, just the first time.) Furthermore, dnssec-cds protects against breaking the delegation by ensuring that the DNSKEY RRset can be verified by every key algorithm in the new DS RRset and that the same set of keys is covered by every DS digest type.
dnssec-cds writes replacement DS records (i.e. the new DS-set to standard output or to the input file if -i
is specified, and -u
prints commands suitable to be read by a dynamic DNS utility such as nsupdate. The replacement DS records will be the same as the existing records when no change is required. The output can be empty if the CDS / CDNSKEY records specify that the child zone wants to go insecure.
The BIND name server in my example hosts the parent zone example.net
, and we’ll create a child zone (sub.example.net
) on PowerDNS Authoritative (because we can). Which server brand the zone’s hosted on is quite irrelevant other than it must be able to serve CDS/CDNSKEY records in the zone. This is particularly easy to automate with PowerDNS.
First we sign the child zone and export its DS-set:
Note how the exported dsset-
contains one DS for each algorithm supported by my PowerDNS installation. We now copy the dsset-
to the parent server, and add its content to the parent zone. The zone is configured with auto-dnssec maintain
so BIND will immediately sign anything we add to it.
If I now query for the DS records for sub.example.net in the parent zone (recall a DS RRset is in the parent) I obtain an appropriate response:
Our parent zone is signed, our child zone is signed, our parent has a signed DS record (more than one actually, but that’s fine) for our child zone: the chain of trust is in place. (Note the key tag on the DS: 32128.)
Let it roll!
At some point in time we want to roll the child’s KSK, and I am not going to address timing issues of the roll proper; I’m discussing CDS only.
In order to roll a key, we create a new key in the child zone. Simultaneously we request PowerDNS publish CDS records in the zone for all keys:
This output is easy to follow once we notice that the top part has some metadata and then come the keys. Note that pdnsutil is printing a DS record for each of the algorithms PowerDNS supports, hence the verbosity. Let’s pay attention to the key tags: in above list we see our original 32128 tag and the new tag 48629.
The child zone is still signed; there are two keys in the zone, and we’ve requested CDS records be published. Does that work?
The CDS records are available with the digest algorithms currently implemented for DS, namely 1 (SHA1) and 2 (SHA256).
.. to the parent
Back on the parent, we prepare to use dnssec-cds for the magic. We already have the dsset-
file, and as discussed above I touch its timestamp (or use -s
switch):
dnssec_cds with the -u
option creates a script suitable for feeding into nsupdate; for debugging purposes, I tee it into a file to show you here:
querying the parent we see the DS records with the superflous algorithms have been deleted and the DS records for the new key have been added. We also see our dsset-
file has been updated accordingly (and I pay attention to the file’s modification time which has been set to the inception time of the DNSKEY RRSIG of the child zone):
Now I delete the “old” key from the child zone using its (in my opinion slightly confusing) ID which is 31 – compare with the output of pdnsutil show zone
above. (I would have preferred pdnsutil utilize key tags to refer to keys for a zone):
Now comes the drum-roll moment: if we re-run our dnssec-cds script what will it do?
A few points to note:
- when lookup at the nsupdate script produced by dnssec-cds pay attention to
add
vs.del
on theupdate
statements. - it’s not necessary to have dnssec-cds maintain the
dsset-
file on the file system, but it gives me a warm and fuzzy feeling so I think I’d always do that - I should also mention that the dnssec-dsfromkey utility is quite versatile; we saw it above, and it’s good to know that the
-C
option creates CDS records au lieu de DS records.
Tony’s dnssec-cds together with a wee bit of scripting will basically allow us to add new DS for zones to their parent zones. In the examples above I’ve used nsupdate, but this could equally well be accomplished by other means.
Further reading
- Automated DNSSEC Provisioning: Guidelines for CDS processing at SWITCH.