mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 01:59:26 +00:00
new: usr: Add manual mode configuration option to dnsec-policy
Add a new option ``manual-mode`` to :any:`dnssec-policy`. The intended use is that if it is enabled, it will not automatically move to the next state transition, but instead the transition is logged. Only after manual confirmation with ``rndc dnssec -step`` the transition is made. Closes #4606 Merge branch '4606-dnssec-policy-dry-run' into 'main' See merge request isc-projects/bind9!10774
This commit is contained in:
commit
888b5f55a8
@ -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\
|
||||
"
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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,6 +934,17 @@ def _check_dnskeys(dnskeys, keys, cdnskey=False):
|
||||
delete_md = f"Sync{delete_md}"
|
||||
|
||||
for key in keys:
|
||||
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
|
||||
@ -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()
|
||||
|
||||
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):
|
||||
"""
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -30,18 +30,19 @@ 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.
|
||||
#
|
||||
|
||||
for tld in kasp manual; do
|
||||
# Step 1:
|
||||
# Introduce the first key. This will immediately be active.
|
||||
setup step1.csk-algorithm-roll.kasp
|
||||
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 -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1)
|
||||
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"
|
||||
@ -50,15 +51,15 @@ $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $in
|
||||
|
||||
# Step 2:
|
||||
# After the publication interval has passed the DNSKEY is OMNIPRESENT.
|
||||
setup step2.csk-algorithm-roll.kasp
|
||||
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 -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)
|
||||
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.
|
||||
@ -71,14 +72,14 @@ $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $in
|
||||
|
||||
# Step 3:
|
||||
# The zone signatures are also OMNIPRESENT.
|
||||
setup step3.csk-algorithm-roll.kasp
|
||||
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 -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)
|
||||
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.
|
||||
@ -91,14 +92,14 @@ $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $in
|
||||
|
||||
# Step 4:
|
||||
# The DS is swapped and can become OMNIPRESENT.
|
||||
setup step4.csk-algorithm-roll.kasp
|
||||
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 -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)
|
||||
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.
|
||||
@ -111,14 +112,14 @@ $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $in
|
||||
|
||||
# Step 5:
|
||||
# The DNSKEY is removed long enough to be HIDDEN.
|
||||
setup step5.csk-algorithm-roll.kasp
|
||||
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 -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)
|
||||
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.
|
||||
@ -131,14 +132,14 @@ $SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $in
|
||||
|
||||
# Step 6:
|
||||
# The RRSIGs have been removed long enough to be HIDDEN.
|
||||
setup step6.csk-algorithm-roll.kasp
|
||||
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 -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)
|
||||
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.
|
||||
@ -148,3 +149,4 @@ 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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -30,13 +30,14 @@ 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.
|
||||
#
|
||||
|
||||
for tld in kasp manual; do
|
||||
# Step 1:
|
||||
# Introduce the first key. This will immediately be active.
|
||||
setup step1.algorithm-roll.kasp
|
||||
setup step1.algorithm-roll.$tld
|
||||
echo "$zone" >>zones
|
||||
TactN="now-7d"
|
||||
TsbmN="now-161h"
|
||||
@ -54,7 +55,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
|
||||
# Step 2:
|
||||
# After the publication interval has passed the DNSKEY is OMNIPRESENT.
|
||||
setup step2.algorithm-roll.kasp
|
||||
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
|
||||
@ -84,7 +85,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
|
||||
# Step 3:
|
||||
# The zone signatures are also OMNIPRESENT.
|
||||
setup step3.algorithm-roll.kasp
|
||||
setup step3.algorithm-roll.$tld
|
||||
# The time passed since the new algorithm keys have been introduced is 7 hours.
|
||||
TpubN1="now-7h"
|
||||
TsbmN1="now"
|
||||
@ -113,7 +114,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
|
||||
# Step 4:
|
||||
# The DS is swapped and can become OMNIPRESENT.
|
||||
setup step4.algorithm-roll.kasp
|
||||
setup step4.algorithm-roll.$tld
|
||||
# The time passed since the DS has been swapped is 3 hours.
|
||||
TpubN1="now-10h"
|
||||
TsbmN1="now-3h"
|
||||
@ -142,7 +143,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
|
||||
# Step 5:
|
||||
# The DNSKEY is removed long enough to be HIDDEN.
|
||||
setup step5.algorithm-roll.kasp
|
||||
setup step5.algorithm-roll.$tld
|
||||
# The time passed since the DNSKEY has been removed is 2 hours.
|
||||
TpubN1="now-12h"
|
||||
TsbmN1="now-5h"
|
||||
@ -171,7 +172,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
|
||||
# Step 6:
|
||||
# The RRSIGs have been removed long enough to be HIDDEN.
|
||||
setup step6.algorithm-roll.kasp
|
||||
setup step6.algorithm-roll.$tld
|
||||
# Additional time passed: 7h.
|
||||
TpubN1="now-19h"
|
||||
TsbmN1="now-12h"
|
||||
@ -197,3 +198,4 @@ 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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -40,16 +40,17 @@ 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).
|
||||
#
|
||||
|
||||
for tld in autosign manual; do
|
||||
# Step 1:
|
||||
# Introduce the first key. This will immediately be active.
|
||||
setup step1.csk-roll1.autosign
|
||||
setup step1.csk-roll1.$tld
|
||||
TactN="now-7d"
|
||||
keytimes="-P ${TactN} -A ${TactN}"
|
||||
CSK=$($KEYGEN -k csk-roll1 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
|
||||
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"
|
||||
@ -58,7 +59,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -
|
||||
|
||||
# Step 2:
|
||||
# It is time to introduce the new CSK.
|
||||
setup step2.csk-roll1.autosign
|
||||
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
|
||||
@ -77,7 +78,7 @@ setup step2.csk-roll1.autosign
|
||||
# = 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)
|
||||
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"
|
||||
@ -86,7 +87,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -
|
||||
|
||||
# Step 3:
|
||||
# It is time to submit the DS and to roll signatures.
|
||||
setup step3.csk-roll1.autosign
|
||||
setup step3.csk-roll1.$tld
|
||||
# According to RFC 7583:
|
||||
#
|
||||
# Tsbm(N+1) >= Trdy(N+1)
|
||||
@ -126,8 +127,8 @@ 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)
|
||||
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.
|
||||
@ -144,7 +145,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -
|
||||
# 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
|
||||
setup step4.csk-roll1.$tld
|
||||
# According to RFC 7583:
|
||||
# Trem(N) = Tret(N) - Iret + IretZ
|
||||
# now = Tsbm(N+1) + Iret
|
||||
@ -172,8 +173,8 @@ 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)
|
||||
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.
|
||||
@ -188,7 +189,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -
|
||||
# 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
|
||||
setup step5.csk-roll1.$tld
|
||||
# Subtract DNSKEY TTL plus zone propagation delay from all the times (2h).
|
||||
TpubN="now-4470h"
|
||||
TactN="now-4445h"
|
||||
@ -200,8 +201,8 @@ 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)
|
||||
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.
|
||||
@ -216,7 +217,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -
|
||||
# Step 6:
|
||||
# After the retire interval has passed the predecessor DNSKEY can be
|
||||
# removed from the zone.
|
||||
setup step6.csk-roll1.autosign
|
||||
setup step6.csk-roll1.$tld
|
||||
# According to RFC 7583:
|
||||
# Trem(N) = Tret(N) + IretZ
|
||||
# Tret(N) = Tact(N) + Lcsk
|
||||
@ -244,8 +245,8 @@ 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)
|
||||
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.
|
||||
@ -259,7 +260,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -
|
||||
|
||||
# Step 7:
|
||||
# Some time later the predecessor DNSKEY enters the HIDDEN state.
|
||||
setup step7.csk-roll1.autosign
|
||||
setup step7.csk-roll1.$tld
|
||||
# Subtract DNSKEY TTL plus zone propagation delay from all the times (2h).
|
||||
TpubN="now-5093h"
|
||||
TactN="now-5068h"
|
||||
@ -271,8 +272,8 @@ 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)
|
||||
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.
|
||||
@ -286,7 +287,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-384" -s now-1h -e now+30d -o $zone -O raw -
|
||||
|
||||
# Step 8:
|
||||
# The predecessor DNSKEY can be purged.
|
||||
setup step8.csk-roll1.autosign
|
||||
setup step8.csk-roll1.$tld
|
||||
TpubN="now-5094h"
|
||||
TactN="now-5069h"
|
||||
TretN="now-630h"
|
||||
@ -298,8 +299,8 @@ 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)
|
||||
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.
|
||||
@ -310,3 +311,4 @@ 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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -40,18 +40,19 @@ 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.
|
||||
#
|
||||
|
||||
for tld in autosign manual; do
|
||||
# Step 1:
|
||||
# Introduce the first key. This will immediately be active.
|
||||
setup step1.csk-roll2.autosign
|
||||
setup step1.csk-roll2.$tld
|
||||
TactN="now-7d"
|
||||
keytimes="-P ${TactN} -A ${TactN}"
|
||||
CSK=$($KEYGEN -k csk-roll2 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
|
||||
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"
|
||||
@ -60,7 +61,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $z
|
||||
|
||||
# Step 2:
|
||||
# It is time to introduce the new CSK.
|
||||
setup step2.csk-roll2.autosign
|
||||
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
|
||||
@ -79,7 +80,7 @@ setup step2.csk-roll2.autosign
|
||||
# = 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)
|
||||
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"
|
||||
@ -88,7 +89,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $z
|
||||
|
||||
# Step 3:
|
||||
# It is time to submit the DS and to roll signatures.
|
||||
setup step3.csk-roll2.autosign
|
||||
setup step3.csk-roll2.$tld
|
||||
# According to RFC 7583:
|
||||
#
|
||||
# Tsbm(N+1) >= Trdy(N+1)
|
||||
@ -128,8 +129,8 @@ 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)
|
||||
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.
|
||||
@ -146,7 +147,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $z
|
||||
# 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
|
||||
setup step4.csk-roll2.$tld
|
||||
# According to RFC 7583:
|
||||
# Trem(N) = Tret(N) + IretZ
|
||||
#
|
||||
@ -176,8 +177,8 @@ 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)
|
||||
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.
|
||||
@ -192,7 +193,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $z
|
||||
# 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
|
||||
setup step5.csk-roll2.$tld
|
||||
# Subtract Iret (170h) - IretZ (38h) = 132h.
|
||||
#
|
||||
# Tpub(N) = now - 4502h - 132h = now - 4634h
|
||||
@ -213,8 +214,8 @@ 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)
|
||||
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.
|
||||
@ -228,7 +229,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $z
|
||||
|
||||
# Step 6:
|
||||
# Some time later the predecessor DNSKEY enters the HIDDEN state.
|
||||
setup step6.csk-roll2.autosign
|
||||
setup step6.csk-roll2.$tld
|
||||
# Subtract DNSKEY TTL plus zone propagation delay (2h).
|
||||
#
|
||||
# Tpub(N) = now - 4634h - 2h = now - 4636h
|
||||
@ -249,8 +250,8 @@ 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)
|
||||
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.
|
||||
@ -264,7 +265,7 @@ $SIGNER -S -z -x -G "cdnskey,cds:sha-256,cds:sha-384" -s now-1h -e now+30d -o $z
|
||||
|
||||
# Step 7:
|
||||
# The predecessor DNSKEY can be purged, but purge-keys is disabled.
|
||||
setup step7.csk-roll2.autosign
|
||||
setup step7.csk-roll2.$tld
|
||||
# Subtract 90 days (default, 2160h) from all the times.
|
||||
#
|
||||
# Tpub(N) = now - 4636h - 2160h = now - 6796h
|
||||
@ -285,8 +286,8 @@ 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)
|
||||
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.
|
||||
@ -297,3 +298,4 @@ 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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -40,26 +40,27 @@ 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.
|
||||
#
|
||||
|
||||
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.autosign
|
||||
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
|
||||
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 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
|
||||
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"
|
||||
@ -68,14 +69,14 @@ $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $i
|
||||
|
||||
# Step 3:
|
||||
# The zone signatures have been published long enough to become OMNIPRESENT.
|
||||
setup step3.enable-dnssec.autosign
|
||||
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 -l kasp.conf $keytimes $zone 2>keygen.out.$zone.1)
|
||||
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"
|
||||
@ -84,7 +85,7 @@ $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O raw -f "${zonefile}.signed" $i
|
||||
|
||||
# Step 4:
|
||||
# The DS has been submitted long enough ago to become OMNIPRESENT.
|
||||
setup step4.enable-dnssec.autosign
|
||||
setup step4.enable-dnssec.$tld
|
||||
# DS TTL: 2 hour (7200 seconds)
|
||||
# parent-propagation-delay: 1 hour (3600 seconds)
|
||||
# Total aditional time: 10800 seconds
|
||||
@ -92,9 +93,10 @@ setup step4.enable-dnssec.autosign
|
||||
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)
|
||||
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
|
||||
|
@ -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)
|
||||
|
@ -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";
|
||||
};
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -40,13 +40,14 @@ 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.
|
||||
#
|
||||
|
||||
for tld in autosign manual; do
|
||||
# Step 1:
|
||||
# Introduce the first key. This will immediately be active.
|
||||
setup step1.ksk-doubleksk.autosign
|
||||
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)
|
||||
@ -59,7 +60,7 @@ $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefil
|
||||
|
||||
# Step 2:
|
||||
# It is time to submit the introduce the new KSK.
|
||||
setup step2.ksk-doubleksk.autosign
|
||||
setup step2.ksk-doubleksk.$tld
|
||||
# Lksk: 60d
|
||||
# Dreg: n/a
|
||||
# DprpC: 1h
|
||||
@ -89,7 +90,7 @@ $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefil
|
||||
|
||||
# Step 3:
|
||||
# It is time to submit the DS.
|
||||
setup step3.ksk-doubleksk.autosign
|
||||
setup step3.ksk-doubleksk.$tld
|
||||
# According to RFC 7583:
|
||||
# Iret = DprpP + TTLds (+retire-safety)
|
||||
#
|
||||
@ -132,7 +133,7 @@ $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefil
|
||||
|
||||
# Step 4:
|
||||
# The DS should be swapped now.
|
||||
setup step4.ksk-doubleksk.autosign
|
||||
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
|
||||
@ -172,7 +173,7 @@ $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefil
|
||||
|
||||
# Step 5:
|
||||
# The predecessor DNSKEY is removed long enough that is has become HIDDEN.
|
||||
setup step5.ksk-doubleksk.autosign
|
||||
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
|
||||
@ -211,7 +212,7 @@ $SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefil
|
||||
|
||||
# Step 6:
|
||||
# The predecessor DNSKEY can be purged.
|
||||
setup step6.ksk-doubleksk.autosign
|
||||
setup step6.ksk-doubleksk.$tld
|
||||
# Subtract purge-keys interval from all the times (1h).
|
||||
TpubN="now-1494h"
|
||||
TactN="now-1467h"
|
||||
@ -239,3 +240,4 @@ 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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -40,13 +40,14 @@ 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.
|
||||
#
|
||||
|
||||
for tld in autosign manual; do
|
||||
# Step 1:
|
||||
# Introduce the first key. This will immediately be active.
|
||||
setup step1.zsk-prepub.autosign
|
||||
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)
|
||||
@ -61,7 +62,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
|
||||
# Step 2:
|
||||
# It is time to pre-publish the successor ZSK.
|
||||
setup step2.zsk-prepub.autosign
|
||||
setup step2.zsk-prepub.$tld
|
||||
# According to RFC 7583:
|
||||
# Tact(N) = now + Ipub - Lzsk = now + 26h - 30d
|
||||
# = now + 26h - 30d = now − 694h
|
||||
@ -80,7 +81,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
# 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
|
||||
setup step3.zsk-prepub.$tld
|
||||
# According to RFC 7583:
|
||||
# Tpub(N+1) <= Tact(N) + Lzsk - Ipub
|
||||
# Tact(N+1) = Tact(N) + Lzsk
|
||||
@ -116,7 +117,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
# Step 4:
|
||||
# After the retire interval has passed the predecessor DNSKEY can be
|
||||
# removed from the zone.
|
||||
setup step4.zsk-prepub.autosign
|
||||
setup step4.zsk-prepub.$tld
|
||||
# Lzsk: 30d
|
||||
# Ipub: 26h
|
||||
# Dsgn: 1w
|
||||
@ -157,7 +158,7 @@ $SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O raw -f "${zonefile}.signed" $inf
|
||||
|
||||
# Step 5:
|
||||
# The predecessor DNSKEY is removed long enough that is has become HIDDEN.
|
||||
setup step5.zsk-prepub.autosign
|
||||
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
|
||||
@ -189,7 +190,7 @@ $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infil
|
||||
|
||||
# Step 6:
|
||||
# The predecessor DNSKEY can be purged.
|
||||
setup step6.zsk-prepub.autosign
|
||||
setup step6.zsk-prepub.$tld
|
||||
# Subtract purge-keys interval from all the times (1h).
|
||||
TactN="now-964h"
|
||||
TremN="now-3h"
|
||||
@ -214,3 +215,4 @@ 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
|
||||
|
@ -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)
|
||||
|
@ -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 <rndc dnssec>` 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.
|
||||
|
@ -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;
|
||||
|
@ -16,6 +16,7 @@ dnssec-policy <string> {
|
||||
dnskey-ttl <duration>;
|
||||
inline-signing <boolean>;
|
||||
keys { ( csk | ksk | zsk ) [ key-directory | key-store <string> ] lifetime <duration_or_unlimited> algorithm <string> [ tag-range <integer> <integer> ] [ <integer> ]; ... };
|
||||
manual-mode <boolean>;
|
||||
max-zone-ttl <duration>;
|
||||
nsec3param [ iterations <integer> ] [ optout <boolean> ] [ salt-length <integer> ];
|
||||
offline-ksk <boolean>;
|
||||
|
@ -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);
|
||||
/*%<
|
||||
|
@ -21,6 +21,11 @@
|
||||
|
||||
#include <dst/dst.h>
|
||||
|
||||
#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.
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
174
lib/dns/keymgr.c
174
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);
|
||||
|
||||
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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 },
|
||||
|
Loading…
x
Reference in New Issue
Block a user