diff --git a/bin/named/config.c b/bin/named/config.c index cb58713c8a..380c8ebc51 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -309,6 +309,7 @@ dnssec-policy \"default\" {\n\ cds-digest-types { 2; };\n\ dnskey-ttl " DNS_KASP_KEY_TTL ";\n\ inline-signing yes;\n\ + manual-mode no;\n\ offline-ksk no;\n\ publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\ retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\ @@ -327,6 +328,7 @@ dnssec-policy \"insecure\" {\n\ max-zone-ttl 0; \n\ keys { };\n\ inline-signing yes;\n\ + manual-mode no;\n\ };\n\ \n\ " diff --git a/bin/named/server.c b/bin/named/server.c index 7afd9af9cf..84fb21ec21 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -6612,7 +6612,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, * Ensure that zone keys are reloaded on reconfig */ if (dns_zone_getkasp(zone) != NULL) { - dns_zone_rekey(zone, fullsign); + dns_zone_rekey(zone, fullsign, false); } cleanup: @@ -12026,7 +12026,7 @@ named_server_rekey(named_server_t *server, isc_lex_t *lex, if (dns_zone_getkasp(zone) == NULL) { result = ISC_R_NOPERM; } else { - dns_zone_rekey(zone, fullsign); + dns_zone_rekey(zone, fullsign, false); } dns_zone_detach(&zone); @@ -14358,6 +14358,8 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, dns_dnsseckeylist_t keys; char *ptr, *zonetext = NULL; const char *msg = NULL; + /* variables for -step */ + bool forcestep = false; /* variables for -checkds */ bool checkds = false, dspublish = false; /* variables for -rollover */ @@ -14401,6 +14403,8 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, rollover = true; } else if (strcasecmp(ptr, "-checkds") == 0) { checkds = true; + } else if (strcasecmp(ptr, "-step") == 0) { + forcestep = true; } else { CHECK(DNS_R_SYNTAX); } @@ -14557,7 +14561,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, * Rekey after checkds command because the next key * event may have changed. */ - dns_zone_rekey(zone, false); + dns_zone_rekey(zone, false, false); if (use_keyid) { char tagbuf[6]; @@ -14607,7 +14611,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, * Rekey after rollover command because the next key * event may have changed. */ - dns_zone_rekey(zone, false); + dns_zone_rekey(zone, false, false); if (use_keyid) { char tagbuf[6]; @@ -14631,7 +14635,10 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, CHECK(putstr(text, isc_result_totext(ret))); break; } + } else if (forcestep) { + dns_zone_rekey(zone, false, true); } + CHECK(putnull(text)); cleanup: @@ -16094,7 +16101,7 @@ named_server_skr(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text) { CHECK(putnull(text)); } else { /* Schedule a rekey */ - dns_zone_rekey(zone, false); + dns_zone_rekey(zone, false, false); } cleanup: diff --git a/bin/tests/system/checkconf/good.conf.j2 b/bin/tests/system/checkconf/good.conf.j2 index 876b086787..fc33cb6503 100644 --- a/bin/tests/system/checkconf/good.conf.j2 +++ b/bin/tests/system/checkconf/good.conf.j2 @@ -27,6 +27,7 @@ dnssec-policy "test" { zsk lifetime P30D algorithm 13; csk key-store "hsm" lifetime P30D algorithm 8 2048; }; + manual-mode no; max-zone-ttl 86400; nsec3param ; parent-ds-ttl 7200; diff --git a/bin/tests/system/isctest/kasp.py b/bin/tests/system/isctest/kasp.py index 3419ffefec..b750cb1f78 100644 --- a/bin/tests/system/isctest/kasp.py +++ b/bin/tests/system/isctest/kasp.py @@ -829,7 +829,13 @@ def check_dnssecstatus(server, zone, keys, policy=None, view=None): def _check_signatures( - signatures, covers, fqdn, keys, offline_ksk=False, zsk_missing=False, smooth=False + signatures, + covers, + fqdn, + keys, + offline_ksk=False, + zsk_missing=False, + smooth=False, ): numsigs = 0 zrrsig = True @@ -917,7 +923,7 @@ def check_signatures( assert numsigs == len(signatures) -def _check_dnskeys(dnskeys, keys, cdnskey=False): +def _check_dnskeys(dnskeys, keys, cdnskey=False, manual_mode=False): now = KeyTimingMetadata.now() numkeys = 0 @@ -928,10 +934,21 @@ def _check_dnskeys(dnskeys, keys, cdnskey=False): delete_md = f"Sync{delete_md}" for key in keys: - publish = key.get_timing(publish_md, must_exist=False) - delete = key.get_timing(delete_md, must_exist=False) - published = publish is not None and now >= publish - removed = delete is not None and delete <= now + if manual_mode: + # State transitions may be blocked, preset key timings may not + # be accurate. Use the state values to determine whether the + # CDS must be published or not. + if cdnskey: + md = key.get_metadata("DSState") + else: + md = key.get_metadata("DNSKEYState") + published = md in ["omnipresent", "rumoured"] + removed = not published + else: + publish = key.get_timing(publish_md, must_exist=False) + delete = key.get_timing(delete_md, must_exist=False) + published = publish is not None and now >= publish + removed = delete is not None and delete <= now if not published or removed: for dnskey in dnskeys: @@ -954,7 +971,7 @@ def _check_dnskeys(dnskeys, keys, cdnskey=False): return numkeys -def check_dnskeys(rrset, ksks, zsks, cdnskey=False): +def check_dnskeys(rrset, ksks, zsks, cdnskey=False, manual_mode=False): # Check if the correct DNSKEY records are published. If the current time # is between the timing metadata 'publish' and 'delete', the key must have # a DNSKEY record published. If 'cdnskey' is True, check against CDNSKEY @@ -969,14 +986,14 @@ def check_dnskeys(rrset, ksks, zsks, cdnskey=False): dnskey = f"{rr.name} {rr.ttl} {rdclass} {rdtype} {rdata}" dnskeys.append(dnskey) - numkeys += _check_dnskeys(dnskeys, ksks, cdnskey=cdnskey) + numkeys += _check_dnskeys(dnskeys, ksks, cdnskey=cdnskey, manual_mode=manual_mode) if not cdnskey: - numkeys += _check_dnskeys(dnskeys, zsks) + numkeys += _check_dnskeys(dnskeys, zsks, manual_mode=manual_mode) assert numkeys == len(dnskeys) -def check_cds(cdss, keys, alg): +def check_cds(cdss, keys, alg, manual_mode=False): # Check if the correct CDS records are published. If the current time # is between the timing metadata 'publish' and 'delete', the key must have # a CDS record published. @@ -986,10 +1003,19 @@ def check_cds(cdss, keys, alg): for key in keys: assert key.is_ksk() - publish = key.get_timing("SyncPublish") - delete = key.get_timing("SyncDelete", must_exist=False) - published = now >= publish - removed = delete is not None and delete <= now + if manual_mode: + # State transitions may be blocked, preset key timings may not + # be accurate. Use the state values to determine whether the + # CDS must be published or not. + md = key.get_metadata("DSState") + published = md in ["omnipresent", "rumoured"] + removed = not published + else: + publish = key.get_timing("SyncPublish") + delete = key.get_timing("SyncDelete", must_exist=False) + published = now >= publish + removed = delete is not None and delete <= now + if not published or removed: for cds in cdss: assert not key.cds_equals(cds, alg) @@ -1069,6 +1095,7 @@ def check_apex( zsks, cdss=None, cds_delete=False, + manual_mode=False, offline_ksk=False, zsk_missing=False, tsig=None, @@ -1082,7 +1109,7 @@ def check_apex( # test dnskey query dnskeys, rrsigs = _query_rrset(server, fqdn, dns.rdatatype.DNSKEY, tsig=tsig) - check_dnskeys(dnskeys, ksks, zsks) + check_dnskeys(dnskeys, ksks, zsks, manual_mode=manual_mode) check_signatures( rrsigs, dns.rdatatype.DNSKEY, fqdn, ksks, zsks, offline_ksk=offline_ksk ) @@ -1108,7 +1135,7 @@ def check_apex( check_cdsdelete(cdnskeys, "0 3 0 AA==") else: if "CDNSKEY" in cdss: - check_dnskeys(cdnskeys, ksks, zsks, cdnskey=True) + check_dnskeys(cdnskeys, ksks, zsks, cdnskey=True, manual_mode=manual_mode) else: assert len(cdnskeys) == 0 @@ -1136,7 +1163,7 @@ def check_apex( for alg in ["SHA-256", "SHA-384"]: if f"CDS ({alg})" in cdss: - numcds += check_cds(cdsrrs, ksks, alg) + numcds += check_cds(cdsrrs, ksks, alg, manual_mode=manual_mode) else: check_cds_prohibit(cdsrrs, ksks, alg) @@ -1185,6 +1212,7 @@ def check_rollover_step(server, config, policy, step): cds_delete = step.get("cds-delete", False) check_keytimes_flag = step.get("check-keytimes", True) zone_signed = step.get("zone-signed", True) + manual_mode = step.get("manual-mode", False) isctest.log.info(f"check rollover step {zone}") @@ -1247,7 +1275,15 @@ def check_rollover_step(server, config, policy, step): check_keytimes(keys, expected) check_dnssecstatus(server, zone, keys, policy=policy) - check_apex(server, zone, ksks, zsks, cdss=cdss, cds_delete=cds_delete) + check_apex( + server, + zone, + ksks, + zsks, + cdss=cdss, + cds_delete=cds_delete, + manual_mode=manual_mode, + ) check_subdomain(server, zone, ksks, zsks, smooth=smooth) def check_next_key_event(): @@ -1256,6 +1292,8 @@ def check_rollover_step(server, config, policy, step): if nextev is not None: isctest.run.retry_with_timeout(check_next_key_event, timeout=5) + return expected + def verify_update_is_signed(server, fqdn, qname, qtype, rdata, ksks, zsks, tsig=None): """ diff --git a/bin/tests/system/kasp/ns3/named-fips.conf.in b/bin/tests/system/kasp/ns3/named-fips.conf.in index 109f3ad811..665b37821e 100644 --- a/bin/tests/system/kasp/ns3/named-fips.conf.in +++ b/bin/tests/system/kasp/ns3/named-fips.conf.in @@ -286,6 +286,15 @@ zone "keyfiles-missing.autosign" { dnssec-policy "autosign"; }; +/* + * Zone that has missing key files, manual-mode. + */ +zone "keyfiles-missing.manual" { + type primary; + file "keyfiles-missing.manual.db"; + dnssec-policy "manual"; +}; + /* * Zone that has missing private KSK. */ diff --git a/bin/tests/system/kasp/ns3/policies/autosign.conf.in b/bin/tests/system/kasp/ns3/policies/autosign.conf.in index c54786247f..9ccc4e62b9 100644 --- a/bin/tests/system/kasp/ns3/policies/autosign.conf.in +++ b/bin/tests/system/kasp/ns3/policies/autosign.conf.in @@ -24,3 +24,19 @@ dnssec-policy "autosign" { zsk key-directory lifetime P1Y algorithm @DEFAULT_ALGORITHM@; }; }; + +dnssec-policy "manual" { + + signatures-refresh P1W; + signatures-validity P2W; + signatures-validity-dnskey P2W; + + dnskey-ttl 300; + + keys { + ksk key-directory lifetime P2Y algorithm @DEFAULT_ALGORITHM@; + zsk key-directory lifetime P2M algorithm @DEFAULT_ALGORITHM@; + }; + + manual-mode yes; +}; diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index 4e521aa769..756c0af19c 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -261,6 +261,19 @@ private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" cp $infile $zonefile $SIGNER -S -x -s now-1w -e now+1w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +# These signatures are still good, but the key files will be removed +# before a second run of reconfiguring keys, now in manual-mode. +setup keyfiles-missing.manual +KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $keytimes $zone 2>keygen.out.$zone.1) +ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $keytimes $zone 2>keygen.out.$zone.2) +$SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" >settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" >settime.out.$zone.2 2>&1 +cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" +cp $infile $zonefile +$SIGNER -S -x -s now-1w -e now+1w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # These signatures are already expired, and the private ZSK is retired. setup zsk-retired.autosign zsktimes="$keytimes -I now" diff --git a/bin/tests/system/kasp/tests_kasp.py b/bin/tests/system/kasp/tests_kasp.py index f43f0dea6f..4e91565a78 100644 --- a/bin/tests/system/kasp/tests_kasp.py +++ b/bin/tests/system/kasp/tests_kasp.py @@ -118,6 +118,7 @@ lifetime = { "P1Y": int(timedelta(days=365).total_seconds()), "P30D": int(timedelta(days=30).total_seconds()), "P6M": int(timedelta(days=31 * 6).total_seconds()), + "P2M": int(timedelta(days=31 * 2).total_seconds()), } KASP_INHERIT_TSIG_SECRET = { @@ -157,10 +158,18 @@ def fips_properties(alg, bits=None): ] -def check_all(server, zone, policy, ksks, zsks, zsk_missing=False, tsig=None): +def check_all( + server, zone, policy, ksks, zsks, manual_mode=False, zsk_missing=False, tsig=None +): isctest.kasp.check_dnssecstatus(server, zone, ksks + zsks, policy=policy) isctest.kasp.check_apex( - server, zone, ksks, zsks, zsk_missing=zsk_missing, tsig=tsig + server, + zone, + ksks, + zsks, + manual_mode=manual_mode, + zsk_missing=zsk_missing, + tsig=tsig, ) isctest.kasp.check_subdomain(server, zone, ksks, zsks, tsig=tsig) @@ -1662,3 +1671,100 @@ def test_kasp_reload_restart(ns6): newttl = 400 isctest.run.retry_with_timeout(check_soa_ttl, timeout=10) + + +def test_kasp_manual_mode(ns3): + + keydir = ns3.identifier + zone = "keyfiles-missing.manual" + policy = "manual" + ttl = int(autosign_config["dnskey-ttl"].total_seconds()) + offset = -timedelta(days=30 * 6) + alg = os.environ["DEFAULT_ALGORITHM_NUMBER"] + size = os.environ["DEFAULT_BITS"] + keyprops = [ + f"ksk {lifetime['P2Y']} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent", + f"zsk {lifetime['P2M']} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent", + ] + + isctest.kasp.wait_keymgr_done(ns3, zone) + + isctest.log.info(f"check test case zone {zone} policy {policy}") + + # First make sure the zone is signed. + isctest.kasp.check_dnssec_verify(ns3, zone) + + # Key properties. + expected = isctest.kasp.policy_to_properties(ttl=ttl, keys=keyprops) + + # Key files. + keys = isctest.kasp.keydir_to_keylist(zone, keydir) + ksks = [k for k in keys if k.is_ksk()] + zsks = [k for k in keys if not k.is_ksk()] + isctest.kasp.check_dnssec_verify(ns3, zone) + isctest.kasp.check_keys(zone, keys, expected) + + for kp in expected: + kp.set_expected_keytimes(autosign_config, offset=offset) + + isctest.kasp.check_keytimes(keys, expected) + + check_all(ns3, zone, policy, ksks, zsks, manual_mode=True) + + # Key rollover should have been be blocked. + tag = expected[1].key.tag + blockmsg = f"keymgr-manual-mode: block ZSK rollover for key {zone}/ECDSAP256SHA256/{tag} (policy {policy})" + ns3.log.expect(blockmsg) + + # Remove files. + for key in ksks + zsks: + shutil.copyfile(key.privatefile, f"{key.privatefile}.backup") + shutil.copyfile(key.keyfile, f"{key.keyfile}.backup") + shutil.copyfile(key.statefile, f"{key.statefile}.backup") + + os.remove(key.keyfile) + os.remove(key.privatefile) + os.remove(key.statefile) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}", log=False) + watcher.wait_for_line( + f"zone {zone}/IN (signed): zone_rekey:zone_verifykeys failed: some key files are missing" + ) + + # Restore key files. + for key in ksks + zsks: + shutil.copyfile(f"{key.privatefile}.backup", key.privatefile) + shutil.copyfile(f"{key.keyfile}.backup", key.keyfile) + shutil.copyfile(f"{key.statefile}.backup", key.statefile) + + # Load keys. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"loadkeys {zone}", log=False) + watcher.wait_for_line(blockmsg) + + # Check keys again, make sure no new keys are created. + isctest.kasp.check_keys(zone, keys, expected) + isctest.kasp.check_keytimes(keys, expected) + check_all(ns3, zone, policy, ksks, zsks, manual_mode=True) + isctest.kasp.check_dnssec_verify(ns3, zone) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}", log=False) + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check keys again, make sure the rollover has started. + keyprops = [ + f"ksk {lifetime['P2Y']} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent", + f"zsk {lifetime['P2M']} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent", + f"zsk {lifetime['P2M']} {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:hidden", + ] + expected = isctest.kasp.policy_to_properties(ttl=ttl, keys=keyprops) + keys = isctest.kasp.keydir_to_keylist(zone, keydir) + ksks = [k for k in keys if k.is_ksk()] + zsks = [k for k in keys if not k.is_ksk()] + isctest.kasp.check_keys(zone, keys, expected) + check_all(ns3, zone, policy, ksks, zsks, manual_mode=True) + isctest.kasp.check_dnssec_verify(ns3, zone) diff --git a/bin/tests/system/rollover-algo-csk/ns6/csk1.conf.j2 b/bin/tests/system/rollover-algo-csk/ns6/csk1.conf.j2 index a5ff042db8..0af722acbf 100644 --- a/bin/tests/system/rollover-algo-csk/ns6/csk1.conf.j2 +++ b/bin/tests/system/rollover-algo-csk/ns6/csk1.conf.j2 @@ -11,7 +11,27 @@ * information regarding copyright ownership. */ -dnssec-policy "csk-algoroll" { +dnssec-policy "csk-algoroll-kasp" { + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + keys { + csk lifetime unlimited algorithm rsasha256; + }; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + zone-propagation-delay 3600; + max-zone-ttl 6h; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; + +dnssec-policy "csk-algoroll-manual" { + manual-mode yes; + signatures-refresh P5D; signatures-validity 30d; signatures-validity-dnskey 30d; diff --git a/bin/tests/system/rollover-algo-csk/ns6/csk2.conf.j2 b/bin/tests/system/rollover-algo-csk/ns6/csk2.conf.j2 index 6d290c3c32..5afdd1e774 100644 --- a/bin/tests/system/rollover-algo-csk/ns6/csk2.conf.j2 +++ b/bin/tests/system/rollover-algo-csk/ns6/csk2.conf.j2 @@ -11,7 +11,27 @@ * information regarding copyright ownership. */ -dnssec-policy "csk-algoroll" { +dnssec-policy "csk-algoroll-kasp" { + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + keys { + csk lifetime unlimited algorithm @DEFAULT_ALGORITHM@; + }; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + zone-propagation-delay 3600; + max-zone-ttl 6h; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; + +dnssec-policy "csk-algoroll-manual" { + manual-mode yes; + signatures-refresh P5D; signatures-validity 30d; signatures-validity-dnskey 30d; diff --git a/bin/tests/system/rollover-algo-csk/ns6/named.conf.j2 b/bin/tests/system/rollover-algo-csk/ns6/named.conf.j2 index d5e785bb27..8bcf2bf149 100644 --- a/bin/tests/system/rollover-algo-csk/ns6/named.conf.j2 +++ b/bin/tests/system/rollover-algo-csk/ns6/named.conf.j2 @@ -13,44 +13,47 @@ {% set csk_roll = csk_roll | default(False) %} {% set _csk_file = "csk1.conf" if not csk_roll else "csk2.conf" %} +{% set zones = ["kasp", "manual"] %} include "@_csk_file@"; include "named.common.conf"; -zone "step1.csk-algorithm-roll.kasp" { +{% for tld in zones %} +zone "step1.csk-algorithm-roll.@tld@" { type primary; - file "step1.csk-algorithm-roll.kasp.db"; - dnssec-policy "csk-algoroll"; + file "step1.csk-algorithm-roll.@tld@.db"; + dnssec-policy "csk-algoroll-@tld@"; }; {% if csk_roll %} -zone "step2.csk-algorithm-roll.kasp" { +zone "step2.csk-algorithm-roll.@tld@" { type primary; - file "step2.csk-algorithm-roll.kasp.db"; - dnssec-policy "csk-algoroll"; + file "step2.csk-algorithm-roll.@tld@.db"; + dnssec-policy "csk-algoroll-@tld@"; }; -zone "step3.csk-algorithm-roll.kasp" { +zone "step3.csk-algorithm-roll.@tld@" { type primary; - file "step3.csk-algorithm-roll.kasp.db"; - dnssec-policy "csk-algoroll"; + file "step3.csk-algorithm-roll.@tld@.db"; + dnssec-policy "csk-algoroll-@tld@"; }; -zone "step4.csk-algorithm-roll.kasp" { +zone "step4.csk-algorithm-roll.@tld@" { type primary; - file "step4.csk-algorithm-roll.kasp.db"; - dnssec-policy "csk-algoroll"; + file "step4.csk-algorithm-roll.@tld@.db"; + dnssec-policy "csk-algoroll-@tld@"; }; -zone "step5.csk-algorithm-roll.kasp" { +zone "step5.csk-algorithm-roll.@tld@" { type primary; - file "step5.csk-algorithm-roll.kasp.db"; - dnssec-policy "csk-algoroll"; + file "step5.csk-algorithm-roll.@tld@.db"; + dnssec-policy "csk-algoroll-@tld@"; }; -zone "step6.csk-algorithm-roll.kasp" { +zone "step6.csk-algorithm-roll.@tld@" { type primary; - file "step6.csk-algorithm-roll.kasp.db"; - dnssec-policy "csk-algoroll"; + file "step6.csk-algorithm-roll.@tld@.db"; + dnssec-policy "csk-algoroll-@tld@"; }; {% endif %} +{% endfor %} diff --git a/bin/tests/system/rollover-algo-csk/setup.sh b/bin/tests/system/rollover-algo-csk/setup.sh index 65318e6b94..200da1388e 100644 --- a/bin/tests/system/rollover-algo-csk/setup.sh +++ b/bin/tests/system/rollover-algo-csk/setup.sh @@ -30,121 +30,123 @@ O="OMNIPRESENT" U="UNRETENTIVE" # -# The zones at csk-algorithm-roll.kasp represent the various steps of a CSK +# The zones at csk-algorithm-roll.$tld represent the various steps of a CSK # algorithm rollover. # -# Step 1: -# Introduce the first key. This will immediately be active. -setup step1.csk-algorithm-roll.kasp -echo "$zone" >>zones -TactN="now-7d" -TsbmN="now-161h" -csktimes="-P ${TactN} -A ${TactN}" -CSK=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone 5 "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +for tld in kasp manual; do + # Step 1: + # Introduce the first key. This will immediately be active. + setup step1.csk-algorithm-roll.$tld + echo "$zone" >>zones + TactN="now-7d" + TsbmN="now-161h" + csktimes="-P ${TactN} -A ${TactN}" + CSK=$($KEYGEN -k csk-algoroll-$tld -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone 5 "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 2: -# After the publication interval has passed the DNSKEY is OMNIPRESENT. -setup step2.csk-algorithm-roll.kasp -# The time passed since the new algorithm keys have been introduced is 3 hours. -TpubN1="now-3h" -# Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h -TsbmN1="now+4h" -csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I now" -newtimes="-P ${TpubN1} -A ${TpubN1}" -CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-algoroll -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -z $R $TpubN1 -d $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${CSK1}.state" -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone 5 "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 2: + # After the publication interval has passed the DNSKEY is OMNIPRESENT. + setup step2.csk-algorithm-roll.$tld + # The time passed since the new algorithm keys have been introduced is 3 hours. + TpubN1="now-3h" + # Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h + TsbmN1="now+4h" + csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I now" + newtimes="-P ${TpubN1} -A ${TpubN1}" + CSK1=$($KEYGEN -k csk-algoroll-$tld -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-algoroll-$tld -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -z $R $TpubN1 -d $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${CSK1}.state" + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone 5 "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 3: -# The zone signatures are also OMNIPRESENT. -setup step3.csk-algorithm-roll.kasp -# The time passed since the new algorithm keys have been introduced is 7 hours. -TpubN1="now-7h" -TsbmN1="now" -ckstimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-algoroll -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -z $R $TpubN1 -d $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${CSK1}.state" -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone 5 "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 3: + # The zone signatures are also OMNIPRESENT. + setup step3.csk-algorithm-roll.$tld + # The time passed since the new algorithm keys have been introduced is 7 hours. + TpubN1="now-7h" + TsbmN1="now" + ckstimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + CSK1=$($KEYGEN -k csk-algoroll-$tld -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-algoroll-$tld -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -z $R $TpubN1 -d $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${CSK1}.state" + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone 5 "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 4: -# The DS is swapped and can become OMNIPRESENT. -setup step4.csk-algorithm-roll.kasp -# The time passed since the DS has been swapped is 3 hours. -TpubN1="now-10h" -TsbmN1="now-3h" -csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-algoroll -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $O $TsbmN1 -d $U $TsbmN1 -D ds $TsbmN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -z $O $TsbmN1 -d $R $TsbmN1 -P ds $TsbmN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${CSK1}.state" -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone 5 "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 4: + # The DS is swapped and can become OMNIPRESENT. + setup step4.csk-algorithm-roll.$tld + # The time passed since the DS has been swapped is 3 hours. + TpubN1="now-10h" + TsbmN1="now-3h" + csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + CSK1=$($KEYGEN -k csk-algoroll-$tld -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-algoroll-$tld -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $O $TsbmN1 -d $U $TsbmN1 -D ds $TsbmN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -z $O $TsbmN1 -d $R $TsbmN1 -P ds $TsbmN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${CSK1}.state" + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone 5 "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 5: -# The DNSKEY is removed long enough to be HIDDEN. -setup step5.csk-algorithm-roll.kasp -# The time passed since the DNSKEY has been removed is 2 hours. -TpubN1="now-12h" -TsbmN1="now-5h" -csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-algoroll -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $U $TactN -r $U $TactN -z $U $TsbmN1 -d $H $TsbmN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -z $O $TsbmN1 -d $O $TsbmN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${CSK1}.state" -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone 5 "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 5: + # The DNSKEY is removed long enough to be HIDDEN. + setup step5.csk-algorithm-roll.$tld + # The time passed since the DNSKEY has been removed is 2 hours. + TpubN1="now-12h" + TsbmN1="now-5h" + csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + CSK1=$($KEYGEN -k csk-algoroll-$tld -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-algoroll-$tld -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $U $TactN -r $U $TactN -z $U $TsbmN1 -d $H $TsbmN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -z $O $TsbmN1 -d $O $TsbmN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${CSK1}.state" + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone 5 "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 6: -# The RRSIGs have been removed long enough to be HIDDEN. -setup step6.csk-algorithm-roll.kasp -# Additional time passed: 7h. -TpubN1="now-19h" -TsbmN1="now-12h" -csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-algoroll -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $H $TactN -r $U $TactN -z $U $TactN -d $H $TsbmN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $O $TsubN1 -d $O $TsbmN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${CSK1}.state" -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone 5 "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 6: + # The RRSIGs have been removed long enough to be HIDDEN. + setup step6.csk-algorithm-roll.$tld + # Additional time passed: 7h. + TpubN1="now-19h" + TsbmN1="now-12h" + csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + CSK1=$($KEYGEN -k csk-algoroll-$tld -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-algoroll-$tld -l csk2.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $H $TactN -r $U $TactN -z $U $TactN -d $H $TsbmN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $O $TsubN1 -d $O $TsbmN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${CSK1}.state" + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone 5 "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py index 9db1f819fa..d8178f623e 100644 --- a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py +++ b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py @@ -11,7 +11,10 @@ # pylint: disable=redefined-outer-name,unused-import +import pytest + import isctest +from isctest.util import param from rollover.common import ( pytestmark, CDSS, @@ -21,10 +24,16 @@ from rollover.common import ( ) -def test_algoroll_csk_initial(ns6): +@pytest.mark.parametrize( + "tld, policy", + [ + param("kasp", "csk-algoroll"), + param("manual", "csk-algoroll-manual"), + ], +) +def test_algoroll_csk_initial(ns6, tld, policy): config = ALGOROLL_CONFIG - policy = "csk-algoroll" - zone = "step1.csk-algorithm-roll.kasp" + zone = f"step1.csk-algorithm-roll.{tld}" isctest.kasp.wait_keymgr_done(ns6, zone) diff --git a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py index e10d15a5af..38527ad5e0 100644 --- a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py +++ b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py @@ -15,6 +15,7 @@ import pytest import isctest from isctest.kasp import KeyTimingMetadata +from isctest.util import param from rollover.common import ( pytestmark, alg, @@ -28,6 +29,7 @@ from rollover.common import ( ALGOROLL_KEYTTLPROP, ALGOROLL_OFFSETS, ALGOROLL_OFFVAL, + DURATION, TIMEDELTA, ) @@ -50,11 +52,45 @@ def reconfigure(ns6, templates): TIME_PASSED = KeyTimingMetadata.now().value - start_time.value -def test_algoroll_csk_reconfig_step1(ns6, alg, size): - zone = "step1.csk-algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_csk_reconfig_step1(tld, ns6, alg, size): + zone = f"step1.csk-algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + if tld == "manual": + # Same as initial. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk 0 8 2048 goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{-DURATION['P7D']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) + + # Check logs. + tag = keys[0].key.tag + msg1 = f"keymgr-manual-mode: block retire DNSKEY {zone}/RSASHA256/{tag} (CSK)" + msg2 = f"keymgr-manual-mode: block new key generation for zone {zone} (policy {policy})" + ns6.log.expect(msg1) + ns6.log.expect(msg2) + + # Force step. + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check state after step. step = { "zone": zone, "cdss": CDSS, @@ -67,14 +103,24 @@ def test_algoroll_csk_reconfig_step1(ns6, alg, size): # Next key event is when the ecdsa256 keys have been propagated. "nextev": ALGOROLL_IPUB, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_csk_reconfig_step2(ns6, alg, size): - zone = "step2.csk-algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_csk_reconfig_step2(tld, ns6, alg, size): + zone = f"step2.csk-algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -94,14 +140,58 @@ def test_algoroll_csk_reconfig_step2(ns6, alg, size): # the time passed between key creation and invoking 'rndc reconfig'. "nextev": ALGOROLL_IPUBC - ALGOROLL_IPUB - TIME_PASSED, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_csk_reconfig_step3(ns6, alg, size): - zone = "step3.csk-algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_csk_reconfig_step3(tld, ns6, alg, size): + zone = f"step3.csk-algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + if tld == "manual": + # Same as step 2, but the zone signatures have become OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFVAL}", + f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:hidden offset:{ALGOROLL_OFFSETS['step3']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type DS state HIDDEN to state RUMOURED" + ns6.log.expect(msg) + + # Force step. + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/RSASHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE" + if msg in ns6.log: + # Force step. + isctest.log.debug( + f"keymgr-manual-mode blocking transition CSK {zone}/RSASHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE, step again" + ) + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -114,14 +204,46 @@ def test_algoroll_csk_reconfig_step3(ns6, alg, size): # after the publication interval of the parent side. "nextev": ALGOROLL_IRETKSK - TIME_PASSED, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_csk_reconfig_step4(ns6, alg, size): - zone = "step4.csk-algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_csk_reconfig_step4(tld, ns6, alg, size): + zone = f"step4.csk-algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + if tld == "manual": + # Same as step 3, but the DS has become HIDDEN/OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:hidden offset:{ALGOROLL_OFFVAL}", + f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFSETS['step4']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/RSASHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE" + ns6.log.expect(msg) + + # Force step. + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -134,14 +256,24 @@ def test_algoroll_csk_reconfig_step4(ns6, alg, size): # This happens after the DNSKEY TTL plus zone propagation delay. "nextev": ALGOROLL_KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_csk_reconfig_step5(ns6, alg, size): - zone = "step5.csk-algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_csk_reconfig_step5(tld, ns6, alg, size): + zone = f"step5.csk-algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -158,14 +290,24 @@ def test_algoroll_csk_reconfig_step5(ns6, alg, size): # between key creation and invoking 'rndc reconfig'. "nextev": ALGOROLL_IRET - ALGOROLL_IRETKSK - ALGOROLL_KEYTTLPROP - TIME_PASSED, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_csk_reconfig_step6(ns6, alg, size): - zone = "step6.csk-algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_csk_reconfig_step6(tld, ns6, alg, size): + zone = f"step6.csk-algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -179,4 +321,4 @@ def test_algoroll_csk_reconfig_step6(ns6, alg, size): # loadkeys interval. "nextev": TIMEDELTA["PT1H"], } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) diff --git a/bin/tests/system/rollover-algo-ksk-zsk/ns6/kasp.conf.j2 b/bin/tests/system/rollover-algo-ksk-zsk/ns6/kasp.conf.j2 index ac3089fe90..4c85e1a03a 100644 --- a/bin/tests/system/rollover-algo-ksk-zsk/ns6/kasp.conf.j2 +++ b/bin/tests/system/rollover-algo-ksk-zsk/ns6/kasp.conf.j2 @@ -11,7 +11,7 @@ * information regarding copyright ownership. */ -dnssec-policy "rsasha256" { +dnssec-policy "rsasha256-kasp" { signatures-refresh P5D; signatures-validity 30d; signatures-validity-dnskey 30d; @@ -30,7 +30,49 @@ dnssec-policy "rsasha256" { parent-ds-ttl 7200; }; -dnssec-policy "ecdsa256" { +dnssec-policy "rsasha256-manual" { + manual-mode yes; + + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + keys { + ksk lifetime unlimited algorithm rsasha256; + zsk lifetime unlimited algorithm rsasha256; + }; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + zone-propagation-delay 3600; + max-zone-ttl 6h; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; + +dnssec-policy "ecdsa256-kasp" { + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + keys { + ksk lifetime unlimited algorithm ecdsa256; + zsk lifetime unlimited algorithm ecdsa256; + }; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + zone-propagation-delay 3600; + max-zone-ttl 6h; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; + +dnssec-policy "ecdsa256-manual" { + manual-mode yes; + signatures-refresh P5D; signatures-validity 30d; signatures-validity-dnskey 30d; diff --git a/bin/tests/system/rollover-algo-ksk-zsk/ns6/named.conf.j2 b/bin/tests/system/rollover-algo-ksk-zsk/ns6/named.conf.j2 index 02ce2368c5..b338623caa 100644 --- a/bin/tests/system/rollover-algo-ksk-zsk/ns6/named.conf.j2 +++ b/bin/tests/system/rollover-algo-ksk-zsk/ns6/named.conf.j2 @@ -13,44 +13,48 @@ {% set alg_roll = alg_roll | default(False) %} {% set policy = "rsasha256" if not alg_roll else "ecdsa256" %} +{% set zones = ["kasp", "manual"] %} include "kasp.conf"; include "named.common.conf"; -zone "step1.algorithm-roll.kasp" { +{% for tld in zones %} +zone "step1.algorithm-roll.@tld@" { type primary; - file "step1.algorithm-roll.kasp.db"; - dnssec-policy @policy@; + file "step1.algorithm-roll.@tld@.db"; + dnssec-policy @policy@-@tld@; }; {% if alg_roll %} -zone "step2.algorithm-roll.kasp" { +zone "step2.algorithm-roll.@tld@" { type primary; - file "step2.algorithm-roll.kasp.db"; - dnssec-policy "ecdsa256"; + file "step2.algorithm-roll.@tld@.db"; + dnssec-policy "ecdsa256-@tld@"; }; -zone "step3.algorithm-roll.kasp" { +zone "step3.algorithm-roll.@tld@" { type primary; - file "step3.algorithm-roll.kasp.db"; - dnssec-policy "ecdsa256"; + file "step3.algorithm-roll.@tld@.db"; + dnssec-policy "ecdsa256-@tld@"; }; -zone "step4.algorithm-roll.kasp" { +zone "step4.algorithm-roll.@tld@" { type primary; - file "step4.algorithm-roll.kasp.db"; - dnssec-policy "ecdsa256"; + file "step4.algorithm-roll.@tld@.db"; + dnssec-policy "ecdsa256-@tld@"; }; -zone "step5.algorithm-roll.kasp" { +zone "step5.algorithm-roll.@tld@" { type primary; - file "step5.algorithm-roll.kasp.db"; - dnssec-policy "ecdsa256"; + file "step5.algorithm-roll.@tld@.db"; + dnssec-policy "ecdsa256-@tld@"; }; -zone "step6.algorithm-roll.kasp" { +zone "step6.algorithm-roll.@tld@" { type primary; - file "step6.algorithm-roll.kasp.db"; - dnssec-policy "ecdsa256"; + file "step6.algorithm-roll.@tld@.db"; + dnssec-policy "ecdsa256-@tld@"; }; + {% endif %} +{% endfor %} diff --git a/bin/tests/system/rollover-algo-ksk-zsk/setup.sh b/bin/tests/system/rollover-algo-ksk-zsk/setup.sh index 5f7a370f14..db27d74e71 100644 --- a/bin/tests/system/rollover-algo-ksk-zsk/setup.sh +++ b/bin/tests/system/rollover-algo-ksk-zsk/setup.sh @@ -30,170 +30,172 @@ O="OMNIPRESENT" U="UNRETENTIVE" # -# The zones at algorithm-roll.kasp represent the various steps of a ZSK/KSK +# The zones at algorithm-roll.$tld represent the various steps of a ZSK/KSK # algorithm rollover. # -# Step 1: -# Introduce the first key. This will immediately be active. -setup step1.algorithm-roll.kasp -echo "$zone" >>zones -TactN="now-7d" -TsbmN="now-161h" -ksktimes="-P ${TactN} -A ${TactN}" -zsktimes="-P ${TactN} -A ${TactN}" -KSK=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a RSASHA256 -L 3600 $zsktimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 -cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" -private_type_record $zone 8 "$KSK" >>"$infile" -private_type_record $zone 8 "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +for tld in kasp manual; do + # Step 1: + # Introduce the first key. This will immediately be active. + setup step1.algorithm-roll.$tld + echo "$zone" >>zones + TactN="now-7d" + TsbmN="now-161h" + ksktimes="-P ${TactN} -A ${TactN}" + zsktimes="-P ${TactN} -A ${TactN}" + KSK=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) + ZSK=$($KEYGEN -a RSASHA256 -L 3600 $zsktimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 + cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" + private_type_record $zone 8 "$KSK" >>"$infile" + private_type_record $zone 8 "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 2: -# After the publication interval has passed the DNSKEY is OMNIPRESENT. -setup step2.algorithm-roll.kasp -# The time passed since the new algorithm keys have been introduced is 3 hours. -TpubN1="now-3h" -# Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h -TsbmN1="now+4h" -ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" -ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -zsk2times="-P ${TpubN1} -A ${TpubN1}" -KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.3 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${KSK1}.state" -echo "Lifetime: 0" >>"${ZSK1}.state" -cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone 8 "$KSK1" >>"$infile" -private_type_record $zone 8 "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 2: + # After the publication interval has passed the DNSKEY is OMNIPRESENT. + setup step2.algorithm-roll.$tld + # The time passed since the new algorithm keys have been introduced is 3 hours. + TpubN1="now-3h" + # Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h + TsbmN1="now+4h" + ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" + ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + zsk2times="-P ${TpubN1} -A ${TpubN1}" + KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.3 2>&1 + $SETTIME -s -g $O -k $R $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${KSK1}.state" + echo "Lifetime: 0" >>"${ZSK1}.state" + cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone 8 "$KSK1" >>"$infile" + private_type_record $zone 8 "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 3: -# The zone signatures are also OMNIPRESENT. -setup step3.algorithm-roll.kasp -# The time passed since the new algorithm keys have been introduced is 7 hours. -TpubN1="now-7h" -TsbmN1="now" -ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" -ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -zsk2times="-P ${TpubN1} -A ${TpubN1}" -KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.3 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${KSK1}.state" -echo "Lifetime: 0" >>"${ZSK1}.state" -cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone 8 "$KSK1" >>"$infile" -private_type_record $zone 8 "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 3: + # The zone signatures are also OMNIPRESENT. + setup step3.algorithm-roll.$tld + # The time passed since the new algorithm keys have been introduced is 7 hours. + TpubN1="now-7h" + TsbmN1="now" + ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" + ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + zsk2times="-P ${TpubN1} -A ${TpubN1}" + KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.3 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${KSK1}.state" + echo "Lifetime: 0" >>"${ZSK1}.state" + cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone 8 "$KSK1" >>"$infile" + private_type_record $zone 8 "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 4: -# The DS is swapped and can become OMNIPRESENT. -setup step4.algorithm-roll.kasp -# The time passed since the DS has been swapped is 3 hours. -TpubN1="now-10h" -TsbmN1="now-3h" -ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" -ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -zsk2times="-P ${TpubN1} -A ${TpubN1}" -KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $U $TsbmN1 -D ds $TsbmN1 "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $R $TsbmN1 -P ds $TsbmN1 "$KSK2" >settime.out.$zone.3 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${KSK1}.state" -echo "Lifetime: 0" >>"${ZSK1}.state" -cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone 8 "$KSK1" >>"$infile" -private_type_record $zone 8 "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 4: + # The DS is swapped and can become OMNIPRESENT. + setup step4.algorithm-roll.$tld + # The time passed since the DS has been swapped is 3 hours. + TpubN1="now-10h" + TsbmN1="now-3h" + ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" + ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + zsk2times="-P ${TpubN1} -A ${TpubN1}" + KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $U $TsbmN1 -D ds $TsbmN1 "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $R $TsbmN1 -P ds $TsbmN1 "$KSK2" >settime.out.$zone.3 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${KSK1}.state" + echo "Lifetime: 0" >>"${ZSK1}.state" + cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone 8 "$KSK1" >>"$infile" + private_type_record $zone 8 "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 5: -# The DNSKEY is removed long enough to be HIDDEN. -setup step5.algorithm-roll.kasp -# The time passed since the DNSKEY has been removed is 2 hours. -TpubN1="now-12h" -TsbmN1="now-5h" -ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" -ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -zsk2times="-P ${TpubN1} -A ${TpubN1}" -KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) -$SETTIME -s -g $H -k $U $TsbmN1 -r $U $TsbmN1 -d $H $TsbmN1 "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $U $TsbmN1 -z $U $TsbmN1 "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $O $TsbmN1 "$KSK2" >settime.out.$zone.3 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${KSK1}.state" -echo "Lifetime: 0" >>"${ZSK1}.state" -cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone 8 "$KSK1" >>"$infile" -private_type_record $zone 8 "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 5: + # The DNSKEY is removed long enough to be HIDDEN. + setup step5.algorithm-roll.$tld + # The time passed since the DNSKEY has been removed is 2 hours. + TpubN1="now-12h" + TsbmN1="now-5h" + ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" + ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + zsk2times="-P ${TpubN1} -A ${TpubN1}" + KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) + $SETTIME -s -g $H -k $U $TsbmN1 -r $U $TsbmN1 -d $H $TsbmN1 "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $U $TsbmN1 -z $U $TsbmN1 "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $O $TsbmN1 "$KSK2" >settime.out.$zone.3 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${KSK1}.state" + echo "Lifetime: 0" >>"${ZSK1}.state" + cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone 8 "$KSK1" >>"$infile" + private_type_record $zone 8 "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 6: -# The RRSIGs have been removed long enough to be HIDDEN. -setup step6.algorithm-roll.kasp -# Additional time passed: 7h. -TpubN1="now-19h" -TsbmN1="now-12h" -ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" -zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" -ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" -zsk2times="-P ${TpubN1} -A ${TpubN1}" -KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) -$SETTIME -s -g $H -k $H $TsbmN1 -r $U $TsbmN1 -d $H $TsbmN1 "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $H $TsbmN1 -z $U $TsbmN1 "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $O $TsbmN1 "$KSK2" >settime.out.$zone.3 2>&1 -$SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 -# Fake lifetime of old algorithm keys. -echo "Lifetime: 0" >>"${KSK1}.state" -echo "Lifetime: 0" >>"${ZSK1}.state" -cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone 8 "$KSK1" >>"$infile" -private_type_record $zone 8 "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 6: + # The RRSIGs have been removed long enough to be HIDDEN. + setup step6.algorithm-roll.$tld + # Additional time passed: 7h. + TpubN1="now-19h" + TsbmN1="now-12h" + ksk1times="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" + zsk1times="-P ${TactN} -A ${TactN} -I ${TsbmN1}" + ksk2times="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" + zsk2times="-P ${TpubN1} -A ${TpubN1}" + KSK1=$($KEYGEN -a RSASHA256 -L 3600 -f KSK $ksk1times $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a RSASHA256 -L 3600 $zsk1times $zone 2>keygen.out.$zone.2) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksk2times $zone 2>keygen.out.$zone.3) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsk2times $zone 2>keygen.out.$zone.4) + $SETTIME -s -g $H -k $H $TsbmN1 -r $U $TsbmN1 -d $H $TsbmN1 "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $H $TsbmN1 -z $U $TsbmN1 "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -r $O $TpubN1 -d $O $TsbmN1 "$KSK2" >settime.out.$zone.3 2>&1 + $SETTIME -s -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" >settime.out.$zone.4 2>&1 + # Fake lifetime of old algorithm keys. + echo "Lifetime: 0" >>"${KSK1}.state" + echo "Lifetime: 0" >>"${ZSK1}.state" + cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone 8 "$KSK1" >>"$infile" + private_type_record $zone 8 "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py index d1e54a05a7..1617a40b5b 100644 --- a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py +++ b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py @@ -11,7 +11,10 @@ # pylint: disable=unused-import +import pytest + import isctest +from isctest.util import param from rollover.common import ( pytestmark, CDSS, @@ -21,10 +24,17 @@ from rollover.common import ( ) -def test_algoroll_ksk_zsk_initial(ns6): +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_ksk_zsk_initial(ns6, tld): config = ALGOROLL_CONFIG - policy = "rsasha256" - zone = "step1.algorithm-roll.kasp" + policy = f"rsasha256-{tld}" + zone = f"step1.algorithm-roll.{tld}" isctest.kasp.wait_keymgr_done(ns6, zone) diff --git a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py index 6058780de0..9646dce7af 100644 --- a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py +++ b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py @@ -15,6 +15,7 @@ import pytest import isctest from isctest.kasp import KeyTimingMetadata +from isctest.util import param from rollover.common import ( pytestmark, alg, @@ -28,6 +29,7 @@ from rollover.common import ( ALGOROLL_KEYTTLPROP, ALGOROLL_OFFSETS, ALGOROLL_OFFVAL, + DURATION, TIMEDELTA, ) @@ -50,11 +52,48 @@ def reconfigure(ns6, templates): TIME_PASSED = KeyTimingMetadata.now().value - start_time.value -def test_algoroll_ksk_zsk_reconfig_step1(ns6, alg, size): - zone = "step1.algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_ksk_zsk_reconfig_step1(tld, ns6, alg, size): + zone = f"step1.algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + if tld == "manual": + # Same as initial. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"ksk 0 8 2048 goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{-DURATION['P7D']}", + f"zsk 0 8 2048 goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{-DURATION['P7D']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) + + # Check logs. + ktag = keys[0].key.tag + ztag = keys[1].key.tag + msg1 = f"keymgr-manual-mode: block retire DNSKEY {zone}/RSASHA256/{ktag} (KSK)" + msg2 = f"keymgr-manual-mode: block retire DNSKEY {zone}/RSASHA256/{ztag} (ZSK)" + msg3 = f"keymgr-manual-mode: block new key generation for zone {zone} (policy {policy})" # twice + ns6.log.expect(msg1) + ns6.log.expect(msg2) + ns6.log.expect(msg3) + + # Force step. + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -69,14 +108,24 @@ def test_algoroll_ksk_zsk_reconfig_step1(ns6, alg, size): # Next key event is when the ecdsa256 keys have been propagated. "nextev": ALGOROLL_IPUB, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_ksk_zsk_reconfig_step2(ns6, alg, size): - zone = "step2.algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_ksk_zsk_reconfig_step2(tld, ns6, alg, size): + zone = f"step2.algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -98,14 +147,60 @@ def test_algoroll_ksk_zsk_reconfig_step2(ns6, alg, size): # key creation and invoking 'rndc reconfig'. "nextev": ALGOROLL_IPUBC - ALGOROLL_IPUB - TIME_PASSED, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_ksk_zsk_reconfig_step3(ns6, alg, size): - zone = "step3.algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_ksk_zsk_reconfig_step3(tld, ns6, alg, size): + zone = f"step3.algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + if tld == "manual": + # Same as step 2, but the zone signatures have become OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"ksk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFVAL}", + f"zsk 0 8 2048 goal:hidden dnskey:omnipresent zrrsig:omnipresent offset:{ALGOROLL_OFFVAL}", + f"ksk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:hidden offset:{ALGOROLL_OFFSETS['step3']}", + f"zsk 0 {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{ALGOROLL_OFFSETS['step3']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) + + # Check logs. + tag = keys[2].key.tag + msg = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type DS state HIDDEN to state RUMOURED" + ns6.log.expect(msg) + + # Force step. + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block transition KSK {zone}/RSASHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE" + if msg in ns6.log: + # Force step. + isctest.log.debug( + f"keymgr-manual-mode blocking transition CSK {zone}/RSASHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE, step again" + ) + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -120,14 +215,51 @@ def test_algoroll_ksk_zsk_reconfig_step3(ns6, alg, size): # after the retire interval. "nextev": ALGOROLL_IRETKSK - TIME_PASSED, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_ksk_zsk_reconfig_step4(ns6, alg, size): - zone = "step4.algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_ksk_zsk_reconfig_step4(tld, ns6, alg, size): + zone = f"step4.algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + if tld == "manual": + # Same as step 3, but the DS has become HIDDEN/OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"ksk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent ds:hidden offset:{ALGOROLL_OFFVAL}", + f"zsk 0 8 2048 goal:hidden dnskey:omnipresent zrrsig:omnipresent offset:{ALGOROLL_OFFVAL}", + f"ksk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFSETS['step4']}", + f"zsk 0 {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{ALGOROLL_OFFSETS['step4']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) + + # Check logs. + ktag = keys[0].key.tag + ztag = keys[1].key.tag + msg1 = f"keymgr-manual-mode: block transition KSK {zone}/RSASHA256/{ktag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE" + msg2 = f"keymgr-manual-mode: block transition ZSK {zone}/RSASHA256/{ztag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE" + ns6.log.expect(msg1) + ns6.log.expect(msg2) + + # Force step. + with ns6.watch_log_from_here() as watcher: + ns6.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -142,14 +274,24 @@ def test_algoroll_ksk_zsk_reconfig_step4(ns6, alg, size): # This happens after the DNSKEY TTL plus zone propagation delay. "nextev": ALGOROLL_KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_ksk_zsk_reconfig_step5(ns6, alg, size): - zone = "step5.algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_ksk_zsk_reconfig_step5(tld, ns6, alg, size): + zone = f"step5.algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -168,14 +310,24 @@ def test_algoroll_ksk_zsk_reconfig_step5(ns6, alg, size): # between key creation and invoking 'rndc reconfig'. "nextev": ALGOROLL_IRET - ALGOROLL_IRETKSK - ALGOROLL_KEYTTLPROP - TIME_PASSED, } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) -def test_algoroll_ksk_zsk_reconfig_step6(ns6, alg, size): - zone = "step6.algorithm-roll.kasp" +@pytest.mark.parametrize( + "tld", + [ + param("kasp"), + param("manual"), + ], +) +def test_algoroll_ksk_zsk_reconfig_step6(tld, ns6, alg, size): + zone = f"step6.algorithm-roll.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -191,4 +343,4 @@ def test_algoroll_ksk_zsk_reconfig_step6(ns6, alg, size): # loadkeys interval. "nextev": TIMEDELTA["PT1H"], } - isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step) diff --git a/bin/tests/system/rollover-csk-roll1/ns3/kasp.conf.j2 b/bin/tests/system/rollover-csk-roll1/ns3/kasp.conf.j2 index 30427737fc..e6defeded2 100644 --- a/bin/tests/system/rollover-csk-roll1/ns3/kasp.conf.j2 +++ b/bin/tests/system/rollover-csk-roll1/ns3/kasp.conf.j2 @@ -11,7 +11,31 @@ * information regarding copyright ownership. */ -dnssec-policy "csk-roll1" { +dnssec-policy "csk-roll1-autosign" { + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + purge-keys PT1H; + + cds-digest-types { "sha-384"; }; // use a different digest type for testing purposes + keys { + csk key-directory lifetime P6M algorithm @DEFAULT_ALGORITHM@; + }; + + zone-propagation-delay 1h; + max-zone-ttl P1D; + + parent-ds-ttl 1h; + parent-propagation-delay 1h; +}; + +dnssec-policy "csk-roll1-manual" { + manual-mode yes; + signatures-refresh P5D; signatures-validity 30d; signatures-validity-dnskey 30d; diff --git a/bin/tests/system/rollover-csk-roll1/ns3/named.conf.j2 b/bin/tests/system/rollover-csk-roll1/ns3/named.conf.j2 index 2c4764e280..93c9882531 100644 --- a/bin/tests/system/rollover-csk-roll1/ns3/named.conf.j2 +++ b/bin/tests/system/rollover-csk-roll1/ns3/named.conf.j2 @@ -11,46 +11,51 @@ * information regarding copyright ownership. */ +{% set zones = ["autosign", "manual"] %} + include "kasp.conf"; include "named.common.conf"; -zone "step1.csk-roll1.autosign" { +{% for tld in zones %} +zone "step1.csk-roll1.@tld@" { type primary; - file "step1.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step1.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; -zone "step2.csk-roll1.autosign" { +zone "step2.csk-roll1.@tld@" { type primary; - file "step2.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step2.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; -zone "step3.csk-roll1.autosign" { +zone "step3.csk-roll1.@tld@" { type primary; - file "step3.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step3.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; -zone "step4.csk-roll1.autosign" { +zone "step4.csk-roll1.@tld@" { type primary; - file "step4.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step4.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; -zone "step5.csk-roll1.autosign" { +zone "step5.csk-roll1.@tld@" { type primary; - file "step5.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step5.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; -zone "step6.csk-roll1.autosign" { +zone "step6.csk-roll1.@tld@" { type primary; - file "step6.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step6.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; -zone "step7.csk-roll1.autosign" { +zone "step7.csk-roll1.@tld@" { type primary; - file "step7.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step7.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; -zone "step8.csk-roll1.autosign" { +zone "step8.csk-roll1.@tld@" { type primary; - file "step8.csk-roll1.autosign.db"; - dnssec-policy "csk-roll1"; + file "step8.csk-roll1.@tld@.db"; + dnssec-policy "csk-roll1-@tld@"; }; + +{% endfor %} diff --git a/bin/tests/system/rollover-csk-roll1/setup.sh b/bin/tests/system/rollover-csk-roll1/setup.sh index 755876feda..088add4121 100644 --- a/bin/tests/system/rollover-csk-roll1/setup.sh +++ b/bin/tests/system/rollover-csk-roll1/setup.sh @@ -40,273 +40,275 @@ O="OMNIPRESENT" U="UNRETENTIVE" # -# The zones at csk-roll1.autosign represent the various steps of a CSK rollover +# The zones at csk-roll1.$tld represent the various steps of a CSK rollover # (which is essentially a ZSK Pre-Publication / KSK Double-KSK rollover). # -# Step 1: -# Introduce the first key. This will immediately be active. -setup step1.csk-roll1.autosign -TactN="now-7d" -keytimes="-P ${TactN} -A ${TactN}" -CSK=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +for tld in autosign manual; do + # Step 1: + # Introduce the first key. This will immediately be active. + setup step1.csk-roll1.$tld + TactN="now-7d" + keytimes="-P ${TactN} -A ${TactN}" + CSK=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 2: -# It is time to introduce the new CSK. -setup step2.csk-roll1.autosign -# According to RFC 7583: -# KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC -# ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub -# IpubC = DprpC + TTLkey (+publish-safety) -# Ipub = IpubC -# Lcsk = Lksk = Lzsk -# -# Lcsk: 6mo (186d, 4464h) -# Dreg: N/A -# DprpC: 1h -# TTLkey: 1h -# publish-safety: 1h -# Ipub: 3h -# -# Tact(N) = now - Lcsk + Ipub = now - 186d + 3h -# = now - 4464h + 3h = now - 4461h -TactN="now-4461h" -keytimes="-P ${TactN} -A ${TactN}" -CSK=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 2: + # It is time to introduce the new CSK. + setup step2.csk-roll1.$tld + # According to RFC 7583: + # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC + # ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub + # IpubC = DprpC + TTLkey (+publish-safety) + # Ipub = IpubC + # Lcsk = Lksk = Lzsk + # + # Lcsk: 6mo (186d, 4464h) + # Dreg: N/A + # DprpC: 1h + # TTLkey: 1h + # publish-safety: 1h + # Ipub: 3h + # + # Tact(N) = now - Lcsk + Ipub = now - 186d + 3h + # = now - 4464h + 3h = now - 4461h + TactN="now-4461h" + keytimes="-P ${TactN} -A ${TactN}" + CSK=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 3: -# It is time to submit the DS and to roll signatures. -setup step3.csk-roll1.autosign -# According to RFC 7583: -# -# Tsbm(N+1) >= Trdy(N+1) -# KSK: Tact(N+1) = Tsbm(N+1) -# ZSK: Tact(N+1) = Tpub(N+1) + Ipub = Tsbm(N+1) -# KSK: Iret = DprpP + TTLds (+retire-safety) -# ZSK: IretZ = Dsgn + Dprp + TTLsig (+retire-safety) -# -# Lcsk: 186d -# Dprp: 1h -# DprpP: 1h -# Dreg: N/A -# Dsgn: 25d -# TTLds: 1h -# TTLsig: 1d -# retire-safety: 2h -# Iret: 4h -# IretZ: 26d3h -# Ipub: 3h -# -# Tpub(N) = now - Lcsk = now - 186d -# Tact(N) = now - Lcsk + Dprp + TTLsig = now - 4439h -# Tret(N) = now -# Trem(N) = now + IretZ = now + 26d3h = now + 627h -# Tpub(N+1) = now - Ipub = now - 3h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now + Lcsk = now + 186d = now + 186d -# Trem(N+1) = now + Lcsk + IretZ = now + 186d + 26d3h = -# = now + 5091h -TpubN="now-186d" -TactN="now-4439h" -TretN="now" -TremN="now+627h" -TpubN1="now-3h" -TactN1="${TretN}" -TretN1="now+186d" -TremN1="now+5091h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll1 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 -z $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 3: + # It is time to submit the DS and to roll signatures. + setup step3.csk-roll1.$tld + # According to RFC 7583: + # + # Tsbm(N+1) >= Trdy(N+1) + # KSK: Tact(N+1) = Tsbm(N+1) + # ZSK: Tact(N+1) = Tpub(N+1) + Ipub = Tsbm(N+1) + # KSK: Iret = DprpP + TTLds (+retire-safety) + # ZSK: IretZ = Dsgn + Dprp + TTLsig (+retire-safety) + # + # Lcsk: 186d + # Dprp: 1h + # DprpP: 1h + # Dreg: N/A + # Dsgn: 25d + # TTLds: 1h + # TTLsig: 1d + # retire-safety: 2h + # Iret: 4h + # IretZ: 26d3h + # Ipub: 3h + # + # Tpub(N) = now - Lcsk = now - 186d + # Tact(N) = now - Lcsk + Dprp + TTLsig = now - 4439h + # Tret(N) = now + # Trem(N) = now + IretZ = now + 26d3h = now + 627h + # Tpub(N+1) = now - Ipub = now - 3h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now + Lcsk = now + 186d = now + 186d + # Trem(N+1) = now + Lcsk + IretZ = now + 186d + 26d3h = + # = now + 5091h + TpubN="now-186d" + TactN="now-4439h" + TretN="now" + TremN="now+627h" + TpubN1="now-3h" + TactN1="${TretN}" + TretN1="now+186d" + TremN1="now+5091h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 -z $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 4: -# Some time later all the ZRRSIG records should be from the new CSK, and the -# DS should be swapped. The ZRRSIG records are all replaced after IretZ -# (which is 26d3h). The DS is swapped after Iret (which is 4h). -# In other words, the DS is swapped before all zone signatures are replaced. -setup step4.csk-roll1.autosign -# According to RFC 7583: -# Trem(N) = Tret(N) - Iret + IretZ -# now = Tsbm(N+1) + Iret -# -# Lcsk: 186d -# Iret: 4h -# IretZ: 26d3h -# -# Tpub(N) = now - Iret - Lcsk = now - 4h - 186d = now - 4468h -# Tret(N) = now - Iret = now - 4h = now - 4h -# Trem(N) = now - Iret + IretZ = now - 4h + 26d3h -# = now + 623h -# Tpub(N+1) = now - Iret - IpubC = now - 4h - 3h = now - 7h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now - Iret + Lcsk = now - 4h + 186d = now + 4460h -# Trem(N+1) = now - Iret + Lcsk + IretZ = now - 4h + 186d + 26d3h -# = now + 5087h -TpubN="now-4468h" -TactN="now-4443h" -TretN="now-4h" -TremN="now+623h" -TpubN1="now-7h" -TactN1="${TretN}" -TretN1="now+4460h" -TremN1="now+5087h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll1 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $U $TactN1 -z $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $R $TactN1 -z $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 4: + # Some time later all the ZRRSIG records should be from the new CSK, and the + # DS should be swapped. The ZRRSIG records are all replaced after IretZ + # (which is 26d3h). The DS is swapped after Iret (which is 4h). + # In other words, the DS is swapped before all zone signatures are replaced. + setup step4.csk-roll1.$tld + # According to RFC 7583: + # Trem(N) = Tret(N) - Iret + IretZ + # now = Tsbm(N+1) + Iret + # + # Lcsk: 186d + # Iret: 4h + # IretZ: 26d3h + # + # Tpub(N) = now - Iret - Lcsk = now - 4h - 186d = now - 4468h + # Tret(N) = now - Iret = now - 4h = now - 4h + # Trem(N) = now - Iret + IretZ = now - 4h + 26d3h + # = now + 623h + # Tpub(N+1) = now - Iret - IpubC = now - 4h - 3h = now - 7h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now - Iret + Lcsk = now - 4h + 186d = now + 4460h + # Trem(N+1) = now - Iret + Lcsk + IretZ = now - 4h + 186d + 26d3h + # = now + 5087h + TpubN="now-4468h" + TactN="now-4443h" + TretN="now-4h" + TremN="now+623h" + TpubN1="now-7h" + TactN1="${TretN}" + TretN1="now+4460h" + TremN1="now+5087h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $U $TactN1 -z $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $R $TactN1 -z $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 5: -# After the DS is swapped in step 4, also the KRRSIG records can be removed. -# At this time these have all become hidden. -setup step5.csk-roll1.autosign -# Subtract DNSKEY TTL plus zone propagation delay from all the times (2h). -TpubN="now-4470h" -TactN="now-4445h" -TretN="now-6h" -TremN="now+621h" -TpubN1="now-9h" -TactN1="${TretN}" -TretN1="now+4458h" -TremN1="now+5085h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll1 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $U now-2h -d $H now-2h -z $U $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O now-2h -z $R $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 5: + # After the DS is swapped in step 4, also the KRRSIG records can be removed. + # At this time these have all become hidden. + setup step5.csk-roll1.$tld + # Subtract DNSKEY TTL plus zone propagation delay from all the times (2h). + TpubN="now-4470h" + TactN="now-4445h" + TretN="now-6h" + TremN="now+621h" + TpubN1="now-9h" + TactN1="${TretN}" + TretN1="now+4458h" + TremN1="now+5085h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $U now-2h -d $H now-2h -z $U $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O now-2h -z $R $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 6: -# After the retire interval has passed the predecessor DNSKEY can be -# removed from the zone. -setup step6.csk-roll1.autosign -# According to RFC 7583: -# Trem(N) = Tret(N) + IretZ -# Tret(N) = Tact(N) + Lcsk -# -# Lcsk: 186d -# Iret: 4h -# IretZ: 26d3h -# -# Tpub(N) = now - IretZ - Lcsk = now - 627h - 186d -# = now - 627h - 4464h = now - 5091h -# Tact(N) = now - 627h - 186d -# Tret(N) = now - IretZ = now - 627h -# Trem(N) = now -# Tpub(N+1) = now - IretZ - Ipub = now - 627h - 3h = now - 630h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now - IretZ + Lcsk = now - 627h + 186d = now + 3837h -# Trem(N+1) = now + Lcsk = now + 186d -TpubN="now-5091h" -TactN="now-5066h" -TretN="now-627h" -TremN="now" -TpubN1="now-630h" -TactN1="${TretN}" -TretN1="now+3837h" -TremN1="now+186d" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll1 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $H $TremN -d $H $TremN -z $U $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $R $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 6: + # After the retire interval has passed the predecessor DNSKEY can be + # removed from the zone. + setup step6.csk-roll1.$tld + # According to RFC 7583: + # Trem(N) = Tret(N) + IretZ + # Tret(N) = Tact(N) + Lcsk + # + # Lcsk: 186d + # Iret: 4h + # IretZ: 26d3h + # + # Tpub(N) = now - IretZ - Lcsk = now - 627h - 186d + # = now - 627h - 4464h = now - 5091h + # Tact(N) = now - 627h - 186d + # Tret(N) = now - IretZ = now - 627h + # Trem(N) = now + # Tpub(N+1) = now - IretZ - Ipub = now - 627h - 3h = now - 630h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now - IretZ + Lcsk = now - 627h + 186d = now + 3837h + # Trem(N+1) = now + Lcsk = now + 186d + TpubN="now-5091h" + TactN="now-5066h" + TretN="now-627h" + TremN="now" + TpubN1="now-630h" + TactN1="${TretN}" + TretN1="now+3837h" + TremN1="now+186d" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $H $TremN -d $H $TremN -z $U $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $R $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 7: -# Some time later the predecessor DNSKEY enters the HIDDEN state. -setup step7.csk-roll1.autosign -# Subtract DNSKEY TTL plus zone propagation delay from all the times (2h). -TpubN="now-5093h" -TactN="now-5068h" -TretN="now-629h" -TremN="now-2h" -TpubN1="now-632h" -TactN1="${TretN}" -TretN1="now+3835h" -TremN1="now+4462h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll1 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $U $TremN -r $H $TremN -d $H $TremN -z $H $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 -z $O $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 7: + # Some time later the predecessor DNSKEY enters the HIDDEN state. + setup step7.csk-roll1.$tld + # Subtract DNSKEY TTL plus zone propagation delay from all the times (2h). + TpubN="now-5093h" + TactN="now-5068h" + TretN="now-629h" + TremN="now-2h" + TpubN1="now-632h" + TactN1="${TretN}" + TretN1="now+3835h" + TremN1="now+4462h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $U $TremN -r $H $TremN -d $H $TremN -z $H $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 -z $O $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 8: -# The predecessor DNSKEY can be purged. -setup step8.csk-roll1.autosign -TpubN="now-5094h" -TactN="now-5069h" -TretN="now-630h" -TremN="now-3h" -TpubN1="now-633h" -TactN1="${TretN}" -TretN1="now+3834h" -TremN1="now+4461h" -# Subtract purge-keys interval from all the times (1h). -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll1 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $H $TremN -r $H $TremN -d $H $TremN -z $H $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 -z $O $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 8: + # The predecessor DNSKEY can be purged. + setup step8.csk-roll1.$tld + TpubN="now-5094h" + TactN="now-5069h" + TretN="now-630h" + TremN="now-3h" + TpubN1="now-633h" + TactN1="${TretN}" + TretN1="now+3834h" + TremN1="now+4461h" + # Subtract purge-keys interval from all the times (1h). + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll1-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $H $TremN -r $H $TremN -d $H $TremN -z $H $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 -z $O $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py b/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py index abf27aca65..5f60cb048c 100644 --- a/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py +++ b/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py @@ -13,8 +13,11 @@ from datetime import timedelta +import pytest + import isctest from isctest.kasp import Ipub, Iret +from isctest.util import param from rollover.common import ( pytestmark, alg, @@ -62,11 +65,22 @@ OFFSETS["step8-p"] = OFFSETS["step7-p"] - int(CONFIG["purge-keys"].total_seconds OFFSETS["step8-s"] = OFFSETS["step7-s"] - int(CONFIG["purge-keys"].total_seconds()) -def test_csk_roll1_step1(alg, size, ns3): - zone = "step1.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step1(tld, alg, size, ns3): + zone = f"step1.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + # Note that the key was already generated during setup. + step = { # Introduce the first key. This will immediately be active. "zone": zone, @@ -79,14 +93,45 @@ def test_csk_roll1_step1(alg, size, ns3): # registration delay). "nextev": CSK_LIFETIME - IPUB - timedelta(days=7), } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll1_step2(alg, size, ns3): - zone = "step2.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step2(tld, alg, size, ns3): + zone = f"step2.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 1. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block CSK rollover for key {zone}/ECDSAP256SHA256/{tag} (policy {policy})" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # Successor CSK is prepublished (signs DNSKEY RRset, but not yet # other RRsets). @@ -104,14 +149,59 @@ def test_csk_roll1_step2(alg, size, ns3): # Next key event is when the successor CSK becomes OMNIPRESENT. "nextev": IPUB, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll1_step3(alg, size, ns3): - zone = "step3.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step3(tld, alg, size, ns3): + zone = f"step3.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 2, but DNSKEY has become OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}", + f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:hidden ds:hidden offset:{OFFSETS['step3-s']}", + ], + "keyrelationships": [0, 1], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state HIDDEN to state RUMOURED" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE" + if msg in ns3.log: + # Force step. + isctest.log.debug( + f"keymgr-manual-mode blocking transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE, step again" + ) + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # Successor CSK becomes omnipresent, meaning we can start signing # the remainder of the zone with the successor CSK, and we can @@ -142,14 +232,50 @@ def test_csk_roll1_step3(alg, size, ns3): # from the predecessor ZSK. "smooth": True, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll1_step4(alg, size, ns3): - zone = "step4.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step4(tld, alg, size, ns3): + zone = f"step4.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 3, but DS has become HIDDEN/OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:unretentive ds:hidden offset:{OFFSETS['step4-p']}", + f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:rumoured ds:omnipresent offset:{OFFSETS['step4-s']}", + ], + "keyrelationships": [0, 1], + "manual-mode": True, + "nextev": None, + # We already swapped the DS in the previous step, so disable ds-swap. + "ds-swap": False, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type KRRSIG state OMNIPRESENT to state UNRETENTIVE" + + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -169,14 +295,24 @@ def test_csk_roll1_step4(alg, size, ns3): # We already swapped the DS in the previous step, so disable ds-swap. "ds-swap": False, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll1_step5(alg, size, ns3): - zone = "step5.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step5(tld, alg, size, ns3): + zone = f"step5.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -192,14 +328,25 @@ def test_csk_roll1_step5(alg, size, ns3): # CSK. "nextev": SIGNDELAY, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll1_step6(alg, size, ns3): - zone = "step6.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step6(tld, alg, size, ns3): + zone = f"step6.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + return + step = { "zone": zone, "cdss": CDSS, @@ -217,14 +364,24 @@ def test_csk_roll1_step6(alg, size, ns3): # This is the DNSKEY TTL plus zone propagation delay. "nextev": KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll1_step7(alg, size, ns3): - zone = "step7.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step7(tld, alg, size, ns3): + zone = f"step7.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -239,14 +396,24 @@ def test_csk_roll1_step7(alg, size, ns3): # minus the prepublication time. "nextev": CSK_LIFETIME - IRETZSK - IPUB - KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll1_step8(alg, size, ns3): - zone = "step8.csk-roll1.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll1_step8(tld, alg, size, ns3): + zone = f"step8.csk-roll1.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -255,4 +422,4 @@ def test_csk_roll1_step8(alg, size, ns3): ], "nextev": None, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) diff --git a/bin/tests/system/rollover-csk-roll2/ns3/kasp.conf.j2 b/bin/tests/system/rollover-csk-roll2/ns3/kasp.conf.j2 index 5d70bcf42d..cf1708f3eb 100644 --- a/bin/tests/system/rollover-csk-roll2/ns3/kasp.conf.j2 +++ b/bin/tests/system/rollover-csk-roll2/ns3/kasp.conf.j2 @@ -11,7 +11,31 @@ * information regarding copyright ownership. */ -dnssec-policy "csk-roll2" { +dnssec-policy "csk-roll2-autosign" { + signatures-refresh 12h; + signatures-validity P1D; + signatures-validity-dnskey P1D; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 1h; + purge-keys 0; + + cds-digest-types { "sha-256"; "sha-384"; }; // use two digest type for testing purposes + keys { + csk key-directory lifetime P6M algorithm @DEFAULT_ALGORITHM@; + }; + + zone-propagation-delay PT1H; + max-zone-ttl 1d; + + parent-ds-ttl PT1H; + parent-propagation-delay P1W; +}; + +dnssec-policy "csk-roll2-manual" { + manual-mode yes; + signatures-refresh 12h; signatures-validity P1D; signatures-validity-dnskey P1D; diff --git a/bin/tests/system/rollover-csk-roll2/ns3/named.conf.j2 b/bin/tests/system/rollover-csk-roll2/ns3/named.conf.j2 index 76cbae53c8..94bba1a094 100644 --- a/bin/tests/system/rollover-csk-roll2/ns3/named.conf.j2 +++ b/bin/tests/system/rollover-csk-roll2/ns3/named.conf.j2 @@ -11,41 +11,46 @@ * information regarding copyright ownership. */ +{% set zones = ["autosign", "manual"] %} + include "kasp.conf"; include "named.common.conf"; -zone "step1.csk-roll2.autosign" { +{% for tld in zones %} +zone "step1.csk-roll2.@tld@" { type primary; - file "step1.csk-roll2.autosign.db"; - dnssec-policy "csk-roll2"; + file "step1.csk-roll2.@tld@.db"; + dnssec-policy "csk-roll2-@tld@"; }; -zone "step2.csk-roll2.autosign" { +zone "step2.csk-roll2.@tld@" { type primary; - file "step2.csk-roll2.autosign.db"; - dnssec-policy "csk-roll2"; + file "step2.csk-roll2.@tld@.db"; + dnssec-policy "csk-roll2-@tld@"; }; -zone "step3.csk-roll2.autosign" { +zone "step3.csk-roll2.@tld@" { type primary; - file "step3.csk-roll2.autosign.db"; - dnssec-policy "csk-roll2"; + file "step3.csk-roll2.@tld@.db"; + dnssec-policy "csk-roll2-@tld@"; }; -zone "step4.csk-roll2.autosign" { +zone "step4.csk-roll2.@tld@" { type primary; - file "step4.csk-roll2.autosign.db"; - dnssec-policy "csk-roll2"; + file "step4.csk-roll2.@tld@.db"; + dnssec-policy "csk-roll2-@tld@"; }; -zone "step5.csk-roll2.autosign" { +zone "step5.csk-roll2.@tld@" { type primary; - file "step5.csk-roll2.autosign.db"; - dnssec-policy "csk-roll2"; + file "step5.csk-roll2.@tld@.db"; + dnssec-policy "csk-roll2-@tld@"; }; -zone "step6.csk-roll2.autosign" { +zone "step6.csk-roll2.@tld@" { type primary; - file "step6.csk-roll2.autosign.db"; - dnssec-policy "csk-roll2"; + file "step6.csk-roll2.@tld@.db"; + dnssec-policy "csk-roll2-@tld@"; }; -zone "step7.csk-roll2.autosign" { +zone "step7.csk-roll2.@tld@" { type primary; - file "step7.csk-roll2.autosign.db"; - dnssec-policy "csk-roll2"; + file "step7.csk-roll2.@tld@.db"; + dnssec-policy "csk-roll2-@tld@"; }; + +{% endfor %} diff --git a/bin/tests/system/rollover-csk-roll2/setup.sh b/bin/tests/system/rollover-csk-roll2/setup.sh index 6121887de3..da1e2d8bfe 100644 --- a/bin/tests/system/rollover-csk-roll2/setup.sh +++ b/bin/tests/system/rollover-csk-roll2/setup.sh @@ -40,260 +40,262 @@ O="OMNIPRESENT" U="UNRETENTIVE" # -# The zones at csk-roll2.autosign represent the various steps of a CSK rollover +# The zones at csk-roll2.$tld represent the various steps of a CSK rollover # (which is essentially a ZSK Pre-Publication / KSK Double-KSK rollover). # This scenario differs from the csk-roll1 one because the zone signatures (ZRRSIG) # are replaced with the new key sooner than the DS is swapped. # -# Step 1: -# Introduce the first key. This will immediately be active. -setup step1.csk-roll2.autosign -TactN="now-7d" -keytimes="-P ${TactN} -A ${TactN}" -CSK=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +for tld in autosign manual; do + # Step 1: + # Introduce the first key. This will immediately be active. + setup step1.csk-roll2.$tld + TactN="now-7d" + keytimes="-P ${TactN} -A ${TactN}" + CSK=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 2: -# It is time to introduce the new CSK. -setup step2.csk-roll2.autosign -# According to RFC 7583: -# KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC -# ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub -# IpubC = DprpC + TTLkey (+publish-safety) -# Ipub = IpubC -# Lcsk = Lksk = Lzsk -# -# Lcsk: 6mo (186d, 4464h) -# Dreg: N/A -# DprpC: 1h -# TTLkey: 1h -# publish-safety: 1h -# Ipub: 3h -# -# Tact(N) = now - Lcsk + Ipub = now - 186d + 3h -# = now - 4464h + 3h = now - 4461h -TactN="now-4461h" -keytimes="-P ${TactN} -A ${TactN}" -CSK=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 2: + # It is time to introduce the new CSK. + setup step2.csk-roll2.$tld + # According to RFC 7583: + # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC + # ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub + # IpubC = DprpC + TTLkey (+publish-safety) + # Ipub = IpubC + # Lcsk = Lksk = Lzsk + # + # Lcsk: 6mo (186d, 4464h) + # Dreg: N/A + # DprpC: 1h + # TTLkey: 1h + # publish-safety: 1h + # Ipub: 3h + # + # Tact(N) = now - Lcsk + Ipub = now - 186d + 3h + # = now - 4464h + 3h = now - 4461h + TactN="now-4461h" + keytimes="-P ${TactN} -A ${TactN}" + CSK=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 3: -# It is time to submit the DS and to roll signatures. -setup step3.csk-roll2.autosign -# According to RFC 7583: -# -# Tsbm(N+1) >= Trdy(N+1) -# KSK: Tact(N+1) = Tsbm(N+1) -# ZSK: Tact(N+1) = Tpub(N+1) + Ipub = Tsbm(N+1) -# KSK: Iret = DprpP + TTLds (+retire-safety) -# ZSK: IretZ = Dsgn + Dprp + TTLsig (+retire-safety) -# -# Lcsk: 186d -# Dprp: 1h -# DprpP: 1w -# Dreg: N/A -# Dsgn: 12h -# TTLds: 1h -# TTLsig: 1d -# retire-safety: 1h -# Iret: 170h -# IretZ: 38h -# Ipub: 3h -# -# Tpub(N) = now - Lcsk = now - 186d -# Tact(N) = now - Lcsk + Dprp + TTLsig = now - 4439h -# Tret(N) = now -# Trem(N) = now + Iret = now + 170h -# Tpub(N+1) = now - Ipub = now - 3h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now + Lcsk = now + 186d -# Trem(N+1) = now + Lcsk + Iret = now + 186d + 170h = -# = now + 4464h + 170h = now + 4634h -TpubN="now-186d" -TactN="now-4439h" -TretN="now" -TremN="now+170h" -TpubN1="now-3h" -TactN1="${TretN}" -TretN1="now+186d" -TremN1="now+4634h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll2 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 -z $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 3: + # It is time to submit the DS and to roll signatures. + setup step3.csk-roll2.$tld + # According to RFC 7583: + # + # Tsbm(N+1) >= Trdy(N+1) + # KSK: Tact(N+1) = Tsbm(N+1) + # ZSK: Tact(N+1) = Tpub(N+1) + Ipub = Tsbm(N+1) + # KSK: Iret = DprpP + TTLds (+retire-safety) + # ZSK: IretZ = Dsgn + Dprp + TTLsig (+retire-safety) + # + # Lcsk: 186d + # Dprp: 1h + # DprpP: 1w + # Dreg: N/A + # Dsgn: 12h + # TTLds: 1h + # TTLsig: 1d + # retire-safety: 1h + # Iret: 170h + # IretZ: 38h + # Ipub: 3h + # + # Tpub(N) = now - Lcsk = now - 186d + # Tact(N) = now - Lcsk + Dprp + TTLsig = now - 4439h + # Tret(N) = now + # Trem(N) = now + Iret = now + 170h + # Tpub(N+1) = now - Ipub = now - 3h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now + Lcsk = now + 186d + # Trem(N+1) = now + Lcsk + Iret = now + 186d + 170h = + # = now + 4464h + 170h = now + 4634h + TpubN="now-186d" + TactN="now-4439h" + TretN="now" + TremN="now+170h" + TpubN1="now-3h" + TactN1="${TretN}" + TretN1="now+186d" + TremN1="now+4634h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN -z $O $TactN "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 -z $H $TpubN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 4: -# Some time later all the ZRRSIG records should be from the new CSK, and the -# DS should be swapped. The ZRRSIG records are all replaced after IretZ (38h). -# The DS is swapped after Dreg + Iret (1w3h). In other words, the zone -# signatures are replaced before the DS is swapped. -setup step4.csk-roll2.autosign -# According to RFC 7583: -# Trem(N) = Tret(N) + IretZ -# -# Lcsk: 186d -# Dreg: N/A -# Iret: 170h -# IretZ: 38h -# -# Tpub(N) = now - IretZ - Lcsk = now - 38h - 186d -# = now - 38h - 4464h = now - 4502h -# Tact(N) = now - Iret - Lcsk + TTLsig = now - 4502h + 25h = now - 4477h -# Tret(N) = now - IretZ = now - 38h -# Trem(N) = now - IretZ + Iret = now - 38h + 170h = now + 132h -# Tpub(N+1) = now - IretZ - IpubC = now - 38h - 3h = now - 41h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now - IretZ + Lcsk = now - 38h + 186d -# = now + 4426h -# Trem(N+1) = now - IretZ + Lcsk + Iret -# = now + 4426h + 3h = now + 4429h -TpubN="now-4502h" -TactN="now-4477h" -TretN="now-38h" -TremN="now+132h" -TpubN1="now-41h" -TactN1="${TretN}" -TretN1="now+4426h" -TremN1="now+4429h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll2 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $U $TretN -d $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $R $TactN1 -d $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 4: + # Some time later all the ZRRSIG records should be from the new CSK, and the + # DS should be swapped. The ZRRSIG records are all replaced after IretZ (38h). + # The DS is swapped after Dreg + Iret (1w3h). In other words, the zone + # signatures are replaced before the DS is swapped. + setup step4.csk-roll2.$tld + # According to RFC 7583: + # Trem(N) = Tret(N) + IretZ + # + # Lcsk: 186d + # Dreg: N/A + # Iret: 170h + # IretZ: 38h + # + # Tpub(N) = now - IretZ - Lcsk = now - 38h - 186d + # = now - 38h - 4464h = now - 4502h + # Tact(N) = now - Iret - Lcsk + TTLsig = now - 4502h + 25h = now - 4477h + # Tret(N) = now - IretZ = now - 38h + # Trem(N) = now - IretZ + Iret = now - 38h + 170h = now + 132h + # Tpub(N+1) = now - IretZ - IpubC = now - 38h - 3h = now - 41h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now - IretZ + Lcsk = now - 38h + 186d + # = now + 4426h + # Trem(N+1) = now - IretZ + Lcsk + Iret + # = now + 4426h + 3h = now + 4429h + TpubN="now-4502h" + TactN="now-4477h" + TretN="now-38h" + TremN="now+132h" + TpubN1="now-41h" + TactN1="${TretN}" + TretN1="now+4426h" + TremN1="now+4429h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $U $TretN -d $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $R $TactN1 -d $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 5: -# Some time later the DS can be swapped and the old DNSKEY can be removed from -# the zone. -setup step5.csk-roll2.autosign -# Subtract Iret (170h) - IretZ (38h) = 132h. -# -# Tpub(N) = now - 4502h - 132h = now - 4634h -# Tact(N) = now - 4477h - 132h = now - 4609h -# Tret(N) = now - 38h - 132h = now - 170h -# Trem(N) = now + 132h - 132h = now -# Tpub(N+1) = now - 41h - 132h = now - 173h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now + 4426h - 132h = now + 4294h -# Trem(N+1) = now + 4492h - 132h = now + 4360h -TpubN="now-4634h" -TactN="now-4609h" -TretN="now-170h" -TremN="now" -TpubN1="now-173h" -TactN1="${TretN}" -TretN1="now+4294h" -TremN1="now+4360h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll2 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $H now-133h -d $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $O now-133h -d $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 5: + # Some time later the DS can be swapped and the old DNSKEY can be removed from + # the zone. + setup step5.csk-roll2.$tld + # Subtract Iret (170h) - IretZ (38h) = 132h. + # + # Tpub(N) = now - 4502h - 132h = now - 4634h + # Tact(N) = now - 4477h - 132h = now - 4609h + # Tret(N) = now - 38h - 132h = now - 170h + # Trem(N) = now + 132h - 132h = now + # Tpub(N+1) = now - 41h - 132h = now - 173h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now + 4426h - 132h = now + 4294h + # Trem(N+1) = now + 4492h - 132h = now + 4360h + TpubN="now-4634h" + TactN="now-4609h" + TretN="now-170h" + TremN="now" + TpubN1="now-173h" + TactN1="${TretN}" + TretN1="now+4294h" + TremN1="now+4360h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -z $H now-133h -d $U $TactN1 -D ds $TactN1 "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -z $O now-133h -d $R $TactN1 -P ds $TactN1 "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 6: -# Some time later the predecessor DNSKEY enters the HIDDEN state. -setup step6.csk-roll2.autosign -# Subtract DNSKEY TTL plus zone propagation delay (2h). -# -# Tpub(N) = now - 4634h - 2h = now - 4636h -# Tact(N) = now - 4609h - 2h = now - 4611h -# Tret(N) = now - 170h - 2h = now - 172h -# Trem(N) = now - 2h -# Tpub(N+1) = now - 173h - 2h = now - 175h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now + 4294h - 2h = now + 4292h -# Trem(N+1) = now + 4360h - 2h = now + 4358h -TpubN="now-4636h" -TactN="now-4611h" -TretN="now-172h" -TremN="now-2h" -TpubN1="now-175h" -TactN1="${TretN}" -TretN1="now+4292h" -TremN1="now+4358h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll2 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $U $TremN -r $U $TremN -d $H $TremN -z $H now-135h "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $O now-135h "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 6: + # Some time later the predecessor DNSKEY enters the HIDDEN state. + setup step6.csk-roll2.$tld + # Subtract DNSKEY TTL plus zone propagation delay (2h). + # + # Tpub(N) = now - 4634h - 2h = now - 4636h + # Tact(N) = now - 4609h - 2h = now - 4611h + # Tret(N) = now - 170h - 2h = now - 172h + # Trem(N) = now - 2h + # Tpub(N+1) = now - 173h - 2h = now - 175h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now + 4294h - 2h = now + 4292h + # Trem(N+1) = now + 4360h - 2h = now + 4358h + TpubN="now-4636h" + TactN="now-4611h" + TretN="now-172h" + TremN="now-2h" + TpubN1="now-175h" + TactN1="${TretN}" + TretN1="now+4292h" + TremN1="now+4358h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $U $TremN -r $U $TremN -d $H $TremN -z $H now-135h "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $O now-135h "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 7: -# The predecessor DNSKEY can be purged, but purge-keys is disabled. -setup step7.csk-roll2.autosign -# Subtract 90 days (default, 2160h) from all the times. -# -# Tpub(N) = now - 4636h - 2160h = now - 6796h -# Tact(N) = now - 4611h - 2160h = now - 6771h -# Tret(N) = now - 172h - 2160h = now - 2332h -# Trem(N) = now - 2h - 2160h = now - 2162h -# Tpub(N+1) = now - 175h - 2160h = now - 2335h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now + 4292h - 2160h = now + 2132h -# Trem(N+1) = now + 4358h - 2160h = now + 2198h -TpubN="now-6796h" -TactN="now-6771h" -TretN="now-2332h" -TremN="now-2162h" -TpubN1="now-2335h" -TactN1="${TretN}" -TretN1="now+2132h" -TremN1="now+2198h" -keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" -CSK1=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -CSK2=$($KEYGEN -k csk-roll2 -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $H -k $U $TremN -r $U $TremN -d $H $TremN -z $H now-135h "$CSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $O now-135h "$CSK2" >settime.out.$zone.2 2>&1 -# Set key rollover relationship. -key_successor $CSK1 $CSK2 -# Sign zone. -cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 7: + # The predecessor DNSKEY can be purged, but purge-keys is disabled. + setup step7.csk-roll2.$tld + # Subtract 90 days (default, 2160h) from all the times. + # + # Tpub(N) = now - 4636h - 2160h = now - 6796h + # Tact(N) = now - 4611h - 2160h = now - 6771h + # Tret(N) = now - 172h - 2160h = now - 2332h + # Trem(N) = now - 2h - 2160h = now - 2162h + # Tpub(N+1) = now - 175h - 2160h = now - 2335h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now + 4292h - 2160h = now + 2132h + # Trem(N+1) = now + 4358h - 2160h = now + 2198h + TpubN="now-6796h" + TactN="now-6771h" + TretN="now-2332h" + TremN="now-2162h" + TpubN1="now-2335h" + TactN1="${TretN}" + TretN1="now+2132h" + TremN1="now+2198h" + keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -P sync ${TactN1} -A ${TactN1} -I ${TretN1} -D ${TremN1}" + CSK1=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + CSK2=$($KEYGEN -k csk-roll2-$tld -l kasp.conf $newtimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $H -k $U $TremN -r $U $TremN -d $H $TremN -z $H now-135h "$CSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TremN -z $O now-135h "$CSK2" >settime.out.$zone.2 2>&1 + # Set key rollover relationship. + key_successor $CSK1 $CSK2 + # Sign zone. + cat template.db.in "${CSK1}.key" "${CSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py b/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py index 1e93dfff7b..c8039453f6 100644 --- a/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py +++ b/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py @@ -13,8 +13,11 @@ from datetime import timedelta +import pytest + import isctest from isctest.kasp import Ipub, Iret +from isctest.util import param from rollover.common import ( pytestmark, alg, @@ -65,11 +68,22 @@ OFFSETS["step7-p"] = OFFSETS["step6-p"] - int(timedelta(days=90).total_seconds() OFFSETS["step7-s"] = OFFSETS["step6-s"] - int(timedelta(days=90).total_seconds()) -def test_csk_roll2_step1(alg, size, ns3): - zone = "step1.csk-roll2.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll2_step1(tld, alg, size, ns3): + zone = f"step1.csk-roll2.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + # Note that the key was already generated during setup. + step = { # Introduce the first key. This will immediately be active. "zone": zone, @@ -82,14 +96,45 @@ def test_csk_roll2_step1(alg, size, ns3): # registration delay). "nextev": CSK_LIFETIME - IPUB - TIMEDELTA["P7D"], } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll2_step2(alg, size, ns3): - zone = "step2.csk-roll2.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll2_step2(tld, alg, size, ns3): + zone = f"step2.csk-roll2.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 1. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block CSK rollover for key {zone}/ECDSAP256SHA256/{tag} (policy {policy})" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # Successor CSK is prepublished (signs DNSKEY RRset, but not yet # other RRsets). @@ -107,14 +152,59 @@ def test_csk_roll2_step2(alg, size, ns3): # Next key event is when the successor CSK becomes OMNIPRESENT. "nextev": IPUB, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll2_step3(alg, size, ns3): - zone = "step3.csk-roll2.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll2_step3(tld, alg, size, ns3): + zone = f"step3.csk-roll2.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 2, but DNSKEY has become OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}", + f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:hidden ds:hidden offset:{OFFSETS['step3-s']}", + ], + "keyrelationships": [0, 1], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state HIDDEN to state RUMOURED" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE" + if msg in ns3.log: + # Force step. + isctest.log.debug( + f"keymgr-manual-mode blocking transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE, step again" + ) + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # Successor CSK becomes omnipresent, meaning we can start signing # the remainder of the zone with the successor CSK, and we can @@ -145,14 +235,24 @@ def test_csk_roll2_step3(alg, size, ns3): # from the predecessor ZSK. "smooth": True, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll2_step4(alg, size, ns3): - zone = "step4.csk-roll2.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll2_step4(tld, alg, size, ns3): + zone = f"step4.csk-roll2.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -174,14 +274,49 @@ def test_csk_roll2_step4(alg, size, ns3): # We already swapped the DS in the previous step, so disable ds-swap. "ds-swap": False, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll2_step5(alg, size, ns3): - zone = "step5.csk-roll2.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll2_step5(tld, alg, size, ns3): + zone = f"step5.csk-roll2.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 4, but DS has become HIDDEN/OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:hidden ds:hidden offset:{OFFSETS['step5-p']}", + f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step5-s']}", + ], + "keyrelationships": [0, 1], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[0].key.tag + msg1 = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE" + msg2 = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type KRRSIG state OMNIPRESENT to state UNRETENTIVE" + ns3.log.expect(msg1) + ns3.log.expect(msg2) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -200,14 +335,24 @@ def test_csk_roll2_step5(alg, size, ns3): # This is the DNSKEY TTL plus zone propagation delay. "nextev": KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll2_step6(alg, size, ns3): - zone = "step6.csk-roll2.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll2_step6(tld, alg, size, ns3): + zone = f"step6.csk-roll2.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -223,14 +368,24 @@ def test_csk_roll2_step6(alg, size, ns3): # This is the Lcsk, minus time passed since the key was published. "nextev": CSK_LIFETIME - IRET - IPUB - KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_csk_roll2_step7(alg, size, ns3): - zone = "step7.csk-roll2.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_csk_roll2_step7(tld, alg, size, ns3): + zone = f"step7.csk-roll2.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -242,4 +397,4 @@ def test_csk_roll2_step7(alg, size, ns3): "keyrelationships": [0, 1], "nextev": None, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) diff --git a/bin/tests/system/rollover-enable-dnssec/ns3/kasp.conf.j2 b/bin/tests/system/rollover-enable-dnssec/ns3/kasp.conf.j2 index 5e3e0a3ac2..1f0c0773d2 100644 --- a/bin/tests/system/rollover-enable-dnssec/ns3/kasp.conf.j2 +++ b/bin/tests/system/rollover-enable-dnssec/ns3/kasp.conf.j2 @@ -11,7 +11,28 @@ * information regarding copyright ownership. */ -dnssec-policy "enable-dnssec" { +dnssec-policy "enable-dnssec-autosign" { + signatures-refresh P1W; + signatures-validity P2W; + signatures-validity-dnskey P2W; + + dnskey-ttl 300; + max-zone-ttl PT12H; + zone-propagation-delay PT5M; + retire-safety PT20M; + publish-safety PT5M; + + parent-propagation-delay 1h; + parent-ds-ttl 2h; + + keys { + csk lifetime unlimited algorithm @DEFAULT_ALGORITHM_NUMBER@; + }; +}; + +dnssec-policy "enable-dnssec-manual" { + manual-mode yes; + signatures-refresh P1W; signatures-validity P2W; signatures-validity-dnskey P2W; diff --git a/bin/tests/system/rollover-enable-dnssec/ns3/named.conf.j2 b/bin/tests/system/rollover-enable-dnssec/ns3/named.conf.j2 index 988790a20c..b07015d257 100644 --- a/bin/tests/system/rollover-enable-dnssec/ns3/named.conf.j2 +++ b/bin/tests/system/rollover-enable-dnssec/ns3/named.conf.j2 @@ -11,26 +11,31 @@ * information regarding copyright ownership. */ +{% set zones = ["autosign", "manual"] %} + include "kasp.conf"; include "named.common.conf"; -zone "step1.enable-dnssec.autosign" { +{% for tld in zones %} +zone "step1.enable-dnssec.@tld@" { type primary; - file "step1.enable-dnssec.autosign.db"; - dnssec-policy "enable-dnssec"; + file "step1.enable-dnssec.@tld@.db"; + dnssec-policy "enable-dnssec-@tld@"; }; -zone "step2.enable-dnssec.autosign" { +zone "step2.enable-dnssec.@tld@" { type primary; - file "step2.enable-dnssec.autosign.db"; - dnssec-policy "enable-dnssec"; + file "step2.enable-dnssec.@tld@.db"; + dnssec-policy "enable-dnssec-@tld@"; }; -zone "step3.enable-dnssec.autosign" { +zone "step3.enable-dnssec.@tld@" { type primary; - file "step3.enable-dnssec.autosign.db"; - dnssec-policy "enable-dnssec"; + file "step3.enable-dnssec.@tld@.db"; + dnssec-policy "enable-dnssec-@tld@"; }; -zone "step4.enable-dnssec.autosign" { +zone "step4.enable-dnssec.@tld@" { type primary; - file "step4.enable-dnssec.autosign.db"; - dnssec-policy "enable-dnssec"; + file "step4.enable-dnssec.@tld@.db"; + dnssec-policy "enable-dnssec-@tld@"; }; + +{% endfor %} diff --git a/bin/tests/system/rollover-enable-dnssec/setup.sh b/bin/tests/system/rollover-enable-dnssec/setup.sh index 0761de2dc4..17ee3a79f9 100644 --- a/bin/tests/system/rollover-enable-dnssec/setup.sh +++ b/bin/tests/system/rollover-enable-dnssec/setup.sh @@ -40,61 +40,63 @@ O="OMNIPRESENT" U="UNRETENTIVE" # -# The zones at enable-dnssec.autosign represent the various steps of the +# The zones at enable-dnssec.$tld represent the various steps of the # initial signing of a zone. # -# Step 1: -# This is an unsigned zone and named should perform the initial steps of -# introducing the DNSSEC records in the right order. -setup step1.enable-dnssec.autosign -cp template.db.in $zonefile +for tld in autosign manual; do + # Step 1: + # This is an unsigned zone and named should perform the initial steps of + # introducing the DNSSEC records in the right order. + setup step1.enable-dnssec.$tld + cp template.db.in $zonefile -# Step 2: -# The DNSKEY has been published long enough to become OMNIPRESENT. -setup step2.enable-dnssec.autosign -# DNSKEY TTL: 300 seconds -# zone-propagation-delay: 5 minutes (300 seconds) -# publish-safety: 5 minutes (300 seconds) -# Total: 900 seconds -TpubN="now-900s" -keytimes="-P ${TpubN} -A ${TpubN}" -CSK=$($KEYGEN -k enable-dnssec -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -k $R $TpubN -r $R $TpubN -d $H $TpubN -z $R $TpubN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 2: + # The DNSKEY has been published long enough to become OMNIPRESENT. + setup step2.enable-dnssec.$tld + # DNSKEY TTL: 300 seconds + # zone-propagation-delay: 5 minutes (300 seconds) + # publish-safety: 5 minutes (300 seconds) + # Total: 900 seconds + TpubN="now-900s" + keytimes="-P ${TpubN} -A ${TpubN}" + CSK=$($KEYGEN -k enable-dnssec-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -k $R $TpubN -r $R $TpubN -d $H $TpubN -z $R $TpubN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 3: -# The zone signatures have been published long enough to become OMNIPRESENT. -setup step3.enable-dnssec.autosign -# Passed time since publication: -# max-zone-ttl: 12 hours (43200 seconds) -# zone-propagation-delay: 5 minutes (300 seconds) -TpubN="now-43500s" -# We can submit the DS now. -keytimes="-P ${TpubN} -A ${TpubN}" -CSK=$($KEYGEN -k enable-dnssec -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -k $O $TpubN -r $O $TpubN -d $H $TpubN -z $R $TpubN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 3: + # The zone signatures have been published long enough to become OMNIPRESENT. + setup step3.enable-dnssec.$tld + # Passed time since publication: + # max-zone-ttl: 12 hours (43200 seconds) + # zone-propagation-delay: 5 minutes (300 seconds) + TpubN="now-43500s" + # We can submit the DS now. + keytimes="-P ${TpubN} -A ${TpubN}" + CSK=$($KEYGEN -k enable-dnssec-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -k $O $TpubN -r $O $TpubN -d $H $TpubN -z $R $TpubN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 4: -# The DS has been submitted long enough ago to become OMNIPRESENT. -setup step4.enable-dnssec.autosign -# DS TTL: 2 hour (7200 seconds) -# parent-propagation-delay: 1 hour (3600 seconds) -# Total aditional time: 10800 seconds -# 43500 + 10800 = 54300 -TpubN="now-54300s" -TsbmN="now-10800s" -keytimes="-P ${TpubN} -A ${TpubN} -P sync ${TsbmN}" -CSK=$($KEYGEN -k enable-dnssec -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) -$SETTIME -s -g $O -P ds $TsbmN -k $O $TpubN -r $O $TpubN -d $R $TpubN -z $O $TsbmN "$CSK" >settime.out.$zone.1 2>&1 -cat template.db.in "${CSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 4: + # The DS has been submitted long enough ago to become OMNIPRESENT. + setup step4.enable-dnssec.$tld + # DS TTL: 2 hour (7200 seconds) + # parent-propagation-delay: 1 hour (3600 seconds) + # Total aditional time: 10800 seconds + # 43500 + 10800 = 54300 + TpubN="now-54300s" + TsbmN="now-10800s" + keytimes="-P ${TpubN} -A ${TpubN} -P sync ${TsbmN}" + CSK=$($KEYGEN -k enable-dnssec-$tld -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1) + $SETTIME -s -g $O -P ds $TsbmN -k $O $TpubN -r $O $TpubN -d $R $TpubN -z $O $TsbmN "$CSK" >settime.out.$zone.1 2>&1 + cat template.db.in "${CSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py b/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py index 9c4dd31b85..2cb805a674 100644 --- a/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py +++ b/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py @@ -11,8 +11,11 @@ # pylint: disable=redefined-outer-name,unused-import +import pytest + import isctest from isctest.kasp import Ipub, IpubC, Iret +from isctest.util import param from rollover.common import ( pytestmark, alg, @@ -44,11 +47,40 @@ OFFSETS["step3"] = -int(IRETZSK.total_seconds()) OFFSETS["step4"] = -int(IPUBC.total_seconds() + IRETKSK.total_seconds()) -def test_rollover_enable_dnssec_step1(alg, size, ns3): - zone = "step1.enable-dnssec.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_rollover_enable_dnssec_step1(tld, alg, size, ns3): + zone = f"step1.enable-dnssec.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as insecure. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [], + "manual-mode": True, + "zone-signed": False, + "nextev": None, + } + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + msg = f"keymgr-manual-mode: block new key generation for zone {zone} (policy {policy})" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -59,14 +91,24 @@ def test_rollover_enable_dnssec_step1(alg, size, ns3): # after the publication interval. "nextev": IPUB, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_rollover_enable_dnssec_step2(alg, size, ns3): - zone = "step2.enable-dnssec.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_rollover_enable_dnssec_step2(tld, alg, size, ns3): + zone = f"step2.enable-dnssec.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -81,14 +123,45 @@ def test_rollover_enable_dnssec_step2(alg, size, ns3): # Minus the time already elapsed. "nextev": IRETZSK - IPUB, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_rollover_enable_dnssec_step3(alg, size, ns3): - zone = "step3.enable-dnssec.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_rollover_enable_dnssec_step3(tld, alg, size, ns3): + zone = f"step3.enable-dnssec.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 2, but zone signatures have become OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"csk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:hidden offset:{OFFSETS['step3']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[0].key.tag + msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type DS state HIDDEN to state RUMOURED" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { "zone": zone, "cdss": CDSS, @@ -102,14 +175,24 @@ def test_rollover_enable_dnssec_step3(alg, size, ns3): # This is after the retire interval. "nextev": IRETKSK, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_rollover_enable_dnssec_step4(alg, size, ns3): - zone = "step4.enable-dnssec.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_rollover_enable_dnssec_step4(tld, alg, size, ns3): + zone = f"step4.enable-dnssec.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { "zone": zone, "cdss": CDSS, @@ -122,4 +205,4 @@ def test_rollover_enable_dnssec_step4(alg, size, ns3): # established. So we fall back to the default loadkeys interval. "nextev": TIMEDELTA["PT1H"], } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) diff --git a/bin/tests/system/rollover-ksk-3crowd/ns3/named.conf.j2 b/bin/tests/system/rollover-ksk-3crowd/ns3/named.conf.j2 index ae01f2eaeb..7775b8ebe3 100644 --- a/bin/tests/system/rollover-ksk-3crowd/ns3/named.conf.j2 +++ b/bin/tests/system/rollover-ksk-3crowd/ns3/named.conf.j2 @@ -19,5 +19,5 @@ zone "three-is-a-crowd.kasp" { file "three-is-a-crowd.kasp.db"; inline-signing yes; /* Use same policy as KSK rollover test zones. */ - dnssec-policy "ksk-doubleksk"; + dnssec-policy "ksk-doubleksk-autosign"; }; diff --git a/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py b/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py index 88ca2196a4..7fb3fce835 100644 --- a/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py +++ b/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py @@ -27,7 +27,7 @@ from rollover.common import ( CDSS = ["CDS (SHA-256)"] -POLICY = "ksk-doubleksk" +POLICY = "ksk-doubleksk-autosign" OFFSET1 = -int(timedelta(days=60).total_seconds()) OFFSET2 = -int(timedelta(hours=27).total_seconds()) TTL = int(KSK_CONFIG["dnskey-ttl"].total_seconds()) diff --git a/bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf.j2 b/bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf.j2 index d73934708a..c33a9f7c40 100644 --- a/bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf.j2 +++ b/bin/tests/system/rollover-ksk-doubleksk/ns3/kasp.conf.j2 @@ -11,7 +11,32 @@ * information regarding copyright ownership. */ -dnssec-policy "ksk-doubleksk" { +dnssec-policy "ksk-doubleksk-autosign" { + signatures-refresh P1W; + signatures-validity P2W; + signatures-validity-dnskey P2W; + + dnskey-ttl 2h; + publish-safety P1D; + retire-safety P2D; + purge-keys PT1H; + + cdnskey no; + keys { + ksk key-directory lifetime P60D algorithm @DEFAULT_ALGORITHM@; + zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@; + }; + + zone-propagation-delay PT1H; + max-zone-ttl 1d; + + parent-ds-ttl 3600; + parent-propagation-delay PT1H; +}; + +dnssec-policy "ksk-doubleksk-manual" { + manual-mode yes; + signatures-refresh P1W; signatures-validity P2W; signatures-validity-dnskey P2W; diff --git a/bin/tests/system/rollover-ksk-doubleksk/ns3/named.conf.j2 b/bin/tests/system/rollover-ksk-doubleksk/ns3/named.conf.j2 index 63a0090fe1..7a51de9635 100644 --- a/bin/tests/system/rollover-ksk-doubleksk/ns3/named.conf.j2 +++ b/bin/tests/system/rollover-ksk-doubleksk/ns3/named.conf.j2 @@ -11,36 +11,40 @@ * information regarding copyright ownership. */ +{% set zones = ["autosign", "manual"] %} + include "kasp.conf"; include "named.common.conf"; -zone "step1.ksk-doubleksk.autosign" { +{% for tld in zones %} +zone "step1.ksk-doubleksk.@tld@" { type primary; - file "step1.ksk-doubleksk.autosign.db"; - dnssec-policy "ksk-doubleksk"; + file "step1.ksk-doubleksk.@tld@.db"; + dnssec-policy "ksk-doubleksk-@tld@"; }; -zone "step2.ksk-doubleksk.autosign" { +zone "step2.ksk-doubleksk.@tld@" { type primary; - file "step2.ksk-doubleksk.autosign.db"; - dnssec-policy "ksk-doubleksk"; + file "step2.ksk-doubleksk.@tld@.db"; + dnssec-policy "ksk-doubleksk-@tld@"; }; -zone "step3.ksk-doubleksk.autosign" { +zone "step3.ksk-doubleksk.@tld@" { type primary; - file "step3.ksk-doubleksk.autosign.db"; - dnssec-policy "ksk-doubleksk"; + file "step3.ksk-doubleksk.@tld@.db"; + dnssec-policy "ksk-doubleksk-@tld@"; }; -zone "step4.ksk-doubleksk.autosign" { +zone "step4.ksk-doubleksk.@tld@" { type primary; - file "step4.ksk-doubleksk.autosign.db"; - dnssec-policy "ksk-doubleksk"; + file "step4.ksk-doubleksk.@tld@.db"; + dnssec-policy "ksk-doubleksk-@tld@"; }; -zone "step5.ksk-doubleksk.autosign" { +zone "step5.ksk-doubleksk.@tld@" { type primary; - file "step5.ksk-doubleksk.autosign.db"; - dnssec-policy "ksk-doubleksk"; + file "step5.ksk-doubleksk.@tld@.db"; + dnssec-policy "ksk-doubleksk-@tld@"; }; -zone "step6.ksk-doubleksk.autosign" { +zone "step6.ksk-doubleksk.@tld@" { type primary; - file "step6.ksk-doubleksk.autosign.db"; - dnssec-policy "ksk-doubleksk"; + file "step6.ksk-doubleksk.@tld@.db"; + dnssec-policy "ksk-doubleksk-@tld@"; }; +{% endfor %} diff --git a/bin/tests/system/rollover-ksk-doubleksk/setup.sh b/bin/tests/system/rollover-ksk-doubleksk/setup.sh index c47c980600..cfd654bffd 100644 --- a/bin/tests/system/rollover-ksk-doubleksk/setup.sh +++ b/bin/tests/system/rollover-ksk-doubleksk/setup.sh @@ -40,202 +40,204 @@ O="OMNIPRESENT" U="UNRETENTIVE" # -# The zones at ksk-doubleksk.autosign represent the various steps of a KSK +# The zones at ksk-doubleksk.$tld represent the various steps of a KSK # Double-KSK rollover. # -# Step 1: -# Introduce the first key. This will immediately be active. -setup step1.ksk-doubleksk.autosign -TactN="now-7d" -keytimes="-P ${TactN} -A ${TactN}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 -cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" -cp $infile $zonefile -$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +for tld in autosign manual; do + # Step 1: + # Introduce the first key. This will immediately be active. + setup step1.ksk-doubleksk.$tld + TactN="now-7d" + keytimes="-P ${TactN} -A ${TactN}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 + cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" + cp $infile $zonefile + $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 2: -# It is time to submit the introduce the new KSK. -setup step2.ksk-doubleksk.autosign -# Lksk: 60d -# Dreg: n/a -# DprpC: 1h -# TTLds: 1d -# TTLkey: 2h -# publish-safety: 1d -# retire-safety: 2d -# -# According to RFC 7583: -# Tpub(N+1) <= Tact(N) + Lksk - Dreg - IpubC -# IpubC = DprpC + TTLkey (+publish-safety) -# -# IpubC = 27h -# Tact(N) = now - Lksk + Dreg + IpubC = now - 60d + 27h -# = now - 1440h + 27h = now - 1413h -TactN="now-1413h" -keytimes="-P ${TactN} -A ${TactN}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 -cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 2: + # It is time to submit the introduce the new KSK. + setup step2.ksk-doubleksk.$tld + # Lksk: 60d + # Dreg: n/a + # DprpC: 1h + # TTLds: 1d + # TTLkey: 2h + # publish-safety: 1d + # retire-safety: 2d + # + # According to RFC 7583: + # Tpub(N+1) <= Tact(N) + Lksk - Dreg - IpubC + # IpubC = DprpC + TTLkey (+publish-safety) + # + # IpubC = 27h + # Tact(N) = now - Lksk + Dreg + IpubC = now - 60d + 27h + # = now - 1440h + 27h = now - 1413h + TactN="now-1413h" + keytimes="-P ${TactN} -A ${TactN}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $keytimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 + cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 3: -# It is time to submit the DS. -setup step3.ksk-doubleksk.autosign -# According to RFC 7583: -# Iret = DprpP + TTLds (+retire-safety) -# -# Iret = 50h -# Tpub(N) = now - Lksk = now - 60d = now - 60d -# Tact(N) = now - 1413h -# Tret(N) = now -# Trem(N) = now + Iret = now + 50h -# Tpub(N+1) = now - IpubC = now - 27h -# Tact(N+1) = now -# Tret(N+1) = now + Lksk = now + 60d -# Trem(N+1) = now + Lksk + Iret = now + 60d + 50h -# = now + 1440h + 50h = 1490h -TpubN="now-60d" -TactN="now-1413h" -TretN="now" -TremN="now+50h" -TpubN1="now-27h" -TactN1="now" -TretN1="now+60d" -TremN1="now+1490h" -ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" -zsktimes="-P ${TpubN} -A ${TpubN}" -KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $H -k $O $TpubN -r $O $TpubN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TpubN -z $O $TpubN "$ZSK" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $KSK1 $KSK2 -# Sign zone. -cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 3: + # It is time to submit the DS. + setup step3.ksk-doubleksk.$tld + # According to RFC 7583: + # Iret = DprpP + TTLds (+retire-safety) + # + # Iret = 50h + # Tpub(N) = now - Lksk = now - 60d = now - 60d + # Tact(N) = now - 1413h + # Tret(N) = now + # Trem(N) = now + Iret = now + 50h + # Tpub(N+1) = now - IpubC = now - 27h + # Tact(N+1) = now + # Tret(N+1) = now + Lksk = now + 60d + # Trem(N+1) = now + Lksk + Iret = now + 60d + 50h + # = now + 1440h + 50h = 1490h + TpubN="now-60d" + TactN="now-1413h" + TretN="now" + TremN="now+50h" + TpubN1="now-27h" + TactN1="now" + TretN1="now+60d" + TremN1="now+1490h" + ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" + zsktimes="-P ${TpubN} -A ${TpubN}" + KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $H -k $O $TpubN -r $O $TpubN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TpubN -z $O $TpubN "$ZSK" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $KSK1 $KSK2 + # Sign zone. + cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 4: -# The DS should be swapped now. -setup step4.ksk-doubleksk.autosign -# Tpub(N) = now - Lksk - Iret = now - 60d - 50h -# = now - 1440h - 50h = now - 1490h -# Tact(N) = now - 1490h + 27h = now - 1463h -# Tret(N) = now - Iret = now - 50h -# Trem(N) = now -# Tpub(N+1) = now - Iret - IpubC = now - 50h - 27h -# = now - 77h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now + Lksk - Iret = now + 60d - 50h = now + 1390h -# Trem(N+1) = now + Lksk = now + 60d -TpubN="now-1490h" -TactN="now-1463h" -TretN="now-50h" -TremN="now" -TpubN1="now-77h" -TactN1="${TretN}" -TretN1="now+1390h" -TremN1="now+60d" -ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" -zsktimes="-P ${TpubN} -A ${TpubN}" -KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $U $TretN -D ds $TretN "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $R $TactN1 -P ds $TactN1 "$KSK2" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $KSK1 $KSK2 -# Sign zone. -cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 4: + # The DS should be swapped now. + setup step4.ksk-doubleksk.$tld + # Tpub(N) = now - Lksk - Iret = now - 60d - 50h + # = now - 1440h - 50h = now - 1490h + # Tact(N) = now - 1490h + 27h = now - 1463h + # Tret(N) = now - Iret = now - 50h + # Trem(N) = now + # Tpub(N+1) = now - Iret - IpubC = now - 50h - 27h + # = now - 77h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now + Lksk - Iret = now + 60d - 50h = now + 1390h + # Trem(N+1) = now + Lksk = now + 60d + TpubN="now-1490h" + TactN="now-1463h" + TretN="now-50h" + TremN="now" + TpubN1="now-77h" + TactN1="${TretN}" + TretN1="now+1390h" + TremN1="now+60d" + ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" + zsktimes="-P ${TpubN} -A ${TpubN}" + KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $U $TretN -D ds $TretN "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $R $TactN1 -P ds $TactN1 "$KSK2" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $KSK1 $KSK2 + # Sign zone. + cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 5: -# The predecessor DNSKEY is removed long enough that is has become HIDDEN. -setup step5.ksk-doubleksk.autosign -# Subtract DNSKEY TTL + zone-propagation-delay from all the times (3h). -# Tpub(N) = now - 1490h - 3h = now - 1493h -# Tact(N) = now - 1463h - 3h = now - 1466h -# Tret(N) = now - 50h - 3h = now - 53h -# Trem(N) = now - 3h -# Tpub(N+1) = now - 77h - 3h = now - 80h -# Tact(N+1) = Tret(N) -# Tret(N+1) = now + 1390h - 3h = now + 1387h -# Trem(N+1) = now + 60d - 3h = now + 1441h -TpubN="now-1493h" -TactN="now-1466h" -TretN="now-53h" -TremN="now-3h" -TpubN1="now-80h" -TactN1="${TretN}" -TretN1="now+1387h" -TremN1="now+1441h" -ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" -zsktimes="-P ${TpubN} -A ${TpubN}" -KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $H -k $U $TretN -r $U $TretN -d $H $TretN "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 "$KSK2" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $KSK1 $KSK2 -# Sign zone. -cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 5: + # The predecessor DNSKEY is removed long enough that is has become HIDDEN. + setup step5.ksk-doubleksk.$tld + # Subtract DNSKEY TTL + zone-propagation-delay from all the times (3h). + # Tpub(N) = now - 1490h - 3h = now - 1493h + # Tact(N) = now - 1463h - 3h = now - 1466h + # Tret(N) = now - 50h - 3h = now - 53h + # Trem(N) = now - 3h + # Tpub(N+1) = now - 77h - 3h = now - 80h + # Tact(N+1) = Tret(N) + # Tret(N+1) = now + 1390h - 3h = now + 1387h + # Trem(N+1) = now + 60d - 3h = now + 1441h + TpubN="now-1493h" + TactN="now-1466h" + TretN="now-53h" + TremN="now-3h" + TpubN1="now-80h" + TactN1="${TretN}" + TretN1="now+1387h" + TremN1="now+1441h" + ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" + zsktimes="-P ${TpubN} -A ${TpubN}" + KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $H -k $U $TretN -r $U $TretN -d $H $TretN "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 "$KSK2" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $KSK1 $KSK2 + # Sign zone. + cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 6: -# The predecessor DNSKEY can be purged. -setup step6.ksk-doubleksk.autosign -# Subtract purge-keys interval from all the times (1h). -TpubN="now-1494h" -TactN="now-1467h" -TretN="now-54h" -TremN="now-4h" -TpubN1="now-81h" -TactN1="${TretN}" -TretN1="now+1386h" -TremN1="now+1440h" -ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" -newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" -zsktimes="-P ${TpubN} -A ${TpubN}" -KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) -KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $H -k $H $TretN -r $H $TretN -d $H $TretN "$KSK1" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 "$KSK2" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $KSK1 $KSK2 -# Sign zone. -cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 6: + # The predecessor DNSKEY can be purged. + setup step6.ksk-doubleksk.$tld + # Subtract purge-keys interval from all the times (1h). + TpubN="now-1494h" + TactN="now-1467h" + TretN="now-54h" + TremN="now-4h" + TpubN1="now-81h" + TactN1="${TretN}" + TretN1="now+1386h" + TremN1="now+1440h" + ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" + newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TactN1} -I ${TretN1} -D ${TremN1}" + zsktimes="-P ${TpubN} -A ${TpubN}" + KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1) + KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $H -k $H $TretN -r $H $TretN -d $H $TretN "$KSK1" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -r $O $TactN1 -d $O $TactN1 "$KSK2" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $KSK1 $KSK2 + # Sign zone. + cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py b/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py index 6a12328892..90ebce96a6 100644 --- a/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py +++ b/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py @@ -13,7 +13,10 @@ from datetime import timedelta +import pytest + import isctest +from isctest.util import param from rollover.common import ( pytestmark, alg, @@ -45,11 +48,22 @@ OFFSETS["step6-p"] = OFFSETS["step5-p"] - int(KSK_CONFIG["purge-keys"].total_sec OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(KSK_CONFIG["purge-keys"].total_seconds()) -def test_ksk_doubleksk_step1(alg, size, ns3): - zone = "step1.ksk-doubleksk.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_ksk_doubleksk_step1(tld, alg, size, ns3): + zone = f"step1.ksk-doubleksk.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + # Note that the key was already generated during setup. + step = { # Introduce the first key. This will immediately be active. "zone": zone, @@ -63,14 +77,46 @@ def test_ksk_doubleksk_step1(alg, size, ns3): # already passed). "nextev": KSK_LIFETIME - KSK_IPUB - timedelta(days=7), } - isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) -def test_ksk_doubleksk_step2(alg, size, ns3): - zone = "step2.ksk-doubleksk.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_ksk_doubleksk_step2(tld, alg, size, ns3): + zone = f"step2.ksk-doubleksk.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 1. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step2-p']}", + f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block KSK rollover for key {zone}/ECDSAP256SHA256/{tag} (policy {policy})" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # Successor KSK is prepublished (and signs DNSKEY RRset). # KSK1 goal: omnipresent -> hidden @@ -88,14 +134,60 @@ def test_ksk_doubleksk_step2(alg, size, ns3): # Next key event is when the successor KSK becomes OMNIPRESENT. "nextev": KSK_IPUB, } - isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) -def test_ksk_doubleksk_step3(alg, size, ns3): - zone = "step3.ksk-doubleksk.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_ksk_doubleksk_step3(tld, alg, size, ns3): + zone = f"step3.ksk-doubleksk.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 2, but DNSKEY has become OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step3-p']}", + f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}", + f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:hidden offset:{OFFSETS['step3-s']}", + ], + "keyrelationships": [1, 2], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + + # Check logs. + tag = keys[2].key.tag + msg = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type DS state HIDDEN to state RUMOURED" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE" + if msg in ns3.log: + # Force step. + isctest.log.debug( + f"keymgr-manual-mode blocking transition KSK {zone}/ECDSAP256SHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE, step again" + ) + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # The successor DNSKEY RRset has become omnipresent. The # predecessor DS can be withdrawn and the successor DS can be @@ -118,14 +210,50 @@ def test_ksk_doubleksk_step3(alg, size, ns3): # successor DS. This is the the retire interval. "nextev": KSK_IRET, } - isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) -def test_ksk_doubleksk_step4(alg, size, ns3): - zone = "step4.ksk-doubleksk.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_ksk_doubleksk_step4(tld, alg, size, ns3): + zone = f"step4.ksk-doubleksk.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 3, but DS has become HIDDEN/OMNIPRESENT. + step = { + "zone": zone, + "cdss": CDSS, + "keyprops": [ + f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step4-p']}", + f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:hidden offset:{OFFSETS['step4-p']}", + f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step4-s']}", + ], + "keyrelationships": [1, 2], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + + # Check logs. + tag = keys[1].key.tag + msg1 = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE" + msg2 = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type KRRSIG state OMNIPRESENT to state UNRETENTIVE" + ns3.log.expect(msg1) + ns3.log.expect(msg2) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # The predecessor DNSKEY may be removed, the successor DS is # omnipresent. @@ -145,14 +273,24 @@ def test_ksk_doubleksk_step4(alg, size, ns3): # This is the DNSKEY TTL plus zone propagation delay. "nextev": KSK_KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) -def test_ksk_doubleksk_step5(alg, size, ns3): - zone = "step5.ksk-doubleksk.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_ksk_doubleksk_step5(tld, alg, size, ns3): + zone = f"step5.ksk-doubleksk.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { # The predecessor DNSKEY is long enough removed from the zone it # has become hidden. @@ -170,14 +308,24 @@ def test_ksk_doubleksk_step5(alg, size, ns3): # This is the KSK lifetime minus Ipub minus Iret minus time elapsed. "nextev": KSK_LIFETIME - KSK_IPUB - KSK_IRET - KSK_KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) -def test_ksk_doubleksk_step6(alg, size, ns3): - zone = "step6.ksk-doubleksk.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_ksk_doubleksk_step6(tld, alg, size, ns3): + zone = f"step6.ksk-doubleksk.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { # Predecessor KSK is now purged. "zone": zone, @@ -188,4 +336,4 @@ def test_ksk_doubleksk_step6(alg, size, ns3): ], "nextev": None, } - isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) diff --git a/bin/tests/system/rollover-zsk-prepub/ns3/kasp.conf.j2 b/bin/tests/system/rollover-zsk-prepub/ns3/kasp.conf.j2 index 7b92bc1632..eac3293a6e 100644 --- a/bin/tests/system/rollover-zsk-prepub/ns3/kasp.conf.j2 +++ b/bin/tests/system/rollover-zsk-prepub/ns3/kasp.conf.j2 @@ -11,7 +11,28 @@ * information regarding copyright ownership. */ -dnssec-policy "zsk-prepub" { +dnssec-policy "zsk-prepub-autosign" { + signatures-refresh P1W; + signatures-validity P2W; + signatures-validity-dnskey P2W; + + dnskey-ttl 3600; + publish-safety P1D; + retire-safety P2D; + purge-keys PT1H; + + keys { + ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@; + zsk key-directory lifetime P30D algorithm @DEFAULT_ALGORITHM@; + }; + + zone-propagation-delay PT1H; + max-zone-ttl 1d; +}; + +dnssec-policy "zsk-prepub-manual" { + manual-mode yes; + signatures-refresh P1W; signatures-validity P2W; signatures-validity-dnskey P2W; diff --git a/bin/tests/system/rollover-zsk-prepub/ns3/named.conf.j2 b/bin/tests/system/rollover-zsk-prepub/ns3/named.conf.j2 index 2e7137762b..462122eb76 100644 --- a/bin/tests/system/rollover-zsk-prepub/ns3/named.conf.j2 +++ b/bin/tests/system/rollover-zsk-prepub/ns3/named.conf.j2 @@ -11,36 +11,40 @@ * information regarding copyright ownership. */ +{% set zones = ["autosign", "manual"] %} + include "kasp.conf"; include "named.common.conf"; -zone "step1.zsk-prepub.autosign" { +{% for tld in zones %} +zone "step1.zsk-prepub.@tld@" { type primary; - file "step1.zsk-prepub.autosign.db"; - dnssec-policy "zsk-prepub"; + file "step1.zsk-prepub.@tld@.db"; + dnssec-policy "zsk-prepub-@tld@"; }; -zone "step2.zsk-prepub.autosign" { +zone "step2.zsk-prepub.@tld@" { type primary; - file "step2.zsk-prepub.autosign.db"; - dnssec-policy "zsk-prepub"; + file "step2.zsk-prepub.@tld@.db"; + dnssec-policy "zsk-prepub-@tld@"; }; -zone "step3.zsk-prepub.autosign" { +zone "step3.zsk-prepub.@tld@" { type primary; - file "step3.zsk-prepub.autosign.db"; - dnssec-policy "zsk-prepub"; + file "step3.zsk-prepub.@tld@.db"; + dnssec-policy "zsk-prepub-@tld@"; }; -zone "step4.zsk-prepub.autosign" { +zone "step4.zsk-prepub.@tld@" { type primary; - file "step4.zsk-prepub.autosign.db"; - dnssec-policy "zsk-prepub"; + file "step4.zsk-prepub.@tld@.db"; + dnssec-policy "zsk-prepub-@tld@"; }; -zone "step5.zsk-prepub.autosign" { +zone "step5.zsk-prepub.@tld@" { type primary; - file "step5.zsk-prepub.autosign.db"; - dnssec-policy "zsk-prepub"; + file "step5.zsk-prepub.@tld@.db"; + dnssec-policy "zsk-prepub-@tld@"; }; -zone "step6.zsk-prepub.autosign" { +zone "step6.zsk-prepub.@tld@" { type primary; - file "step6.zsk-prepub.autosign.db"; - dnssec-policy "zsk-prepub"; + file "step6.zsk-prepub.@tld@.db"; + dnssec-policy "zsk-prepub-@tld@"; }; +{% endfor %} diff --git a/bin/tests/system/rollover-zsk-prepub/setup.sh b/bin/tests/system/rollover-zsk-prepub/setup.sh index d9555f6e2c..86bea47159 100644 --- a/bin/tests/system/rollover-zsk-prepub/setup.sh +++ b/bin/tests/system/rollover-zsk-prepub/setup.sh @@ -40,177 +40,179 @@ O="OMNIPRESENT" U="UNRETENTIVE" # -# The zones at zsk-prepub.autosign represent the various steps of a ZSK +# The zones at zsk-prepub.$tld represent the various steps of a ZSK # Pre-Publication rollover. # -# Step 1: -# Introduce the first key. This will immediately be active. -setup step1.zsk-prepub.autosign -TactN="now-7d" -keytimes="-P ${TactN} -A ${TactN}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $keytimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 -cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +for tld in autosign manual; do + # Step 1: + # Introduce the first key. This will immediately be active. + setup step1.zsk-prepub.$tld + TactN="now-7d" + keytimes="-P ${TactN} -A ${TactN}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $keytimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 + cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 2: -# It is time to pre-publish the successor ZSK. -setup step2.zsk-prepub.autosign -# According to RFC 7583: -# Tact(N) = now + Ipub - Lzsk = now + 26h - 30d -# = now + 26h - 30d = now − 694h -TactN="now-694h" -keytimes="-P ${TactN} -A ${TactN}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $keytimes $zone 2>keygen.out.$zone.2) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 -cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 2: + # It is time to pre-publish the successor ZSK. + setup step2.zsk-prepub.$tld + # According to RFC 7583: + # Tact(N) = now + Ipub - Lzsk = now + 26h - 30d + # = now + 26h - 30d = now − 694h + TactN="now-694h" + keytimes="-P ${TactN} -A ${TactN}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $keytimes $zone 2>keygen.out.$zone.2) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.2 2>&1 + cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 3: -# After the publication interval has passed the DNSKEY of the successor ZSK -# is OMNIPRESENT and the zone can thus be signed with the successor ZSK. -setup step3.zsk-prepub.autosign -# According to RFC 7583: -# Tpub(N+1) <= Tact(N) + Lzsk - Ipub -# Tact(N+1) = Tact(N) + Lzsk -# -# Tact(N) = now - Lzsk = now - 30d -# Tpub(N+1) = now - Ipub = now - 26h -# Tact(N+1) = now -# Tret(N) = now -# Trem(N) = now + Iret = now + Dsign + Dprp + TTLsig + retire-safety = 8d1h = now + 241h -TactN="now-30d" -TpubN1="now-26h" -TactN1="now" -TremN="now+241h" -keytimes="-P ${TactN} -A ${TactN}" -oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" -newtimes="-P ${TpubN1} -A ${TactN1}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $R $TpubN1 -z $H $TpubN1 "$ZSK2" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $ZSK1 $ZSK2 -# Sign zone. -cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 3: + # After the publication interval has passed the DNSKEY of the successor ZSK + # is OMNIPRESENT and the zone can thus be signed with the successor ZSK. + setup step3.zsk-prepub.$tld + # According to RFC 7583: + # Tpub(N+1) <= Tact(N) + Lzsk - Ipub + # Tact(N+1) = Tact(N) + Lzsk + # + # Tact(N) = now - Lzsk = now - 30d + # Tpub(N+1) = now - Ipub = now - 26h + # Tact(N+1) = now + # Tret(N) = now + # Trem(N) = now + Iret = now + Dsign + Dprp + TTLsig + retire-safety = 8d1h = now + 241h + TactN="now-30d" + TpubN1="now-26h" + TactN1="now" + TremN="now+241h" + keytimes="-P ${TactN} -A ${TactN}" + oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" + newtimes="-P ${TpubN1} -A ${TactN1}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $O $TactN -z $O $TactN "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $R $TpubN1 -z $H $TpubN1 "$ZSK2" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $ZSK1 $ZSK2 + # Sign zone. + cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 4: -# After the retire interval has passed the predecessor DNSKEY can be -# removed from the zone. -setup step4.zsk-prepub.autosign -# Lzsk: 30d -# Ipub: 26h -# Dsgn: 1w -# Dprp: 1h -# TTLsig: 1d -# retire-safety: 2d -# -# According to RFC 7583: -# Iret = Dsgn + Dprp + TTLsig (+retire-safety) -# Iret = 1w + 1h + 1d + 2d = 10d1h = 241h -# -# Tact(N) = now - Iret - Lzsk -# = now - 241h - 30d = now - 241h - 720h -# = now - 961h -# Tpub(N+1) = now - Iret - Ipub -# = now - 241h - 26h -# = now - 267h -# Tact(N+1) = now - Iret = now - 241h -TactN="now-961h" -TpubN1="now-267h" -TactN1="now-241h" -TremN="now" -keytimes="-P ${TactN} -A ${TactN}" -oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" -newtimes="-P ${TpubN1} -A ${TactN1}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $O $TactN -z $U $TactN1 "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -z $R $TactN1 "$ZSK2" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $ZSK1 $ZSK2 -# Sign zone. -cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" -cp $infile $zonefile -$SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 4: + # After the retire interval has passed the predecessor DNSKEY can be + # removed from the zone. + setup step4.zsk-prepub.$tld + # Lzsk: 30d + # Ipub: 26h + # Dsgn: 1w + # Dprp: 1h + # TTLsig: 1d + # retire-safety: 2d + # + # According to RFC 7583: + # Iret = Dsgn + Dprp + TTLsig (+retire-safety) + # Iret = 1w + 1h + 1d + 2d = 10d1h = 241h + # + # Tact(N) = now - Iret - Lzsk + # = now - 241h - 30d = now - 241h - 720h + # = now - 961h + # Tpub(N+1) = now - Iret - Ipub + # = now - 241h - 26h + # = now - 267h + # Tact(N+1) = now - Iret = now - 241h + TactN="now-961h" + TpubN1="now-267h" + TactN1="now-241h" + TremN="now" + keytimes="-P ${TactN} -A ${TactN}" + oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" + newtimes="-P ${TpubN1} -A ${TactN1}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $O $TactN -z $U $TactN1 "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -z $R $TactN1 "$ZSK2" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $ZSK1 $ZSK2 + # Sign zone. + cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" + cp $infile $zonefile + $SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 5: -# The predecessor DNSKEY is removed long enough that is has become HIDDEN. -setup step5.zsk-prepub.autosign -# Subtract DNSKEY TTL + zone-propagation-delay from all the times (2h). -# Tact(N) = now - 961h - 2h = now - 963h -# Tpub(N+1) = now - 267h - 2h = now - 269h -# Tact(N+1) = now - 241h - 2h = now - 243h -# Trem(N) = Tact(N+1) + Iret = now -2h -TactN="now-963h" -TremN="now-2h" -TpubN1="now-269h" -TactN1="now-243h" -TremN="now-2h" -keytimes="-P ${TactN} -A ${TactN}" -oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" -newtimes="-P ${TpubN1} -A ${TactN1}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $U $TremN -z $H $TremN "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -z $O $TremN "$ZSK2" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $ZSK1 $ZSK2 -# Sign zone. -cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 5: + # The predecessor DNSKEY is removed long enough that is has become HIDDEN. + setup step5.zsk-prepub.$tld + # Subtract DNSKEY TTL + zone-propagation-delay from all the times (2h). + # Tact(N) = now - 961h - 2h = now - 963h + # Tpub(N+1) = now - 267h - 2h = now - 269h + # Tact(N+1) = now - 241h - 2h = now - 243h + # Trem(N) = Tact(N+1) + Iret = now -2h + TactN="now-963h" + TremN="now-2h" + TpubN1="now-269h" + TactN1="now-243h" + TremN="now-2h" + keytimes="-P ${TactN} -A ${TactN}" + oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" + newtimes="-P ${TpubN1} -A ${TactN1}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $U $TremN -z $H $TremN "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -z $O $TremN "$ZSK2" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $ZSK1 $ZSK2 + # Sign zone. + cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 -# Step 6: -# The predecessor DNSKEY can be purged. -setup step6.zsk-prepub.autosign -# Subtract purge-keys interval from all the times (1h). -TactN="now-964h" -TremN="now-3h" -TpubN1="now-270h" -TactN1="now-244h" -TremN="now-3h" -keytimes="-P ${TactN} -A ${TactN}" -oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" -newtimes="-P ${TpubN1} -A ${TactN1}" -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) -ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) -ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) -$SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 -$SETTIME -s -g $H -k $H $TremN -z $H $TremN "$ZSK1" >settime.out.$zone.2 2>&1 -$SETTIME -s -g $O -k $O $TactN1 -z $O $TremN "$ZSK2" >settime.out.$zone.3 2>&1 -# Set key rollover relationship. -key_successor $ZSK1 $ZSK2 -# Sign zone. -cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK1" >>"$infile" -private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" -cp $infile $zonefile -$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 + # Step 6: + # The predecessor DNSKEY can be purged. + setup step6.zsk-prepub.$tld + # Subtract purge-keys interval from all the times (1h). + TactN="now-964h" + TremN="now-3h" + TpubN1="now-270h" + TactN1="now-244h" + TremN="now-3h" + keytimes="-P ${TactN} -A ${TactN}" + oldtimes="-P ${TactN} -A ${TactN} -I ${TactN1} -D ${TremN}" + newtimes="-P ${TpubN1} -A ${TactN1}" + KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) + ZSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $oldtimes $zone 2>keygen.out.$zone.2) + ZSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $newtimes $zone 2>keygen.out.$zone.3) + $SETTIME -s -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" >settime.out.$zone.1 2>&1 + $SETTIME -s -g $H -k $H $TremN -z $H $TremN "$ZSK1" >settime.out.$zone.2 2>&1 + $SETTIME -s -g $O -k $O $TactN1 -z $O $TremN "$ZSK2" >settime.out.$zone.3 2>&1 + # Set key rollover relationship. + key_successor $ZSK1 $ZSK2 + # Sign zone. + cat template.db.in "${KSK}.key" "${ZSK1}.key" "${ZSK2}.key" >"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK1" >>"$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile" + cp $infile $zonefile + $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py b/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py index d5550bf509..232be8f7f9 100644 --- a/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py +++ b/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py @@ -13,8 +13,11 @@ from datetime import timedelta +import pytest + import isctest from isctest.kasp import Ipub, Iret +from isctest.util import param from rollover.common import ( pytestmark, alg, @@ -54,11 +57,22 @@ OFFSETS["step6-p"] = OFFSETS["step5-p"] - int(CONFIG["purge-keys"].total_seconds OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(CONFIG["purge-keys"].total_seconds()) -def test_zsk_prepub_step1(alg, size, ns3): - zone = "step1.zsk-prepub.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_zsk_prepub_step1(tld, alg, size, ns3): + zone = f"step1.zsk-prepub.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + # Note that the key was already generated during setup. + step = { # Introduce the first key. This will immediately be active. "zone": zone, @@ -71,14 +85,45 @@ def test_zsk_prepub_step1(alg, size, ns3): # already passed). "nextev": ZSK_LIFETIME - IPUB - timedelta(days=7), } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_zsk_prepub_step2(alg, size, ns3): - zone = "step2.zsk-prepub.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_zsk_prepub_step2(tld, alg, size, ns3): + zone = f"step2.zsk-prepub.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 1. + step = { + "zone": zone, + "keyprops": [ + f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}", + f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step2-p']}", + ], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block ZSK rollover for key {zone}/ECDSAP256SHA256/{tag} (policy {policy})" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # it is time to pre-publish the successor zsk. # zsk1 goal: omnipresent -> hidden @@ -95,14 +140,59 @@ def test_zsk_prepub_step2(alg, size, ns3): # that is the dnskey ttl plus the zone propagation delay "nextev": IPUB, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_zsk_prepub_step3(alg, size, ns3): - zone = "step3.zsk-prepub.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_zsk_prepub_step3(tld, alg, size, ns3): + zone = f"step3.zsk-prepub.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 2, but DNSKEY has become OMNIPRESENT. + step = { + "zone": zone, + "keyprops": [ + f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}", + f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step3-p']}", + f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:hidden offset:{OFFSETS['step3-s']}", + ], + "keyrelationships": [1, 2], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[2].key.tag + msg = f"keymgr-manual-mode: block transition ZSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state HIDDEN to state RUMOURED" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block transition ZSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE" + if msg in ns3.log: + # Force step. + isctest.log.debug( + f"keymgr-manual-mode blocking transition ZSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE, step again" + ) + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # predecessor zsk is no longer actively signing. successor zsk is # now actively signing. @@ -124,14 +214,47 @@ def test_zsk_prepub_step3(alg, size, ns3): # from the predecessor zsk. "smooth": True, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_zsk_prepub_step4(alg, size, ns3): - zone = "step4.zsk-prepub.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_zsk_prepub_step4(tld, alg, size, ns3): + zone = f"step4.zsk-prepub.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + if tld == "manual": + # Same as step 3, but zone signatures have become HIDDEN/OMNIPRESENT. + step = { + "zone": zone, + "keyprops": [ + f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step4-p']}", + f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:hidden offset:{OFFSETS['step4-p']}", + f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step4-s']}", + ], + "keyrelationships": [1, 2], + "manual-mode": True, + "nextev": None, + } + keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + # Check logs. + tag = keys[1].key.tag + msg = f"keymgr-manual-mode: block transition ZSK {zone}/ECDSAP256SHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE" + ns3.log.expect(msg) + + # Force step. + with ns3.watch_log_from_here() as watcher: + ns3.rndc(f"dnssec -step {zone}") + watcher.wait_for_line(f"keymgr: {zone} done") + step = { # predecessor zsk is no longer needed. all rrsets are signed with # the successor zsk. @@ -149,14 +272,24 @@ def test_zsk_prepub_step4(alg, size, ns3): # this is the dnskey ttl plus zone propagation delay. "nextev": KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_zsk_prepub_step5(alg, size, ns3): - zone = "step5.zsk-prepub.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_zsk_prepub_step5(tld, alg, size, ns3): + zone = f"step5.zsk-prepub.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { # predecessor zsk is now removed. # zsk1 dnskey: unretentive -> hidden @@ -172,14 +305,24 @@ def test_zsk_prepub_step5(alg, size, ns3): # elapsed. "nextev": ZSK_LIFETIME - IRET - IPUB - KEYTTLPROP, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) -def test_zsk_prepub_step6(alg, size, ns3): - zone = "step6.zsk-prepub.autosign" +@pytest.mark.parametrize( + "tld", + [ + param("autosign"), + param("manual"), + ], +) +def test_zsk_prepub_step6(tld, alg, size, ns3): + zone = f"step6.zsk-prepub.{tld}" + policy = f"{POLICY}-{tld}" isctest.kasp.wait_keymgr_done(ns3, zone) + # manual-mode: Nothing changing in the zone, no 'dnssec -step' required. + step = { # predecessor zsk is now purged. "zone": zone, @@ -189,4 +332,4 @@ def test_zsk_prepub_step6(alg, size, ns3): ], "nextev": None, } - isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) + isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 0592c64bf7..c0fb31ecad 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -6408,6 +6408,16 @@ keys ``insecure``. In this specific case, the existing key files should be moved to the zone's ``key-directory`` from the new configuration. +.. namedconf:statement:: manual-mode + :tags: dnssec + :short: Run key management in a manual mode. + + If enabled, BIND 9 does not automatically start and progress key rollovers, + instead the change is logged. Only after manual confirmation with + :option:`rndc dnssec -step ` the change is made. + + This feature is off by default. + .. namedconf:statement:: offline-ksk :tags: dnssec :short: Specifies whether the DNSKEY, CDS, and CDNSKEY RRsets are being signed offline. diff --git a/doc/misc/dnssec-policy.default.conf b/doc/misc/dnssec-policy.default.conf index d5571bac04..ce99f978fe 100644 --- a/doc/misc/dnssec-policy.default.conf +++ b/doc/misc/dnssec-policy.default.conf @@ -33,6 +33,7 @@ dnssec-policy "default" { signatures-validity-dnskey 14d; // Zone parameters + manual-mode no; inline-signing yes; max-zone-ttl 86400; zone-propagation-delay 300; diff --git a/doc/misc/options b/doc/misc/options index 3215fc7af7..5bb047d95a 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -16,6 +16,7 @@ dnssec-policy { dnskey-ttl ; inline-signing ; keys { ( csk | ksk | zsk ) [ key-directory | key-store ] lifetime algorithm [ tag-range ] [ ]; ... }; + manual-mode ; max-zone-ttl ; nsec3param [ iterations ] [ optout ] [ salt-length ]; offline-ksk ; diff --git a/lib/dns/include/dns/kasp.h b/lib/dns/include/dns/kasp.h index e2b0ea94df..999e08ddcb 100644 --- a/lib/dns/include/dns/kasp.h +++ b/lib/dns/include/dns/kasp.h @@ -108,6 +108,7 @@ struct dns_kasp { dns_ttl_t zone_max_ttl; uint32_t zone_propagation_delay; bool inline_signing; + bool manual_mode; /* Parent settings */ dns_ttl_t parent_ds_ttl; @@ -439,6 +440,30 @@ dns_kasp_setinlinesigning(dns_kasp_t *kasp, bool value); *\li 'kasp' is a valid, thawed kasp. */ +bool +dns_kasp_manualmode(dns_kasp_t *kasp); +/*%< + * Should we use manual-mode for this DNSSEC policy? + * + * Requires: + * + *\li 'kasp' is a valid, frozen kasp. + * + * Returns: + * + *\li true or false. + */ + +void +dns_kasp_setmanualmode(dns_kasp_t *kasp, bool value); +/*%< + * Set manual-mode. + * + * Requires: + * + *\li 'kasp' is a valid, thawed kasp. + */ + dns_ttl_t dns_kasp_zonemaxttl(dns_kasp_t *kasp, bool fallback); /*%< diff --git a/lib/dns/include/dns/keymgr.h b/lib/dns/include/dns/keymgr.h index bf24457e3c..b8cbf2d355 100644 --- a/lib/dns/include/dns/keymgr.h +++ b/lib/dns/include/dns/keymgr.h @@ -21,6 +21,11 @@ #include +#define DNS_KEYMGRATTR_NONE 0x00 /*%< No ordering. */ +#define DNS_KEYMGRATTR_S2I 0x01 /*%< Secure to insecure. */ +#define DNS_KEYMGRATTR_NOROLL 0x02 /*%< No rollover allowed. */ +#define DNS_KEYMGRATTR_FORCESTEP 0x04 /*%< Force next step in manual-mode */ + void dns_keymgr_settime_syncpublish(dst_key_t *key, dns_kasp_t *kasp, bool first); /*%< @@ -36,7 +41,8 @@ isc_result_t dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, isc_mem_t *mctx, dns_dnsseckeylist_t *keyring, dns_dnsseckeylist_t *dnskeys, const char *keydir, - dns_kasp_t *kasp, isc_stdtime_t now, isc_stdtime_t *nexttime); + dns_kasp_t *kasp, uint8_t options, isc_stdtime_t now, + isc_stdtime_t *nexttime); /*%< * Manage keys in 'keyring' and update timing data according to 'kasp' policy. * Create new keys for 'origin' if necessary. Append all such keys, along @@ -45,6 +51,10 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, * Update key states and store changes back to disk. Store when to run next * in 'nexttime'. * + * If 'options' has DNS_KEYMGRATTR_FORCESTEP set, the next steps in the process + * are allowed, even if 'kasp' has 'manual-mode' enabled. Other options should + * not be set in 'options'. + * * Requires: *\li 'origin' is a valid FQDN. *\li 'mctx' is a valid memory context. diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 193121b920..39c1183045 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -104,6 +104,7 @@ typedef enum { DNS_ZONEOPT_CHECKSVCB = 1 << 30, /*%< check SVBC records */ DNS_ZONEOPT_ZONEVERSION = 1U << 31, /*%< enable zoneversion */ DNS_ZONEOPT_FULLSIGN = 1ULL << 32, /*%< fully sign zone */ + DNS_ZONEOPT_FORCEKEYMGR = 1ULL << 33, /*%< force keymgr step */ DNS_ZONEOPT___MAX = UINT64_MAX, /* trick to make the ENUM 64-bit wide */ } dns_zoneopt_t; @@ -2210,7 +2211,7 @@ dns_zone_getprivatetype(dns_zone_t *zone); */ void -dns_zone_rekey(dns_zone_t *zone, bool fullsign); +dns_zone_rekey(dns_zone_t *zone, bool fullsign, bool forcekeymgr); /*%< * Update the zone's DNSKEY set from the key repository. * @@ -2218,6 +2219,9 @@ dns_zone_rekey(dns_zone_t *zone, bool fullsign); * the zone with the new key. Otherwise, if there are no keys or * if the new keys are for algorithms that have already signed the * zone, then the zone can be re-signed incrementally. + * + * If 'forcekeymgr' is true, trigger a rekey event and allow the + * next steps in the run to happen. */ isc_result_t diff --git a/lib/dns/kasp.c b/lib/dns/kasp.c index 722c069d3a..884e3e33ec 100644 --- a/lib/dns/kasp.c +++ b/lib/dns/kasp.c @@ -273,6 +273,22 @@ dns_kasp_setinlinesigning(dns_kasp_t *kasp, bool value) { kasp->inline_signing = value; } +bool +dns_kasp_manualmode(dns_kasp_t *kasp) { + REQUIRE(DNS_KASP_VALID(kasp)); + REQUIRE(kasp->frozen); + + return kasp->manual_mode; +} + +void +dns_kasp_setmanualmode(dns_kasp_t *kasp, bool value) { + REQUIRE(DNS_KASP_VALID(kasp)); + REQUIRE(!kasp->frozen); + + kasp->manual_mode = value; +} + dns_ttl_t dns_kasp_zonemaxttl(dns_kasp_t *kasp, bool fallback) { REQUIRE(DNS_KASP_VALID(kasp)); diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 3a18b26224..935d989c4d 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -342,7 +342,8 @@ keymgr_prepublication_time(dns_dnsseckey_t *key, dns_kasp_t *kasp, } static void -keymgr_key_retire(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now) { +keymgr_key_retire(dns_dnsseckey_t *key, dns_kasp_t *kasp, uint8_t opts, + isc_stdtime_t now) { char keystr[DST_KEY_FORMATSIZE]; isc_result_t ret; isc_stdtime_t retire; @@ -352,17 +353,39 @@ keymgr_key_retire(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now) { REQUIRE(key != NULL); REQUIRE(key->key != NULL); - /* This key wants to retire and hide in a corner. */ + dst_key_format(key->key, keystr, sizeof(keystr)); + + ret = dst_key_getstate(key->key, DST_KEY_GOAL, &s); + INSIST(ret == ISC_R_SUCCESS); + + if (dns_kasp_manualmode(kasp) && + (opts & DNS_KEYMGRATTR_FORCESTEP) == 0 && s != HIDDEN) + { + isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, + ISC_LOG_INFO, + "keymgr-manual-mode: block retire DNSKEY " + "%s (%s)", + keystr, keymgr_keyrole(key->key)); + return; + } else { + /* This key wants to retire and hide in a corner. */ + isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, + ISC_LOG_INFO, "keymgr: retire DNSKEY %s (%s)", + keystr, keymgr_keyrole(key->key)); + + dst_key_setstate(key->key, DST_KEY_GOAL, HIDDEN); + } + + /* + * This key may not have key states set yet. Pretend as if they are + * in the OMNIPRESENT state. + */ ret = dst_key_gettime(key->key, DST_TIME_INACTIVE, &retire); if (ret != ISC_R_SUCCESS || (retire > now)) { dst_key_settime(key->key, DST_TIME_INACTIVE, now); } - dst_key_setstate(key->key, DST_KEY_GOAL, HIDDEN); keymgr_settime_remove(key, kasp); - /* This key may not have key states set yet. Pretend as if they are - * in the OMNIPRESENT state. - */ if (dst_key_getstate(key->key, DST_KEY_DNSKEY, &s) != ISC_R_SUCCESS) { dst_key_setstate(key->key, DST_KEY_DNSKEY, OMNIPRESENT); dst_key_settime(key->key, DST_TIME_DNSKEY, now); @@ -391,11 +414,6 @@ keymgr_key_retire(dns_dnsseckey_t *key, dns_kasp_t *kasp, isc_stdtime_t now) { dst_key_settime(key->key, DST_TIME_ZRRSIG, now); } } - - dst_key_format(key->key, keystr, sizeof(keystr)); - isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, - ISC_LOG_INFO, "keymgr: retire DNSKEY %s (%s)", keystr, - keymgr_keyrole(key->key)); } /* Update lifetime and retire and remove time accordingly. */ @@ -963,7 +981,7 @@ keymgr_dnskey_hidden_or_chained(dns_dnsseckeylist_t *keyring, */ static bool keymgr_have_ds(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, int type, - dst_key_state_t next_state, bool secure_to_insecure) { + dst_key_state_t next_state, uint8_t opts) { /* (3a) */ dst_key_state_t states[2][NUM_KEYSTATES] = { /* DNSKEY, ZRRSIG, KRRSIG, DS */ @@ -981,7 +999,7 @@ keymgr_have_ds(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, int type, states[0], na, false, false) || keymgr_key_exists_with_state(keyring, key, type, next_state, states[1], na, false, false) || - (secure_to_insecure && + ((opts & DNS_KEYMGRATTR_S2I) != 0 && keymgr_key_exists_with_state(keyring, key, type, next_state, na, na, false, false)); } @@ -1220,17 +1238,14 @@ keymgr_policy_approval(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, */ static bool keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, - int type, dst_key_state_t next_state, - bool secure_to_insecure) { + int type, dst_key_state_t next_state, uint8_t opts) { /* Debug logging. */ if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) { bool rule1a, rule1b, rule2a, rule2b, rule3a, rule3b; char keystr[DST_KEY_FORMATSIZE]; dst_key_format(key->key, keystr, sizeof(keystr)); - rule1a = keymgr_have_ds(keyring, key, type, NA, - secure_to_insecure); - rule1b = keymgr_have_ds(keyring, key, type, next_state, - secure_to_insecure); + rule1a = keymgr_have_ds(keyring, key, type, NA, opts); + rule1b = keymgr_have_ds(keyring, key, type, next_state, opts); rule2a = keymgr_have_dnskey(keyring, key, type, NA); rule2b = keymgr_have_dnskey(keyring, key, type, next_state); rule3a = keymgr_have_rrsig(keyring, key, type, NA); @@ -1255,9 +1270,8 @@ keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, * invalid state. If the rule check passes, also check if * the next state is also still a valid situation. */ - (!keymgr_have_ds(keyring, key, type, NA, secure_to_insecure) || - keymgr_have_ds(keyring, key, type, next_state, - secure_to_insecure)) && + (!keymgr_have_ds(keyring, key, type, NA, opts) || + keymgr_have_ds(keyring, key, type, next_state, opts)) && /* * Rule 2: There must be a DNSKEY at all times. Again, first * check the current situation, then assess the next state. @@ -1448,8 +1462,10 @@ keymgr_transition_time(dns_dnsseckey_t *key, int type, */ static isc_result_t keymgr_update(dns_dnsseckeylist_t *keyring, dns_kasp_t *kasp, isc_stdtime_t now, - isc_stdtime_t *nexttime, bool secure_to_insecure) { + isc_stdtime_t *nexttime, uint8_t opts) { + isc_result_t result = DNS_R_UNCHANGED; bool changed; + bool force = ((opts & DNS_KEYMGRATTR_FORCESTEP) != 0); /* Repeat until nothing changed. */ transition: @@ -1534,8 +1550,7 @@ transition: /* Is the transition DNSSEC safe? */ if (!keymgr_transition_allowed(keyring, dkey, i, - next_state, - secure_to_insecure)) + next_state, opts)) { /* No, this would make the zone bogus. */ isc_log_write( @@ -1572,6 +1587,28 @@ transition: continue; } + /* + * Are we allowed to make the transition automatically? + */ + if (next_state != OMNIPRESENT && next_state != HIDDEN) { + if (dns_kasp_manualmode(kasp) && !force) { + isc_log_write( + DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, + ISC_LOG_INFO, + "keymgr-manual-mode: block " + "transition " + "%s %s type %s " + "state %s to state %s", + keymgr_keyrole(dkey->key), + keystr, keystatetags[i], + keystatestrings[state], + keystatestrings[next_state]); + continue; + } + } + + /* It is safe to make the transition. */ isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1), "keymgr: transition %s %s type %s " @@ -1580,7 +1617,6 @@ transition: keystatetags[i], keystatestrings[state], keystatestrings[next_state]); - /* It is safe to make the transition. */ dst_key_setstate(dkey->key, i, next_state); dst_key_settime(dkey->key, keystatetimes[i], now); INSIST(dst_key_ismodified(dkey->key)); @@ -1590,10 +1626,13 @@ transition: /* We changed something, continue processing. */ if (changed) { + result = ISC_R_SUCCESS; + /* No longer force for the next run */ + force = false; goto transition; } - return ISC_R_SUCCESS; + return result; } /* @@ -1716,9 +1755,10 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, dns_dnsseckeylist_t *keyring, dns_dnsseckeylist_t *newkeys, const dns_name_t *origin, dns_rdataclass_t rdclass, dns_kasp_t *kasp, const char *keydir, uint32_t lifetime, - bool rollover, isc_stdtime_t now, isc_stdtime_t *nexttime, + uint8_t opts, isc_stdtime_t now, isc_stdtime_t *nexttime, isc_mem_t *mctx) { char keystr[DST_KEY_FORMATSIZE]; + char namestr[DNS_NAME_FORMATSIZE]; isc_stdtime_t retire = 0, active = 0, prepub = 0; dns_dnsseckey_t *new_key = NULL; dst_key_t *dst_key = NULL; @@ -1795,7 +1835,7 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, /* * If rollover is not allowed, warn. */ - if (!rollover) { + if ((opts & DNS_KEYMGRATTR_NOROLL) != 0) { dst_key_format(active_key->key, keystr, sizeof(keystr)); isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, @@ -1806,7 +1846,6 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, return ISC_R_SUCCESS; } } else if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) { - char namestr[DNS_NAME_FORMATSIZE]; dns_name_format(origin, namestr, sizeof(namestr)); isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1), @@ -1830,6 +1869,49 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key, } } + if (dns_kasp_manualmode(kasp) && (opts & DNS_KEYMGRATTR_FORCESTEP) == 0) + { + if (active_key != NULL && new_key != NULL) { + char keystr2[DST_KEY_FORMATSIZE]; + dst_key_format(active_key->key, keystr, sizeof(keystr)); + dst_key_format(new_key->key, keystr2, sizeof(keystr2)); + dns_name_format(origin, namestr, sizeof(namestr)); + isc_log_write( + DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, + ISC_LOG_INFO, + "keymgr-manual-mode: block %s rollover for key " + "%s to key %s (policy %s)", + keymgr_keyrole(active_key->key), keystr, + keystr2, dns_kasp_getname(kasp)); + } else if (active_key != NULL) { + dst_key_format(active_key->key, keystr, sizeof(keystr)); + dns_name_format(origin, namestr, sizeof(namestr)); + isc_log_write(DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, + "keymgr-manual-mode: block %s rollover " + "for key %s (policy %s)", + keymgr_keyrole(active_key->key), keystr, + dns_kasp_getname(kasp)); + } else if (new_key != NULL) { + dst_key_format(new_key->key, keystr, sizeof(keystr)); + dns_name_format(origin, namestr, sizeof(namestr)); + isc_log_write(DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, + "keymgr-manual-mode: block %s " + "introduction %s (policy %s)", + keymgr_keyrole(new_key->key), keystr, + dns_kasp_getname(kasp)); + } else { + dns_name_format(origin, namestr, sizeof(namestr)); + isc_log_write(DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, + "keymgr-manual-mode: block new key " + "generation for zone %s (policy %s)", + namestr, dns_kasp_getname(kasp)); + } + return ISC_R_SUCCESS; + } + if (new_key == NULL) { /* No key available in keyring, create a new one. */ bool csk = (dns_kasp_key_ksk(kaspkey) && @@ -2039,10 +2121,10 @@ isc_result_t dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, isc_mem_t *mctx, dns_dnsseckeylist_t *keyring, dns_dnsseckeylist_t *dnskeys, const char *keydir, - dns_kasp_t *kasp, isc_stdtime_t now, isc_stdtime_t *nexttime) { - isc_result_t result = ISC_R_SUCCESS; + dns_kasp_t *kasp, uint8_t opts, isc_stdtime_t now, + isc_stdtime_t *nexttime) { + isc_result_t result = DNS_R_UNCHANGED; dns_dnsseckeylist_t newkeys; - bool secure_to_insecure = false; int numkeys = 0; int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); char keystr[DST_KEY_FORMATSIZE]; @@ -2103,7 +2185,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, /* No match, so retire unwanted retire key. */ if (!found_match) { - keymgr_key_retire(dkey, kasp, now); + keymgr_key_retire(dkey, kasp, opts, now); } /* Check purge-keys interval. */ @@ -2129,7 +2211,6 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) { uint32_t lifetime = dns_kasp_key_lifetime(kkey); dns_dnsseckey_t *active_key = NULL; - bool rollover_allowed = true; /* Do we have keys available for this kasp key? */ ISC_LIST_FOREACH(*keyring, dkey, link) { @@ -2168,7 +2249,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, * Retire excess keys in use. */ keymgr_key_retire(dkey, kasp, - now); + opts, now); } continue; } @@ -2206,7 +2287,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, keystr, keymgr_keyrole(dnskey->key), dns_kasp_getname(kasp)); - rollover_allowed = false; + opts |= DNS_KEYMGRATTR_NOROLL; active_key = dnskey; break; } @@ -2214,10 +2295,11 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, } /* See if this key requires a rollover. */ - RETERR(keymgr_key_rollover(kkey, active_key, keyring, &newkeys, - origin, rdclass, kasp, keydir, - lifetime, rollover_allowed, now, - nexttime, mctx)); + RETERR(keymgr_key_rollover( + kkey, active_key, keyring, &newkeys, origin, rdclass, + kasp, keydir, lifetime, opts, now, nexttime, mctx)); + + opts &= ~DNS_KEYMGRATTR_NOROLL; } /* Walked all kasp key configurations. Append new keys. */ @@ -2229,10 +2311,12 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, * If the policy has an empty key list, this means the zone is going * back to unsigned. */ - secure_to_insecure = dns_kasp_keylist_empty(kasp); + if (dns_kasp_keylist_empty(kasp)) { + opts |= DNS_KEYMGRATTR_S2I; + } /* Read to update key states. */ - keymgr_update(keyring, kasp, now, nexttime, secure_to_insecure); + isc_result_t retval = keymgr_update(keyring, kasp, now, nexttime, opts); /* Store key states and update hints. */ ISC_LIST_FOREACH(*keyring, dkey, link) { @@ -2240,6 +2324,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, if (dst_key_getttl(dkey->key) != dns_kasp_dnskeyttl(kasp)) { dst_key_setttl(dkey->key, dns_kasp_dnskeyttl(kasp)); modified = true; + retval = ISC_R_SUCCESS; } if (modified && !dkey->purge) { const char *directory = dst_key_directory(dkey->key); @@ -2265,10 +2350,9 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, dst_key_setmodified(dkey->key, false); } - result = ISC_R_SUCCESS; - + result = retval; failure: - if (result != ISC_R_SUCCESS) { + if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) { ISC_LIST_FOREACH(newkeys, newkey, link) { ISC_LIST_UNLINK(newkeys, newkey, link); INSIST(newkey->key != NULL); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 0ca4efad23..edd6f5d143 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -21211,7 +21211,7 @@ checkds_done(void *arg) { /* Rekey after checkds. */ if (rekey) { - dns_zone_rekey(zone, false); + dns_zone_rekey(zone, false, false); } failure: @@ -22233,6 +22233,8 @@ zone_rekey(dns_zone_t *zone) { bool newalg = false; bool fullsign; bool offlineksk = false; + bool kasp_change = false; + uint8_t options = 0; uint32_t sigval = 0; dns_ttl_t ttl = 3600; const char *dir = NULL; @@ -22351,6 +22353,14 @@ zone_rekey(dns_zone_t *zone) { */ fullsign = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FULLSIGN); + /* + * True when called from "rndc dnssec -step". Indicates the zone + * is allowed to do the next step(s) in the keymgr process. + */ + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FORCEKEYMGR)) { + options |= DNS_KEYMGRATTR_FORCESTEP; + } + if (offlineksk) { /* Lookup the correct bundle in the SKR. */ LOCK_ZONE(zone); @@ -22456,10 +22466,14 @@ zone_rekey(dns_zone_t *zone) { dns_zone_lock_keyfiles(zone); result = dns_keymgr_run(&zone->origin, zone->rdclass, mctx, &keys, &dnskeys, dir, - kasp, now, &nexttime); + kasp, options, now, &nexttime); dns_zone_unlock_keyfiles(zone); - if (result != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + kasp_change = true; + } else if (result == DNS_R_UNCHANGED) { + result = ISC_R_SUCCESS; + } else { dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:dns_keymgr_run " "failed: %s", @@ -22677,7 +22691,7 @@ zone_rekey(dns_zone_t *zone) { "allowed"); } - if (newactive || fullsign || sane_diff) { + if (newactive || fullsign || sane_diff || kasp_change) { CHECK(dns_diff_apply(&diff, db, ver)); CHECK(clean_nsec3param(zone, db, ver, &diff)); CHECK(add_signing_records(db, zone->privatetype, ver, @@ -22936,6 +22950,13 @@ failure: 0); isc_time_nowplusinterval(&zone->refreshkeytime, &ival); } + + /* + * Clear forcekeymgr flag, if it was set, so we don't do + * another force next time. + */ + DNS_ZONE_CLROPTION(zone, DNS_ZONEOPT_FORCEKEYMGR); + UNLOCK_ZONE(zone); dns_diff_clear(&diff); @@ -22974,7 +22995,7 @@ failure: } void -dns_zone_rekey(dns_zone_t *zone, bool fullsign) { +dns_zone_rekey(dns_zone_t *zone, bool fullsign, bool forcekeymgr) { isc_time_t now; if (zone->type == dns_zone_primary && zone->loop != NULL) { @@ -22983,6 +23004,9 @@ dns_zone_rekey(dns_zone_t *zone, bool fullsign) { if (fullsign) { DNS_ZONE_SETOPTION(zone, DNS_ZONEOPT_FULLSIGN); } + if (forcekeymgr) { + DNS_ZONE_SETOPTION(zone, DNS_ZONEOPT_FORCEKEYMGR); + } now = isc_time_now(); zone->refreshkeytime = now; diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c index 4bc62c0ba4..d83e0af46b 100644 --- a/lib/isccfg/kaspconf.c +++ b/lib/isccfg/kaspconf.c @@ -473,7 +473,7 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, uint32_t zonepropdelay = 0, parentpropdelay = 0; uint32_t ipub = 0, iret = 0; uint32_t ksk_min_lifetime = 0, zsk_min_lifetime = 0; - bool offline_ksk = false; + bool offline_ksk = false, manual_mode = false; REQUIRE(config != NULL); REQUIRE(kaspp != NULL && *kaspp == NULL); @@ -578,6 +578,13 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp, dns_kasp_setinlinesigning(kasp, true); } + obj = NULL; + (void)confget(maps, "manual-mode", &obj); + if (obj != NULL) { + manual_mode = cfg_obj_asboolean(obj); + } + dns_kasp_setmanualmode(kasp, manual_mode); + maxttl = get_duration(maps, "max-zone-ttl", DNS_KASP_ZONE_MAXTTL); dns_kasp_setzonemaxttl(kasp, maxttl); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 67824c75e8..6d204ebeeb 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2213,6 +2213,7 @@ static cfg_clausedef_t dnssecpolicy_clauses[] = { { "dnskey-ttl", &cfg_type_duration, 0 }, { "inline-signing", &cfg_type_boolean, 0 }, { "keys", &cfg_type_kaspkeys, 0 }, + { "manual-mode", &cfg_type_boolean, 0 }, { "max-zone-ttl", &cfg_type_duration, 0 }, { "nsec3param", &cfg_type_nsec3, 0 }, { "offline-ksk", &cfg_type_boolean, 0 },