From 56814221fdfea2e335341b194da5aef4d8b82e4d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 19 Jun 2024 13:41:07 +0200 Subject: [PATCH] Update key states in offline-ksk mode With offline-ksk enabled, we don't run the keymgr because the key timings are determined by the SKR. We do update the key states but we derive them from the timing metadata. Then, we can skip a other tasks in offline-ksk mode, like DS checking at the parent and CDS synchronization, because the CDS and CDNSKEY RRsets also come from the SKR. (cherry picked from commit 2190aa904fb9a5c990395c76673c42636798a2ee) --- lib/dns/include/dns/keymgr.h | 23 +++++ lib/dns/keymgr.c | 174 +++++++++++++++++++++++++++++++++++ lib/dns/zone.c | 45 ++++++--- 3 files changed, 227 insertions(+), 15 deletions(-) diff --git a/lib/dns/include/dns/keymgr.h b/lib/dns/include/dns/keymgr.h index eadb0ea877..61cf3df7aa 100644 --- a/lib/dns/include/dns/keymgr.h +++ b/lib/dns/include/dns/keymgr.h @@ -52,6 +52,29 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, *\li On error, keypool is unchanged */ +isc_result_t +dns_keymgr_offline(const dns_name_t *origin, dns_dnsseckeylist_t *keyring, + dns_kasp_t *kasp, isc_stdtime_t now, + isc_stdtime_t *nexttime); +/*%< + * Manage keys in 'keyring' when in offline-ksk mode, and update timing data + * according to the metadata in the key files. KSKs are skipped. + * + * Update key states and store changes back to disk. Store when to run next + * in 'nexttime'. + * + * Requires: + *\li 'origin' is a valid FQDN. + *\li 'mctx' is a valid memory context. + *\li 'keyring' is not NULL. + *\li 'kasp' is not NULL. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li any error returned by dst_key_getstate(), dst_key_gettime(), + * isc_dir_open(), or dst_key_to_file(). + */ + isc_result_t dns_keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, isc_stdtime_t now, isc_stdtime_t when, bool dspublish); diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 0d25db5d3f..9f54922ee8 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -2664,3 +2664,177 @@ dns_keymgr_rollover(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, return (result); } + +isc_result_t +dns_keymgr_offline(const dns_name_t *origin, dns_dnsseckeylist_t *keyring, + dns_kasp_t *kasp, isc_stdtime_t now, + isc_stdtime_t *nexttime) { + isc_result_t result = ISC_R_SUCCESS; + int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); + char keystr[DST_KEY_FORMATSIZE]; + + *nexttime = 0; + + /* Store key states and update hints. */ + for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; + dkey = ISC_LIST_NEXT(dkey, link)) + { + bool modified; + bool ksk = false, zsk = false; + isc_stdtime_t active = 0, published = 0, inactive = 0, + remove = 0; + isc_stdtime_t lastchange = 0, nextchange = 0; + dst_key_state_t dnskey_state = HIDDEN, zrrsig_state = HIDDEN, + goal_state = HIDDEN; + dst_key_state_t current_dnskey, current_zrrsig, current_goal; + + (void)dst_key_role(dkey->key, &ksk, &zsk); + if (ksk || !zsk) { + continue; + } + + keymgr_key_init(dkey, kasp, now, false); + + /* Get current metadata */ + RETERR(dst_key_getstate(dkey->key, DST_KEY_DNSKEY, + ¤t_dnskey)); + RETERR(dst_key_getstate(dkey->key, DST_KEY_ZRRSIG, + ¤t_zrrsig)); + RETERR(dst_key_getstate(dkey->key, DST_KEY_GOAL, + ¤t_goal)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_PUBLISH, + &published)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_ACTIVATE, &active)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_INACTIVE, + &inactive)); + RETERR(dst_key_gettime(dkey->key, DST_TIME_DELETE, &remove)); + + /* Determine key states from the metadata. */ + if (active <= now) { + dns_ttl_t ttlsig = dns_kasp_zonemaxttl(kasp, true); + ttlsig += dns_kasp_zonepropagationdelay(kasp); + if ((active + ttlsig) <= now) { + zrrsig_state = OMNIPRESENT; + } else { + zrrsig_state = RUMOURED; + (void)dst_key_gettime(dkey->key, + DST_TIME_ZRRSIG, + &lastchange); + nextchange = lastchange + ttlsig + + dns_kasp_retiresafety(kasp); + } + goal_state = OMNIPRESENT; + } + + if (published <= now) { + dns_ttl_t key_ttl = dst_key_getttl(dkey->key); + key_ttl += dns_kasp_zonepropagationdelay(kasp); + if ((published + key_ttl) <= now) { + dnskey_state = OMNIPRESENT; + } else { + dnskey_state = RUMOURED; + (void)dst_key_gettime(dkey->key, + DST_TIME_DNSKEY, + &lastchange); + nextchange = lastchange + key_ttl + + dns_kasp_publishsafety(kasp); + } + goal_state = OMNIPRESENT; + } + + if (inactive <= now) { + dns_ttl_t ttlsig = dns_kasp_zonemaxttl(kasp, true); + ttlsig += dns_kasp_zonepropagationdelay(kasp); + if ((inactive + ttlsig) <= now) { + zrrsig_state = HIDDEN; + } else { + zrrsig_state = UNRETENTIVE; + (void)dst_key_gettime(dkey->key, + DST_TIME_ZRRSIG, + &lastchange); + nextchange = lastchange + ttlsig + + dns_kasp_retiresafety(kasp); + } + goal_state = HIDDEN; + } + + if (remove <= now) { + dns_ttl_t key_ttl = dst_key_getttl(dkey->key); + key_ttl += dns_kasp_zonepropagationdelay(kasp); + if ((remove + key_ttl) <= now) { + dnskey_state = HIDDEN; + } else { + dnskey_state = UNRETENTIVE; + (void)dst_key_gettime(dkey->key, + DST_TIME_DNSKEY, + &lastchange); + nextchange = + lastchange + key_ttl + + dns_kasp_zonepropagationdelay(kasp); + } + zrrsig_state = HIDDEN; + goal_state = HIDDEN; + } + + if ((*nexttime == 0 || *nexttime > nextchange) && + nextchange > 0) + { + *nexttime = nextchange; + } + + /* Update key states if necessary. */ + if (goal_state != current_goal) { + dst_key_setstate(dkey->key, DST_KEY_GOAL, goal_state); + } + if (dnskey_state != current_dnskey) { + dst_key_setstate(dkey->key, DST_KEY_DNSKEY, + dnskey_state); + dst_key_settime(dkey->key, DST_TIME_DNSKEY, now); + } + if (zrrsig_state != current_zrrsig) { + dst_key_setstate(dkey->key, DST_KEY_ZRRSIG, + zrrsig_state); + dst_key_settime(dkey->key, DST_TIME_ZRRSIG, now); + if (zrrsig_state == RUMOURED) { + dkey->first_sign = true; + } + } + modified = dst_key_ismodified(dkey->key); + + if (modified) { + const char *directory = dst_key_directory(dkey->key); + if (directory == NULL) { + directory = "."; + } + + dns_dnssec_get_hints(dkey, now); + + RETERR(dst_key_tofile(dkey->key, options, directory)); + dst_key_setmodified(dkey->key, false); + + if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) { + continue; + } + dst_key_format(dkey->key, keystr, sizeof(keystr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(3), + "keymgr: DNSKEY %s (%s) " + "saved to directory %s, policy %s", + keystr, keymgr_keyrole(dkey->key), + directory, dns_kasp_getname(kasp)); + } + dst_key_setmodified(dkey->key, false); + } + + result = ISC_R_SUCCESS; + +failure: + if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(origin, namebuf, sizeof(namebuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(3), + "keymgr: %s (offline-ksk) done", namebuf); + } + return (result); +} diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 856a2c669e..a9c3d108da 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -22187,7 +22187,7 @@ zone_rekey(dns_zone_t *zone) { isc_result_totext(result)); } - if (kasp != NULL) { + if (kasp != NULL && !offlineksk) { /* * Check DS at parental agents. Clear ongoing checks. */ @@ -22227,6 +22227,15 @@ zone_rekey(dns_zone_t *zone) { goto failure; } } + } else if (offlineksk) { + /* + * With offline-ksk enabled we don't run the keymgr. + * Instead we derive the states from the timing metadata. + */ + dns_zone_lock_keyfiles(zone); + result = dns_keymgr_offline(&zone->origin, &keys, kasp, now, + &nexttime); + dns_zone_unlock_keyfiles(zone); } KASP_UNLOCK(kasp); @@ -22245,6 +22254,25 @@ zone_rekey(dns_zone_t *zone) { bool sane_diff, sane_dnskey; isc_stdtime_t when; + result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys, + &zone->origin, ttl, &diff, mctx, + dnssec_report); + /* + * Keys couldn't be updated for some reason; + * try again later. + */ + if (result != ISC_R_SUCCESS) { + dnssec_log(zone, ISC_LOG_ERROR, + "zone_rekey:couldn't update zone keys: %s", + isc_result_totext(result)); + goto failure; + } + + if (offlineksk) { + /* We can skip a lot of things */ + goto post_sync; + } + /* * Publish CDS/CDNSKEY DELETE records if the zone is * transitioning from secure to insecure. @@ -22310,20 +22338,6 @@ zone_rekey(dns_zone_t *zone) { digests = dns_kasp_digests(zone->defaultkasp); } - result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys, - &zone->origin, ttl, &diff, mctx, - dnssec_report); - /* - * Keys couldn't be updated for some reason; - * try again later. - */ - if (result != ISC_R_SUCCESS) { - dnssec_log(zone, ISC_LOG_ERROR, - "zone_rekey:couldn't update zone keys: %s", - isc_result_totext(result)); - goto failure; - } - /* * Update CDS / CDNSKEY records. */ @@ -22376,6 +22390,7 @@ zone_rekey(dns_zone_t *zone) { goto failure; } + post_sync: /* * See if any pre-existing keys have newly become active; * also, see if any new key is for a new algorithm, as in that