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 2190aa904f)
This commit is contained in:
Matthijs Mekking 2024-06-19 13:41:07 +02:00
parent 6f6d000103
commit 56814221fd
3 changed files with 227 additions and 15 deletions

View file

@ -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);

View file

@ -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,
&current_dnskey));
RETERR(dst_key_getstate(dkey->key, DST_KEY_ZRRSIG,
&current_zrrsig));
RETERR(dst_key_getstate(dkey->key, DST_KEY_GOAL,
&current_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);
}

View file

@ -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