diff --git a/bin/tests/system/kasp/ns4/named.conf.in b/bin/tests/system/kasp/ns4/named.conf.in index df02b2d462..ac4406371b 100644 --- a/bin/tests/system/kasp/ns4/named.conf.in +++ b/bin/tests/system/kasp/ns4/named.conf.in @@ -13,6 +13,8 @@ // NS4 +include "purgekeys.conf"; + key rndc_key { secret "1234abcd8765"; algorithm @DEFAULT_HMAC@; @@ -154,6 +156,12 @@ view "example1" { inline-signing no; file "example1.db"; }; + + zone "purgekeys.kasp" { + type primary; + file "purgekeys.kasp.example1.db"; + dnssec-policy "purgekeys"; + }; }; view "example2" { @@ -163,6 +171,12 @@ view "example2" { type primary; file "example2.db"; }; + + zone "purgekeys.kasp" { + type primary; + file "purgekeys.kasp.example2.db"; + dnssec-policy "purgekeys"; + }; }; view "example3" { diff --git a/bin/tests/system/kasp/ns4/purgekeys1.conf b/bin/tests/system/kasp/ns4/purgekeys1.conf new file mode 100644 index 0000000000..9845c8936c --- /dev/null +++ b/bin/tests/system/kasp/ns4/purgekeys1.conf @@ -0,0 +1,28 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-policy "purgekeys" { + keys { + ksk key-directory lifetime 0 algorithm 13; + zsk key-directory lifetime P30D algorithm 13; + }; + /* + * Initially set to 0, so no keys are purged. Keys that are no longer + * in use will still be in the zone's keyring, one per view. After + * reconfig the purge-keys value is set to 7 days, at least one key + * will be eligible for purging, and should be purged from both + * keyrings without issues. + */ + purge-keys 0; + //purge-keys P7D; +}; diff --git a/bin/tests/system/kasp/ns4/purgekeys2.conf b/bin/tests/system/kasp/ns4/purgekeys2.conf new file mode 100644 index 0000000000..62376c1fd7 --- /dev/null +++ b/bin/tests/system/kasp/ns4/purgekeys2.conf @@ -0,0 +1,21 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-policy "purgekeys" { + keys { + ksk key-directory lifetime 0 algorithm 13; + zsk key-directory lifetime P30D algorithm 13; + }; + //purge-keys 0; + purge-keys P7D; +}; diff --git a/bin/tests/system/kasp/ns4/setup.sh b/bin/tests/system/kasp/ns4/setup.sh index c488bc4588..2b1c9d7944 100644 --- a/bin/tests/system/kasp/ns4/setup.sh +++ b/bin/tests/system/kasp/ns4/setup.sh @@ -30,3 +30,22 @@ done cp example1.db.in example1.db cp example2.db.in example2.db + +# Regression test for GL #5315 +cp purgekeys1.conf purgekeys.conf +cp example1.db.in purgekeys.kasp.example1.db +cp example2.db.in purgekeys.kasp.example2.db + +zone="purgekeys.kasp" +H="HIDDEN" +O="OMNIPRESENT" +T="now-9mo" +# KSK omnipresent +KSK=$($KEYGEN -fk -a 13 -L 3600 $zone 2>keygen.out.$zone.1) +$SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" >settime.out.$zone.1 2>&1 +# ZSK omnipresent +ZSK1=$($KEYGEN -a 13 -L 3600 $zone 2>keygen.out.$zone.2) +$SETTIME -s -g $O -k $O $T -z $O $T "$ZSK1" >settime.out.$zone.2 2>&1 +# ZSK hidden (may be purged) +ZSK2=$($KEYGEN -a 13 -L 3600 $zone 2>keygen.out.$zone.2) +$SETTIME -s -g $H -k $H $T -z $H $T "$ZSK2" >settime.out.$zone.2 2>&1 diff --git a/bin/tests/system/kasp/tests_kasp.py b/bin/tests/system/kasp/tests_kasp.py index 542f7f496c..e6bc020554 100644 --- a/bin/tests/system/kasp/tests_kasp.py +++ b/bin/tests/system/kasp/tests_kasp.py @@ -78,6 +78,8 @@ pytestmark = pytest.mark.extra_artifacts( "ns*/policies/*.conf", "ns3/legacy-keys.*", "ns3/dynamic-signed-inline-signing.kasp.db.signed.signed", + "ns4/purgekeys.conf", + "ns4/purgekeys2.conf", ] ) @@ -1566,6 +1568,33 @@ def test_kasp_zsk_retired(servers): server.log.prohibit(msg) +def test_kasp_purge_keys(servers): + zone = "purgekeys.kasp" + server = servers["ns4"] + + tsig1 = ( + f"{os.environ['DEFAULT_HMAC']}:keyforview1:{KASP_INHERIT_TSIG_SECRET['view1']}" + ) + tsig2 = ( + f"{os.environ['DEFAULT_HMAC']}:keyforview2:{KASP_INHERIT_TSIG_SECRET['view2']}" + ) + + isctest.kasp.check_dnssec_verify(server, zone, tsig=tsig1) + isctest.kasp.check_dnssec_verify(server, zone, tsig=tsig2) + + # Reconfig, make sure the purged key is not an issue when verifying keys. + shutil.copyfile("ns4/purgekeys2.conf", "ns4/purgekeys.conf") + with server.watch_log_from_here() as watcher: + server.rndc("reconfig", log=False) + watcher.wait_for_line(f"keymgr: {zone} done") + + msg = f"zone {zone}/IN/example1 (signed): zone_rekey:zone_verifykeys failed: some key files are missing" + server.log.prohibit(msg) + + msg = f"zone {zone}/IN/example2 (signed): zone_rekey:zone_verifykeys failed: some key files are missing" + server.log.prohibit(msg) + + def test_kasp_reload_restart(servers): server = servers["ns6"] zone = "example" diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 5e52548d29..a794c9d5bb 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -2296,7 +2296,7 @@ dst_key_tkeytoken(const dst_key_t *key) { * */ bool -dst_key_is_unused(dst_key_t *key) { +dst_key_is_unused(const dst_key_t *key) { isc_stdtime_t val; dst_key_state_t st; int state_type; @@ -2598,7 +2598,7 @@ dst_key_is_removed(dst_key_t *key, isc_stdtime_t now, isc_stdtime_t *remove) { } dst_key_state_t -dst_key_goal(dst_key_t *key) { +dst_key_goal(const dst_key_t *key) { dst_key_state_t state; isc_result_t result; diff --git a/lib/dns/include/dns/keymgr.h b/lib/dns/include/dns/keymgr.h index 2e5b6572fb..bf24457e3c 100644 --- a/lib/dns/include/dns/keymgr.h +++ b/lib/dns/include/dns/keymgr.h @@ -158,3 +158,16 @@ dns_keymgr_status(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, *\li Printable status in 'out'. * */ + +bool +dns_keymgr_key_may_be_purged(const dst_key_t *key, uint32_t after, + isc_stdtime_t now); +/*%< + * Checks if the key files for 'key' may be removed from disk. + * + * Requires: + *\li 'key' is a valid key. + * + * Returns: + *\li true if the key files may be purged, false otherwise. + */ diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index 8ba5fe1360..25b5413856 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -1081,7 +1081,7 @@ dst_key_haskasp(dst_key_t *key); */ bool -dst_key_is_unused(dst_key_t *key); +dst_key_is_unused(const dst_key_t *key); /*%< * Check if this key is unused. * @@ -1138,7 +1138,7 @@ dst_key_is_removed(dst_key_t *key, isc_stdtime_t now, isc_stdtime_t *remove); */ dst_key_state_t -dst_key_goal(dst_key_t *key); +dst_key_goal(const dst_key_t *key); /*%< * Get the key goal. Should be OMNIPRESENT or HIDDEN. * This can be used to determine if the key is being introduced or diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index fb94cce729..26a2b2c1a1 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -605,7 +605,7 @@ keymgr_desiredstate(dns_dnsseckey_t *key, dst_key_state_t state) { * */ static bool -keymgr_key_match_state(dst_key_t *key, dst_key_t *subject, int type, +keymgr_key_match_state(const dst_key_t *key, const dst_key_t *subject, int type, dst_key_state_t next_state, dst_key_state_t states[NUM_KEYSTATES]) { REQUIRE(key != NULL); @@ -1930,8 +1930,9 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, return ISC_R_SUCCESS; } -static bool -keymgr_key_may_be_purged(dst_key_t *key, uint32_t after, isc_stdtime_t now) { +bool +dns_keymgr_key_may_be_purged(const dst_key_t *key, uint32_t after, + isc_stdtime_t now) { bool ksk = false; bool zsk = false; dst_key_state_t hidden[NUM_KEYSTATES] = { HIDDEN, NA, NA, NA }; @@ -2106,8 +2107,8 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, } /* Check purge-keys interval. */ - if (keymgr_key_may_be_purged(dkey->key, - dns_kasp_purgekeys(kasp), now)) + if (dns_keymgr_key_may_be_purged(dkey->key, + dns_kasp_purgekeys(kasp), now)) { dst_key_format(dkey->key, keystr, sizeof(keystr)); isc_log_write(DNS_LOGCATEGORY_DNSSEC, diff --git a/lib/dns/zone.c b/lib/dns/zone.c index ffe1b65887..2100a9810b 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -21974,7 +21974,8 @@ update_ttl(dns_rdataset_t *rdataset, dns_name_t *name, dns_ttl_t ttl, } static isc_result_t -zone_verifykeys(dns_zone_t *zone, dns_dnsseckeylist_t *newkeys) { +zone_verifykeys(dns_zone_t *zone, dns_dnsseckeylist_t *newkeys, + uint32_t purgeval, isc_stdtime_t now) { /* * Make sure that the existing keys are also present in the new keylist. */ @@ -21984,6 +21985,9 @@ zone_verifykeys(dns_zone_t *zone, dns_dnsseckeylist_t *newkeys) { if (dst_key_is_unused(key1->key)) { continue; } + if (dns_keymgr_key_may_be_purged(key1->key, purgeval, now)) { + continue; + } if (key1->purge) { continue; } @@ -22271,7 +22275,8 @@ zone_rekey(dns_zone_t *zone) { if (kasp != NULL && !offlineksk) { /* Verify new keys. */ - isc_result_t ret = zone_verifykeys(zone, &keys); + isc_result_t ret = zone_verifykeys( + zone, &keys, dns_kasp_purgekeys(kasp), now); if (ret != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:zone_verifykeys failed: "