2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 18:19:42 +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:
Matthijs Mekking 2025-08-21 15:18:15 +00:00
commit 888b5f55a8
52 changed files with 3163 additions and 1538 deletions

View File

@ -309,6 +309,7 @@ dnssec-policy \"default\" {\n\
cds-digest-types { 2; };\n\ cds-digest-types { 2; };\n\
dnskey-ttl " DNS_KASP_KEY_TTL ";\n\ dnskey-ttl " DNS_KASP_KEY_TTL ";\n\
inline-signing yes;\n\ inline-signing yes;\n\
manual-mode no;\n\
offline-ksk no;\n\ offline-ksk no;\n\
publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\ publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\
retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\ retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\
@ -327,6 +328,7 @@ dnssec-policy \"insecure\" {\n\
max-zone-ttl 0; \n\ max-zone-ttl 0; \n\
keys { };\n\ keys { };\n\
inline-signing yes;\n\ inline-signing yes;\n\
manual-mode no;\n\
};\n\ };\n\
\n\ \n\
" "

View File

@ -6612,7 +6612,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
* Ensure that zone keys are reloaded on reconfig * Ensure that zone keys are reloaded on reconfig
*/ */
if (dns_zone_getkasp(zone) != NULL) { if (dns_zone_getkasp(zone) != NULL) {
dns_zone_rekey(zone, fullsign); dns_zone_rekey(zone, fullsign, false);
} }
cleanup: cleanup:
@ -12026,7 +12026,7 @@ named_server_rekey(named_server_t *server, isc_lex_t *lex,
if (dns_zone_getkasp(zone) == NULL) { if (dns_zone_getkasp(zone) == NULL) {
result = ISC_R_NOPERM; result = ISC_R_NOPERM;
} else { } else {
dns_zone_rekey(zone, fullsign); dns_zone_rekey(zone, fullsign, false);
} }
dns_zone_detach(&zone); dns_zone_detach(&zone);
@ -14358,6 +14358,8 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex,
dns_dnsseckeylist_t keys; dns_dnsseckeylist_t keys;
char *ptr, *zonetext = NULL; char *ptr, *zonetext = NULL;
const char *msg = NULL; const char *msg = NULL;
/* variables for -step */
bool forcestep = false;
/* variables for -checkds */ /* variables for -checkds */
bool checkds = false, dspublish = false; bool checkds = false, dspublish = false;
/* variables for -rollover */ /* variables for -rollover */
@ -14401,6 +14403,8 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex,
rollover = true; rollover = true;
} else if (strcasecmp(ptr, "-checkds") == 0) { } else if (strcasecmp(ptr, "-checkds") == 0) {
checkds = true; checkds = true;
} else if (strcasecmp(ptr, "-step") == 0) {
forcestep = true;
} else { } else {
CHECK(DNS_R_SYNTAX); 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 * Rekey after checkds command because the next key
* event may have changed. * event may have changed.
*/ */
dns_zone_rekey(zone, false); dns_zone_rekey(zone, false, false);
if (use_keyid) { if (use_keyid) {
char tagbuf[6]; 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 * Rekey after rollover command because the next key
* event may have changed. * event may have changed.
*/ */
dns_zone_rekey(zone, false); dns_zone_rekey(zone, false, false);
if (use_keyid) { if (use_keyid) {
char tagbuf[6]; 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))); CHECK(putstr(text, isc_result_totext(ret)));
break; break;
} }
} else if (forcestep) {
dns_zone_rekey(zone, false, true);
} }
CHECK(putnull(text)); CHECK(putnull(text));
cleanup: cleanup:
@ -16094,7 +16101,7 @@ named_server_skr(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
CHECK(putnull(text)); CHECK(putnull(text));
} else { } else {
/* Schedule a rekey */ /* Schedule a rekey */
dns_zone_rekey(zone, false); dns_zone_rekey(zone, false, false);
} }
cleanup: cleanup:

View File

@ -27,6 +27,7 @@ dnssec-policy "test" {
zsk lifetime P30D algorithm 13; zsk lifetime P30D algorithm 13;
csk key-store "hsm" lifetime P30D algorithm 8 2048; csk key-store "hsm" lifetime P30D algorithm 8 2048;
}; };
manual-mode no;
max-zone-ttl 86400; max-zone-ttl 86400;
nsec3param ; nsec3param ;
parent-ds-ttl 7200; parent-ds-ttl 7200;

View File

@ -829,7 +829,13 @@ def check_dnssecstatus(server, zone, keys, policy=None, view=None):
def _check_signatures( 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 numsigs = 0
zrrsig = True zrrsig = True
@ -917,7 +923,7 @@ def check_signatures(
assert numsigs == len(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() now = KeyTimingMetadata.now()
numkeys = 0 numkeys = 0
@ -928,6 +934,17 @@ def _check_dnskeys(dnskeys, keys, cdnskey=False):
delete_md = f"Sync{delete_md}" delete_md = f"Sync{delete_md}"
for key in keys: 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) publish = key.get_timing(publish_md, must_exist=False)
delete = key.get_timing(delete_md, must_exist=False) delete = key.get_timing(delete_md, must_exist=False)
published = publish is not None and now >= publish published = publish is not None and now >= publish
@ -954,7 +971,7 @@ def _check_dnskeys(dnskeys, keys, cdnskey=False):
return numkeys 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 # Check if the correct DNSKEY records are published. If the current time
# is between the timing metadata 'publish' and 'delete', the key must have # is between the timing metadata 'publish' and 'delete', the key must have
# a DNSKEY record published. If 'cdnskey' is True, check against CDNSKEY # 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}" dnskey = f"{rr.name} {rr.ttl} {rdclass} {rdtype} {rdata}"
dnskeys.append(dnskey) dnskeys.append(dnskey)
numkeys += _check_dnskeys(dnskeys, ksks, cdnskey=cdnskey) numkeys += _check_dnskeys(dnskeys, ksks, cdnskey=cdnskey, manual_mode=manual_mode)
if not cdnskey: if not cdnskey:
numkeys += _check_dnskeys(dnskeys, zsks) numkeys += _check_dnskeys(dnskeys, zsks, manual_mode=manual_mode)
assert numkeys == len(dnskeys) 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 # Check if the correct CDS records are published. If the current time
# is between the timing metadata 'publish' and 'delete', the key must have # is between the timing metadata 'publish' and 'delete', the key must have
# a CDS record published. # a CDS record published.
@ -986,10 +1003,19 @@ def check_cds(cdss, keys, alg):
for key in keys: for key in keys:
assert key.is_ksk() 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") publish = key.get_timing("SyncPublish")
delete = key.get_timing("SyncDelete", must_exist=False) delete = key.get_timing("SyncDelete", must_exist=False)
published = now >= publish published = now >= publish
removed = delete is not None and delete <= now removed = delete is not None and delete <= now
if not published or removed: if not published or removed:
for cds in cdss: for cds in cdss:
assert not key.cds_equals(cds, alg) assert not key.cds_equals(cds, alg)
@ -1069,6 +1095,7 @@ def check_apex(
zsks, zsks,
cdss=None, cdss=None,
cds_delete=False, cds_delete=False,
manual_mode=False,
offline_ksk=False, offline_ksk=False,
zsk_missing=False, zsk_missing=False,
tsig=None, tsig=None,
@ -1082,7 +1109,7 @@ def check_apex(
# test dnskey query # test dnskey query
dnskeys, rrsigs = _query_rrset(server, fqdn, dns.rdatatype.DNSKEY, tsig=tsig) 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( check_signatures(
rrsigs, dns.rdatatype.DNSKEY, fqdn, ksks, zsks, offline_ksk=offline_ksk 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==") check_cdsdelete(cdnskeys, "0 3 0 AA==")
else: else:
if "CDNSKEY" in cdss: if "CDNSKEY" in cdss:
check_dnskeys(cdnskeys, ksks, zsks, cdnskey=True) check_dnskeys(cdnskeys, ksks, zsks, cdnskey=True, manual_mode=manual_mode)
else: else:
assert len(cdnskeys) == 0 assert len(cdnskeys) == 0
@ -1136,7 +1163,7 @@ def check_apex(
for alg in ["SHA-256", "SHA-384"]: for alg in ["SHA-256", "SHA-384"]:
if f"CDS ({alg})" in cdss: if f"CDS ({alg})" in cdss:
numcds += check_cds(cdsrrs, ksks, alg) numcds += check_cds(cdsrrs, ksks, alg, manual_mode=manual_mode)
else: else:
check_cds_prohibit(cdsrrs, ksks, alg) 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) cds_delete = step.get("cds-delete", False)
check_keytimes_flag = step.get("check-keytimes", True) check_keytimes_flag = step.get("check-keytimes", True)
zone_signed = step.get("zone-signed", True) zone_signed = step.get("zone-signed", True)
manual_mode = step.get("manual-mode", False)
isctest.log.info(f"check rollover step {zone}") 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_keytimes(keys, expected)
check_dnssecstatus(server, zone, keys, policy=policy) 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) check_subdomain(server, zone, ksks, zsks, smooth=smooth)
def check_next_key_event(): def check_next_key_event():
@ -1256,6 +1292,8 @@ def check_rollover_step(server, config, policy, step):
if nextev is not None: if nextev is not None:
isctest.run.retry_with_timeout(check_next_key_event, timeout=5) 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): def verify_update_is_signed(server, fqdn, qname, qtype, rdata, ksks, zsks, tsig=None):
""" """

View File

@ -286,6 +286,15 @@ zone "keyfiles-missing.autosign" {
dnssec-policy "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. * Zone that has missing private KSK.
*/ */

View File

@ -24,3 +24,19 @@ dnssec-policy "autosign" {
zsk key-directory lifetime P1Y algorithm @DEFAULT_ALGORITHM@; 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;
};

View File

@ -261,6 +261,19 @@ private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
cp $infile $zonefile 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 $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. # These signatures are already expired, and the private ZSK is retired.
setup zsk-retired.autosign setup zsk-retired.autosign
zsktimes="$keytimes -I now" zsktimes="$keytimes -I now"

View File

@ -118,6 +118,7 @@ lifetime = {
"P1Y": int(timedelta(days=365).total_seconds()), "P1Y": int(timedelta(days=365).total_seconds()),
"P30D": int(timedelta(days=30).total_seconds()), "P30D": int(timedelta(days=30).total_seconds()),
"P6M": int(timedelta(days=31 * 6).total_seconds()), "P6M": int(timedelta(days=31 * 6).total_seconds()),
"P2M": int(timedelta(days=31 * 2).total_seconds()),
} }
KASP_INHERIT_TSIG_SECRET = { 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_dnssecstatus(server, zone, ksks + zsks, policy=policy)
isctest.kasp.check_apex( 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) isctest.kasp.check_subdomain(server, zone, ksks, zsks, tsig=tsig)
@ -1662,3 +1671,100 @@ def test_kasp_reload_restart(ns6):
newttl = 400 newttl = 400
isctest.run.retry_with_timeout(check_soa_ttl, timeout=10) 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)

View File

@ -11,7 +11,27 @@
* information regarding copyright ownership. * 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-refresh P5D;
signatures-validity 30d; signatures-validity 30d;
signatures-validity-dnskey 30d; signatures-validity-dnskey 30d;

View File

@ -11,7 +11,27 @@
* information regarding copyright ownership. * 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-refresh P5D;
signatures-validity 30d; signatures-validity 30d;
signatures-validity-dnskey 30d; signatures-validity-dnskey 30d;

View File

@ -13,44 +13,47 @@
{% set csk_roll = csk_roll | default(False) %} {% set csk_roll = csk_roll | default(False) %}
{% set _csk_file = "csk1.conf" if not csk_roll else "csk2.conf" %} {% set _csk_file = "csk1.conf" if not csk_roll else "csk2.conf" %}
{% set zones = ["kasp", "manual"] %}
include "@_csk_file@"; include "@_csk_file@";
include "named.common.conf"; include "named.common.conf";
zone "step1.csk-algorithm-roll.kasp" { {% for tld in zones %}
zone "step1.csk-algorithm-roll.@tld@" {
type primary; type primary;
file "step1.csk-algorithm-roll.kasp.db"; file "step1.csk-algorithm-roll.@tld@.db";
dnssec-policy "csk-algoroll"; dnssec-policy "csk-algoroll-@tld@";
}; };
{% if csk_roll %} {% if csk_roll %}
zone "step2.csk-algorithm-roll.kasp" { zone "step2.csk-algorithm-roll.@tld@" {
type primary; type primary;
file "step2.csk-algorithm-roll.kasp.db"; file "step2.csk-algorithm-roll.@tld@.db";
dnssec-policy "csk-algoroll"; dnssec-policy "csk-algoroll-@tld@";
}; };
zone "step3.csk-algorithm-roll.kasp" { zone "step3.csk-algorithm-roll.@tld@" {
type primary; type primary;
file "step3.csk-algorithm-roll.kasp.db"; file "step3.csk-algorithm-roll.@tld@.db";
dnssec-policy "csk-algoroll"; dnssec-policy "csk-algoroll-@tld@";
}; };
zone "step4.csk-algorithm-roll.kasp" { zone "step4.csk-algorithm-roll.@tld@" {
type primary; type primary;
file "step4.csk-algorithm-roll.kasp.db"; file "step4.csk-algorithm-roll.@tld@.db";
dnssec-policy "csk-algoroll"; dnssec-policy "csk-algoroll-@tld@";
}; };
zone "step5.csk-algorithm-roll.kasp" { zone "step5.csk-algorithm-roll.@tld@" {
type primary; type primary;
file "step5.csk-algorithm-roll.kasp.db"; file "step5.csk-algorithm-roll.@tld@.db";
dnssec-policy "csk-algoroll"; dnssec-policy "csk-algoroll-@tld@";
}; };
zone "step6.csk-algorithm-roll.kasp" { zone "step6.csk-algorithm-roll.@tld@" {
type primary; type primary;
file "step6.csk-algorithm-roll.kasp.db"; file "step6.csk-algorithm-roll.@tld@.db";
dnssec-policy "csk-algoroll"; dnssec-policy "csk-algoroll-@tld@";
}; };
{% endif %} {% endif %}
{% endfor %}

View File

@ -30,18 +30,19 @@ O="OMNIPRESENT"
U="UNRETENTIVE" 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. # algorithm rollover.
# #
for tld in kasp manual; do
# Step 1: # Step 1:
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
setup step1.csk-algorithm-roll.kasp setup step1.csk-algorithm-roll.$tld
echo "$zone" >>zones echo "$zone" >>zones
TactN="now-7d" TactN="now-7d"
TsbmN="now-161h" TsbmN="now-161h"
csktimes="-P ${TactN} -A ${TactN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone 5 "$CSK" >>"$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: # Step 2:
# After the publication interval has passed the DNSKEY is OMNIPRESENT. # 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. # The time passed since the new algorithm keys have been introduced is 3 hours.
TpubN1="now-3h" TpubN1="now-3h"
# Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h # Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h
TsbmN1="now+4h" TsbmN1="now+4h"
csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I now" csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I now"
newtimes="-P ${TpubN1} -A ${TpubN1}" newtimes="-P ${TpubN1} -A ${TpubN1}"
CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) CSK1=$($KEYGEN -k csk-algoroll-$tld -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) 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 $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 $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. # 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: # Step 3:
# The zone signatures are also OMNIPRESENT. # 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. # The time passed since the new algorithm keys have been introduced is 7 hours.
TpubN1="now-7h" TpubN1="now-7h"
TsbmN1="now" TsbmN1="now"
ckstimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" ckstimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}"
newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}"
CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) CSK1=$($KEYGEN -k csk-algoroll-$tld -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) 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 $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 $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. # 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: # Step 4:
# The DS is swapped and can become OMNIPRESENT. # 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. # The time passed since the DS has been swapped is 3 hours.
TpubN1="now-10h" TpubN1="now-10h"
TsbmN1="now-3h" TsbmN1="now-3h"
csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}"
newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}"
CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) CSK1=$($KEYGEN -k csk-algoroll-$tld -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) 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 $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 $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. # 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: # Step 5:
# The DNSKEY is removed long enough to be HIDDEN. # 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. # The time passed since the DNSKEY has been removed is 2 hours.
TpubN1="now-12h" TpubN1="now-12h"
TsbmN1="now-5h" TsbmN1="now-5h"
csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}"
newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}"
CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) CSK1=$($KEYGEN -k csk-algoroll-$tld -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) 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 $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 $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. # 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: # Step 6:
# The RRSIGs have been removed long enough to be HIDDEN. # 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. # Additional time passed: 7h.
TpubN1="now-19h" TpubN1="now-19h"
TsbmN1="now-12h" TsbmN1="now-12h"
csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}" csktimes="-P ${TactN} -A ${TactN} -P sync ${TsbmN} -I ${TsbmN1}"
newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}" newtimes="-P ${TpubN1} -A ${TpubN1} -P sync ${TsbmN1}"
CSK1=$($KEYGEN -k csk-algoroll -l csk1.conf $csktimes $zone 2>keygen.out.$zone.1) CSK1=$($KEYGEN -k csk-algoroll-$tld -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) 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 $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 $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. # 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" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
cp $infile $zonefile 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 $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

View File

@ -11,7 +11,10 @@
# pylint: disable=redefined-outer-name,unused-import # pylint: disable=redefined-outer-name,unused-import
import pytest
import isctest import isctest
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
CDSS, 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 config = ALGOROLL_CONFIG
policy = "csk-algoroll" zone = f"step1.csk-algorithm-roll.{tld}"
zone = "step1.csk-algorithm-roll.kasp"
isctest.kasp.wait_keymgr_done(ns6, zone) isctest.kasp.wait_keymgr_done(ns6, zone)

View File

@ -15,6 +15,7 @@ import pytest
import isctest import isctest
from isctest.kasp import KeyTimingMetadata from isctest.kasp import KeyTimingMetadata
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
alg, alg,
@ -28,6 +29,7 @@ from rollover.common import (
ALGOROLL_KEYTTLPROP, ALGOROLL_KEYTTLPROP,
ALGOROLL_OFFSETS, ALGOROLL_OFFSETS,
ALGOROLL_OFFVAL, ALGOROLL_OFFVAL,
DURATION,
TIMEDELTA, TIMEDELTA,
) )
@ -50,11 +52,45 @@ def reconfigure(ns6, templates):
TIME_PASSED = KeyTimingMetadata.now().value - start_time.value TIME_PASSED = KeyTimingMetadata.now().value - start_time.value
def test_algoroll_csk_reconfig_step1(ns6, alg, size): @pytest.mark.parametrize(
zone = "step1.csk-algorithm-roll.kasp" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # Next key event is when the ecdsa256 keys have been propagated.
"nextev": ALGOROLL_IPUB, "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): @pytest.mark.parametrize(
zone = "step2.csk-algorithm-roll.kasp" "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) isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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'. # the time passed between key creation and invoking 'rndc reconfig'.
"nextev": ALGOROLL_IPUBC - ALGOROLL_IPUB - TIME_PASSED, "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): @pytest.mark.parametrize(
zone = "step3.csk-algorithm-roll.kasp" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -114,14 +204,46 @@ def test_algoroll_csk_reconfig_step3(ns6, alg, size):
# after the publication interval of the parent side. # after the publication interval of the parent side.
"nextev": ALGOROLL_IRETKSK - TIME_PASSED, "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): @pytest.mark.parametrize(
zone = "step4.csk-algorithm-roll.kasp" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # This happens after the DNSKEY TTL plus zone propagation delay.
"nextev": ALGOROLL_KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step5.csk-algorithm-roll.kasp" "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) isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -158,14 +290,24 @@ def test_algoroll_csk_reconfig_step5(ns6, alg, size):
# between key creation and invoking 'rndc reconfig'. # between key creation and invoking 'rndc reconfig'.
"nextev": ALGOROLL_IRET - ALGOROLL_IRETKSK - ALGOROLL_KEYTTLPROP - TIME_PASSED, "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): @pytest.mark.parametrize(
zone = "step6.csk-algorithm-roll.kasp" "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) isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -179,4 +321,4 @@ def test_algoroll_csk_reconfig_step6(ns6, alg, size):
# loadkeys interval. # loadkeys interval.
"nextev": TIMEDELTA["PT1H"], "nextev": TIMEDELTA["PT1H"],
} }
isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)

View File

@ -11,7 +11,7 @@
* information regarding copyright ownership. * information regarding copyright ownership.
*/ */
dnssec-policy "rsasha256" { dnssec-policy "rsasha256-kasp" {
signatures-refresh P5D; signatures-refresh P5D;
signatures-validity 30d; signatures-validity 30d;
signatures-validity-dnskey 30d; signatures-validity-dnskey 30d;
@ -30,7 +30,49 @@ dnssec-policy "rsasha256" {
parent-ds-ttl 7200; 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-refresh P5D;
signatures-validity 30d; signatures-validity 30d;
signatures-validity-dnskey 30d; signatures-validity-dnskey 30d;

View File

@ -13,44 +13,48 @@
{% set alg_roll = alg_roll | default(False) %} {% set alg_roll = alg_roll | default(False) %}
{% set policy = "rsasha256" if not alg_roll else "ecdsa256" %} {% set policy = "rsasha256" if not alg_roll else "ecdsa256" %}
{% set zones = ["kasp", "manual"] %}
include "kasp.conf"; include "kasp.conf";
include "named.common.conf"; include "named.common.conf";
zone "step1.algorithm-roll.kasp" { {% for tld in zones %}
zone "step1.algorithm-roll.@tld@" {
type primary; type primary;
file "step1.algorithm-roll.kasp.db"; file "step1.algorithm-roll.@tld@.db";
dnssec-policy @policy@; dnssec-policy @policy@-@tld@;
}; };
{% if alg_roll %} {% if alg_roll %}
zone "step2.algorithm-roll.kasp" { zone "step2.algorithm-roll.@tld@" {
type primary; type primary;
file "step2.algorithm-roll.kasp.db"; file "step2.algorithm-roll.@tld@.db";
dnssec-policy "ecdsa256"; dnssec-policy "ecdsa256-@tld@";
}; };
zone "step3.algorithm-roll.kasp" { zone "step3.algorithm-roll.@tld@" {
type primary; type primary;
file "step3.algorithm-roll.kasp.db"; file "step3.algorithm-roll.@tld@.db";
dnssec-policy "ecdsa256"; dnssec-policy "ecdsa256-@tld@";
}; };
zone "step4.algorithm-roll.kasp" { zone "step4.algorithm-roll.@tld@" {
type primary; type primary;
file "step4.algorithm-roll.kasp.db"; file "step4.algorithm-roll.@tld@.db";
dnssec-policy "ecdsa256"; dnssec-policy "ecdsa256-@tld@";
}; };
zone "step5.algorithm-roll.kasp" { zone "step5.algorithm-roll.@tld@" {
type primary; type primary;
file "step5.algorithm-roll.kasp.db"; file "step5.algorithm-roll.@tld@.db";
dnssec-policy "ecdsa256"; dnssec-policy "ecdsa256-@tld@";
}; };
zone "step6.algorithm-roll.kasp" { zone "step6.algorithm-roll.@tld@" {
type primary; type primary;
file "step6.algorithm-roll.kasp.db"; file "step6.algorithm-roll.@tld@.db";
dnssec-policy "ecdsa256"; dnssec-policy "ecdsa256-@tld@";
}; };
{% endif %} {% endif %}
{% endfor %}

View File

@ -30,13 +30,14 @@ O="OMNIPRESENT"
U="UNRETENTIVE" 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. # algorithm rollover.
# #
for tld in kasp manual; do
# Step 1: # Step 1:
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
setup step1.algorithm-roll.kasp setup step1.algorithm-roll.$tld
echo "$zone" >>zones echo "$zone" >>zones
TactN="now-7d" TactN="now-7d"
TsbmN="now-161h" 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: # Step 2:
# After the publication interval has passed the DNSKEY is OMNIPRESENT. # 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. # The time passed since the new algorithm keys have been introduced is 3 hours.
TpubN1="now-3h" TpubN1="now-3h"
# Tsbm(N+1) = TpubN1 + Ipub = now + TTLsig + Dprp = now - 3h + 6h + 1h = now + 4h # 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: # Step 3:
# The zone signatures are also OMNIPRESENT. # 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. # The time passed since the new algorithm keys have been introduced is 7 hours.
TpubN1="now-7h" TpubN1="now-7h"
TsbmN1="now" 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: # Step 4:
# The DS is swapped and can become OMNIPRESENT. # 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. # The time passed since the DS has been swapped is 3 hours.
TpubN1="now-10h" TpubN1="now-10h"
TsbmN1="now-3h" 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: # Step 5:
# The DNSKEY is removed long enough to be HIDDEN. # 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. # The time passed since the DNSKEY has been removed is 2 hours.
TpubN1="now-12h" TpubN1="now-12h"
TsbmN1="now-5h" 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: # Step 6:
# The RRSIGs have been removed long enough to be HIDDEN. # The RRSIGs have been removed long enough to be HIDDEN.
setup step6.algorithm-roll.kasp setup step6.algorithm-roll.$tld
# Additional time passed: 7h. # Additional time passed: 7h.
TpubN1="now-19h" TpubN1="now-19h"
TsbmN1="now-12h" 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" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile"
cp $infile $zonefile 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 $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
done

View File

@ -11,7 +11,10 @@
# pylint: disable=unused-import # pylint: disable=unused-import
import pytest
import isctest import isctest
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
CDSS, 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 config = ALGOROLL_CONFIG
policy = "rsasha256" policy = f"rsasha256-{tld}"
zone = "step1.algorithm-roll.kasp" zone = f"step1.algorithm-roll.{tld}"
isctest.kasp.wait_keymgr_done(ns6, zone) isctest.kasp.wait_keymgr_done(ns6, zone)

View File

@ -15,6 +15,7 @@ import pytest
import isctest import isctest
from isctest.kasp import KeyTimingMetadata from isctest.kasp import KeyTimingMetadata
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
alg, alg,
@ -28,6 +29,7 @@ from rollover.common import (
ALGOROLL_KEYTTLPROP, ALGOROLL_KEYTTLPROP,
ALGOROLL_OFFSETS, ALGOROLL_OFFSETS,
ALGOROLL_OFFVAL, ALGOROLL_OFFVAL,
DURATION,
TIMEDELTA, TIMEDELTA,
) )
@ -50,11 +52,48 @@ def reconfigure(ns6, templates):
TIME_PASSED = KeyTimingMetadata.now().value - start_time.value TIME_PASSED = KeyTimingMetadata.now().value - start_time.value
def test_algoroll_ksk_zsk_reconfig_step1(ns6, alg, size): @pytest.mark.parametrize(
zone = "step1.algorithm-roll.kasp" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # Next key event is when the ecdsa256 keys have been propagated.
"nextev": ALGOROLL_IPUB, "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): @pytest.mark.parametrize(
zone = "step2.algorithm-roll.kasp" "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) isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -98,14 +147,60 @@ def test_algoroll_ksk_zsk_reconfig_step2(ns6, alg, size):
# key creation and invoking 'rndc reconfig'. # key creation and invoking 'rndc reconfig'.
"nextev": ALGOROLL_IPUBC - ALGOROLL_IPUB - TIME_PASSED, "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): @pytest.mark.parametrize(
zone = "step3.algorithm-roll.kasp" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -120,14 +215,51 @@ def test_algoroll_ksk_zsk_reconfig_step3(ns6, alg, size):
# after the retire interval. # after the retire interval.
"nextev": ALGOROLL_IRETKSK - TIME_PASSED, "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): @pytest.mark.parametrize(
zone = "step4.algorithm-roll.kasp" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # This happens after the DNSKEY TTL plus zone propagation delay.
"nextev": ALGOROLL_KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step5.algorithm-roll.kasp" "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) isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -168,14 +310,24 @@ def test_algoroll_ksk_zsk_reconfig_step5(ns6, alg, size):
# between key creation and invoking 'rndc reconfig'. # between key creation and invoking 'rndc reconfig'.
"nextev": ALGOROLL_IRET - ALGOROLL_IRETKSK - ALGOROLL_KEYTTLPROP - TIME_PASSED, "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): @pytest.mark.parametrize(
zone = "step6.algorithm-roll.kasp" "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) isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -191,4 +343,4 @@ def test_algoroll_ksk_zsk_reconfig_step6(ns6, alg, size):
# loadkeys interval. # loadkeys interval.
"nextev": TIMEDELTA["PT1H"], "nextev": TIMEDELTA["PT1H"],
} }
isctest.kasp.check_rollover_step(ns6, CONFIG, POLICY, step) isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)

View File

@ -11,7 +11,31 @@
* information regarding copyright ownership. * 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-refresh P5D;
signatures-validity 30d; signatures-validity 30d;
signatures-validity-dnskey 30d; signatures-validity-dnskey 30d;

View File

@ -11,46 +11,51 @@
* information regarding copyright ownership. * information regarding copyright ownership.
*/ */
{% set zones = ["autosign", "manual"] %}
include "kasp.conf"; include "kasp.conf";
include "named.common.conf"; include "named.common.conf";
zone "step1.csk-roll1.autosign" { {% for tld in zones %}
zone "step1.csk-roll1.@tld@" {
type primary; type primary;
file "step1.csk-roll1.autosign.db"; file "step1.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
zone "step2.csk-roll1.autosign" { zone "step2.csk-roll1.@tld@" {
type primary; type primary;
file "step2.csk-roll1.autosign.db"; file "step2.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
zone "step3.csk-roll1.autosign" { zone "step3.csk-roll1.@tld@" {
type primary; type primary;
file "step3.csk-roll1.autosign.db"; file "step3.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
zone "step4.csk-roll1.autosign" { zone "step4.csk-roll1.@tld@" {
type primary; type primary;
file "step4.csk-roll1.autosign.db"; file "step4.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
zone "step5.csk-roll1.autosign" { zone "step5.csk-roll1.@tld@" {
type primary; type primary;
file "step5.csk-roll1.autosign.db"; file "step5.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
zone "step6.csk-roll1.autosign" { zone "step6.csk-roll1.@tld@" {
type primary; type primary;
file "step6.csk-roll1.autosign.db"; file "step6.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
zone "step7.csk-roll1.autosign" { zone "step7.csk-roll1.@tld@" {
type primary; type primary;
file "step7.csk-roll1.autosign.db"; file "step7.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
zone "step8.csk-roll1.autosign" { zone "step8.csk-roll1.@tld@" {
type primary; type primary;
file "step8.csk-roll1.autosign.db"; file "step8.csk-roll1.@tld@.db";
dnssec-policy "csk-roll1"; dnssec-policy "csk-roll1-@tld@";
}; };
{% endfor %}

View File

@ -40,16 +40,17 @@ O="OMNIPRESENT"
U="UNRETENTIVE" 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). # (which is essentially a ZSK Pre-Publication / KSK Double-KSK rollover).
# #
for tld in autosign manual; do
# Step 1: # Step 1:
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
setup step1.csk-roll1.autosign setup step1.csk-roll1.$tld
TactN="now-7d" TactN="now-7d"
keytimes="-P ${TactN} -A ${TactN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$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: # Step 2:
# It is time to introduce the new CSK. # It is time to introduce the new CSK.
setup step2.csk-roll1.autosign setup step2.csk-roll1.$tld
# According to RFC 7583: # According to RFC 7583:
# KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC
# ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub # ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub
@ -77,7 +78,7 @@ setup step2.csk-roll1.autosign
# = now - 4464h + 3h = now - 4461h # = now - 4464h + 3h = now - 4461h
TactN="now-4461h" TactN="now-4461h"
keytimes="-P ${TactN} -A ${TactN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$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: # Step 3:
# It is time to submit the DS and to roll signatures. # 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: # According to RFC 7583:
# #
# Tsbm(N+1) >= Trdy(N+1) # Tsbm(N+1) >= Trdy(N+1)
@ -126,8 +127,8 @@ TretN1="now+186d"
TremN1="now+5091h" TremN1="now+5091h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll1-$tld -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) 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 $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 $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. # 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 # DS should be swapped. The ZRRSIG records are all replaced after IretZ
# (which is 26d3h). The DS is swapped after Iret (which is 4h). # (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. # 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: # According to RFC 7583:
# Trem(N) = Tret(N) - Iret + IretZ # Trem(N) = Tret(N) - Iret + IretZ
# now = Tsbm(N+1) + Iret # now = Tsbm(N+1) + Iret
@ -172,8 +173,8 @@ TretN1="now+4460h"
TremN1="now+5087h" TremN1="now+5087h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll1-$tld -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) 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 $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 $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. # 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: # Step 5:
# After the DS is swapped in step 4, also the KRRSIG records can be removed. # After the DS is swapped in step 4, also the KRRSIG records can be removed.
# At this time these have all become hidden. # 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). # Subtract DNSKEY TTL plus zone propagation delay from all the times (2h).
TpubN="now-4470h" TpubN="now-4470h"
TactN="now-4445h" TactN="now-4445h"
@ -200,8 +201,8 @@ TretN1="now+4458h"
TremN1="now+5085h" TremN1="now+5085h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll1-$tld -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) 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 $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 $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. # 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: # Step 6:
# After the retire interval has passed the predecessor DNSKEY can be # After the retire interval has passed the predecessor DNSKEY can be
# removed from the zone. # removed from the zone.
setup step6.csk-roll1.autosign setup step6.csk-roll1.$tld
# According to RFC 7583: # According to RFC 7583:
# Trem(N) = Tret(N) + IretZ # Trem(N) = Tret(N) + IretZ
# Tret(N) = Tact(N) + Lcsk # Tret(N) = Tact(N) + Lcsk
@ -244,8 +245,8 @@ TretN1="now+3837h"
TremN1="now+186d" TremN1="now+186d"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll1-$tld -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) 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 $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 $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. # 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: # Step 7:
# Some time later the predecessor DNSKEY enters the HIDDEN state. # 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). # Subtract DNSKEY TTL plus zone propagation delay from all the times (2h).
TpubN="now-5093h" TpubN="now-5093h"
TactN="now-5068h" TactN="now-5068h"
@ -271,8 +272,8 @@ TretN1="now+3835h"
TremN1="now+4462h" TremN1="now+4462h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll1-$tld -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) 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 $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 $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. # 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: # Step 8:
# The predecessor DNSKEY can be purged. # The predecessor DNSKEY can be purged.
setup step8.csk-roll1.autosign setup step8.csk-roll1.$tld
TpubN="now-5094h" TpubN="now-5094h"
TactN="now-5069h" TactN="now-5069h"
TretN="now-630h" TretN="now-630h"
@ -298,8 +299,8 @@ TremN1="now+4461h"
# Subtract purge-keys interval from all the times (1h). # Subtract purge-keys interval from all the times (1h).
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll1-$tld -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) 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 $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 $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. # 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" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
cp $infile $zonefile 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 $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

View File

@ -13,8 +13,11 @@
from datetime import timedelta from datetime import timedelta
import pytest
import isctest import isctest
from isctest.kasp import Ipub, Iret from isctest.kasp import Ipub, Iret
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
alg, 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()) OFFSETS["step8-s"] = OFFSETS["step7-s"] - int(CONFIG["purge-keys"].total_seconds())
def test_csk_roll1_step1(alg, size, ns3): @pytest.mark.parametrize(
zone = "step1.csk-roll1.autosign" "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) 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 = { step = {
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
"zone": zone, "zone": zone,
@ -79,14 +93,45 @@ def test_csk_roll1_step1(alg, size, ns3):
# registration delay). # registration delay).
"nextev": CSK_LIFETIME - IPUB - timedelta(days=7), "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): @pytest.mark.parametrize(
zone = "step2.csk-roll1.autosign" "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) 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 = { step = {
# Successor CSK is prepublished (signs DNSKEY RRset, but not yet # Successor CSK is prepublished (signs DNSKEY RRset, but not yet
# other RRsets). # other RRsets).
@ -104,14 +149,59 @@ def test_csk_roll1_step2(alg, size, ns3):
# Next key event is when the successor CSK becomes OMNIPRESENT. # Next key event is when the successor CSK becomes OMNIPRESENT.
"nextev": IPUB, "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): @pytest.mark.parametrize(
zone = "step3.csk-roll1.autosign" "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) 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 = { step = {
# Successor CSK becomes omnipresent, meaning we can start signing # Successor CSK becomes omnipresent, meaning we can start signing
# the remainder of the zone with the successor CSK, and we can # 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. # from the predecessor ZSK.
"smooth": True, "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): @pytest.mark.parametrize(
zone = "step4.csk-roll1.autosign" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # We already swapped the DS in the previous step, so disable ds-swap.
"ds-swap": False, "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): @pytest.mark.parametrize(
zone = "step5.csk-roll1.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -192,14 +328,25 @@ def test_csk_roll1_step5(alg, size, ns3):
# CSK. # CSK.
"nextev": SIGNDELAY, "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): @pytest.mark.parametrize(
zone = "step6.csk-roll1.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
if tld == "manual":
return
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -217,14 +364,24 @@ def test_csk_roll1_step6(alg, size, ns3):
# This is the DNSKEY TTL plus zone propagation delay. # This is the DNSKEY TTL plus zone propagation delay.
"nextev": KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step7.csk-roll1.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -239,14 +396,24 @@ def test_csk_roll1_step7(alg, size, ns3):
# minus the prepublication time. # minus the prepublication time.
"nextev": CSK_LIFETIME - IRETZSK - IPUB - KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step8.csk-roll1.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -255,4 +422,4 @@ def test_csk_roll1_step8(alg, size, ns3):
], ],
"nextev": None, "nextev": None,
} }
isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)

View File

@ -11,7 +11,31 @@
* information regarding copyright ownership. * 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-refresh 12h;
signatures-validity P1D; signatures-validity P1D;
signatures-validity-dnskey P1D; signatures-validity-dnskey P1D;

View File

@ -11,41 +11,46 @@
* information regarding copyright ownership. * information regarding copyright ownership.
*/ */
{% set zones = ["autosign", "manual"] %}
include "kasp.conf"; include "kasp.conf";
include "named.common.conf"; include "named.common.conf";
zone "step1.csk-roll2.autosign" { {% for tld in zones %}
zone "step1.csk-roll2.@tld@" {
type primary; type primary;
file "step1.csk-roll2.autosign.db"; file "step1.csk-roll2.@tld@.db";
dnssec-policy "csk-roll2"; dnssec-policy "csk-roll2-@tld@";
}; };
zone "step2.csk-roll2.autosign" { zone "step2.csk-roll2.@tld@" {
type primary; type primary;
file "step2.csk-roll2.autosign.db"; file "step2.csk-roll2.@tld@.db";
dnssec-policy "csk-roll2"; dnssec-policy "csk-roll2-@tld@";
}; };
zone "step3.csk-roll2.autosign" { zone "step3.csk-roll2.@tld@" {
type primary; type primary;
file "step3.csk-roll2.autosign.db"; file "step3.csk-roll2.@tld@.db";
dnssec-policy "csk-roll2"; dnssec-policy "csk-roll2-@tld@";
}; };
zone "step4.csk-roll2.autosign" { zone "step4.csk-roll2.@tld@" {
type primary; type primary;
file "step4.csk-roll2.autosign.db"; file "step4.csk-roll2.@tld@.db";
dnssec-policy "csk-roll2"; dnssec-policy "csk-roll2-@tld@";
}; };
zone "step5.csk-roll2.autosign" { zone "step5.csk-roll2.@tld@" {
type primary; type primary;
file "step5.csk-roll2.autosign.db"; file "step5.csk-roll2.@tld@.db";
dnssec-policy "csk-roll2"; dnssec-policy "csk-roll2-@tld@";
}; };
zone "step6.csk-roll2.autosign" { zone "step6.csk-roll2.@tld@" {
type primary; type primary;
file "step6.csk-roll2.autosign.db"; file "step6.csk-roll2.@tld@.db";
dnssec-policy "csk-roll2"; dnssec-policy "csk-roll2-@tld@";
}; };
zone "step7.csk-roll2.autosign" { zone "step7.csk-roll2.@tld@" {
type primary; type primary;
file "step7.csk-roll2.autosign.db"; file "step7.csk-roll2.@tld@.db";
dnssec-policy "csk-roll2"; dnssec-policy "csk-roll2-@tld@";
}; };
{% endfor %}

View File

@ -40,18 +40,19 @@ O="OMNIPRESENT"
U="UNRETENTIVE" 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). # (which is essentially a ZSK Pre-Publication / KSK Double-KSK rollover).
# This scenario differs from the csk-roll1 one because the zone signatures (ZRRSIG) # 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. # are replaced with the new key sooner than the DS is swapped.
# #
for tld in autosign manual; do
# Step 1: # Step 1:
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
setup step1.csk-roll2.autosign setup step1.csk-roll2.$tld
TactN="now-7d" TactN="now-7d"
keytimes="-P ${TactN} -A ${TactN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$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: # Step 2:
# It is time to introduce the new CSK. # It is time to introduce the new CSK.
setup step2.csk-roll2.autosign setup step2.csk-roll2.$tld
# According to RFC 7583: # According to RFC 7583:
# KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC # KSK: Tpub(N+1) <= Tact(N) + Lksk - IpubC
# ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub # ZSK: Tpub(N+1) <= Tact(N) + Lzsk - Ipub
@ -79,7 +80,7 @@ setup step2.csk-roll2.autosign
# = now - 4464h + 3h = now - 4461h # = now - 4464h + 3h = now - 4461h
TactN="now-4461h" TactN="now-4461h"
keytimes="-P ${TactN} -A ${TactN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$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: # Step 3:
# It is time to submit the DS and to roll signatures. # 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: # According to RFC 7583:
# #
# Tsbm(N+1) >= Trdy(N+1) # Tsbm(N+1) >= Trdy(N+1)
@ -128,8 +129,8 @@ TretN1="now+186d"
TremN1="now+4634h" TremN1="now+4634h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll2-$tld -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) 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 $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 $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. # 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). # 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 # The DS is swapped after Dreg + Iret (1w3h). In other words, the zone
# signatures are replaced before the DS is swapped. # signatures are replaced before the DS is swapped.
setup step4.csk-roll2.autosign setup step4.csk-roll2.$tld
# According to RFC 7583: # According to RFC 7583:
# Trem(N) = Tret(N) + IretZ # Trem(N) = Tret(N) + IretZ
# #
@ -176,8 +177,8 @@ TretN1="now+4426h"
TremN1="now+4429h" TremN1="now+4429h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll2-$tld -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) 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 $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 $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. # 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: # Step 5:
# Some time later the DS can be swapped and the old DNSKEY can be removed from # Some time later the DS can be swapped and the old DNSKEY can be removed from
# the zone. # the zone.
setup step5.csk-roll2.autosign setup step5.csk-roll2.$tld
# Subtract Iret (170h) - IretZ (38h) = 132h. # Subtract Iret (170h) - IretZ (38h) = 132h.
# #
# Tpub(N) = now - 4502h - 132h = now - 4634h # Tpub(N) = now - 4502h - 132h = now - 4634h
@ -213,8 +214,8 @@ TretN1="now+4294h"
TremN1="now+4360h" TremN1="now+4360h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll2-$tld -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) 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 $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 $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. # 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: # Step 6:
# Some time later the predecessor DNSKEY enters the HIDDEN state. # 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). # Subtract DNSKEY TTL plus zone propagation delay (2h).
# #
# Tpub(N) = now - 4634h - 2h = now - 4636h # Tpub(N) = now - 4634h - 2h = now - 4636h
@ -249,8 +250,8 @@ TretN1="now+4292h"
TremN1="now+4358h" TremN1="now+4358h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll2-$tld -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) 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 $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 $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. # 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: # Step 7:
# The predecessor DNSKEY can be purged, but purge-keys is disabled. # 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. # Subtract 90 days (default, 2160h) from all the times.
# #
# Tpub(N) = now - 4636h - 2160h = now - 6796h # Tpub(N) = now - 4636h - 2160h = now - 6796h
@ -285,8 +286,8 @@ TretN1="now+2132h"
TremN1="now+2198h" TremN1="now+2198h"
keytimes="-P ${TpubN} -P sync ${TactN} -A ${TpubN} -I ${TretN} -D ${TremN} -D sync ${TactN1}" 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}" 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) CSK1=$($KEYGEN -k csk-roll2-$tld -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) 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 $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 $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. # 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" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK2" >>"$infile"
cp $infile $zonefile 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 $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

View File

@ -13,8 +13,11 @@
from datetime import timedelta from datetime import timedelta
import pytest
import isctest import isctest
from isctest.kasp import Ipub, Iret from isctest.kasp import Ipub, Iret
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
alg, 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()) OFFSETS["step7-s"] = OFFSETS["step6-s"] - int(timedelta(days=90).total_seconds())
def test_csk_roll2_step1(alg, size, ns3): @pytest.mark.parametrize(
zone = "step1.csk-roll2.autosign" "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) 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 = { step = {
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
"zone": zone, "zone": zone,
@ -82,14 +96,45 @@ def test_csk_roll2_step1(alg, size, ns3):
# registration delay). # registration delay).
"nextev": CSK_LIFETIME - IPUB - TIMEDELTA["P7D"], "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): @pytest.mark.parametrize(
zone = "step2.csk-roll2.autosign" "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) 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 = { step = {
# Successor CSK is prepublished (signs DNSKEY RRset, but not yet # Successor CSK is prepublished (signs DNSKEY RRset, but not yet
# other RRsets). # other RRsets).
@ -107,14 +152,59 @@ def test_csk_roll2_step2(alg, size, ns3):
# Next key event is when the successor CSK becomes OMNIPRESENT. # Next key event is when the successor CSK becomes OMNIPRESENT.
"nextev": IPUB, "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): @pytest.mark.parametrize(
zone = "step3.csk-roll2.autosign" "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) 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 = { step = {
# Successor CSK becomes omnipresent, meaning we can start signing # Successor CSK becomes omnipresent, meaning we can start signing
# the remainder of the zone with the successor CSK, and we can # 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. # from the predecessor ZSK.
"smooth": True, "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): @pytest.mark.parametrize(
zone = "step4.csk-roll2.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # We already swapped the DS in the previous step, so disable ds-swap.
"ds-swap": False, "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): @pytest.mark.parametrize(
zone = "step5.csk-roll2.autosign" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -200,14 +335,24 @@ def test_csk_roll2_step5(alg, size, ns3):
# This is the DNSKEY TTL plus zone propagation delay. # This is the DNSKEY TTL plus zone propagation delay.
"nextev": KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step6.csk-roll2.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # This is the Lcsk, minus time passed since the key was published.
"nextev": CSK_LIFETIME - IRET - IPUB - KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step7.csk-roll2.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -242,4 +397,4 @@ def test_csk_roll2_step7(alg, size, ns3):
"keyrelationships": [0, 1], "keyrelationships": [0, 1],
"nextev": None, "nextev": None,
} }
isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)

View File

@ -11,7 +11,28 @@
* information regarding copyright ownership. * 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-refresh P1W;
signatures-validity P2W; signatures-validity P2W;
signatures-validity-dnskey P2W; signatures-validity-dnskey P2W;

View File

@ -11,26 +11,31 @@
* information regarding copyright ownership. * information regarding copyright ownership.
*/ */
{% set zones = ["autosign", "manual"] %}
include "kasp.conf"; include "kasp.conf";
include "named.common.conf"; include "named.common.conf";
zone "step1.enable-dnssec.autosign" { {% for tld in zones %}
zone "step1.enable-dnssec.@tld@" {
type primary; type primary;
file "step1.enable-dnssec.autosign.db"; file "step1.enable-dnssec.@tld@.db";
dnssec-policy "enable-dnssec"; dnssec-policy "enable-dnssec-@tld@";
}; };
zone "step2.enable-dnssec.autosign" { zone "step2.enable-dnssec.@tld@" {
type primary; type primary;
file "step2.enable-dnssec.autosign.db"; file "step2.enable-dnssec.@tld@.db";
dnssec-policy "enable-dnssec"; dnssec-policy "enable-dnssec-@tld@";
}; };
zone "step3.enable-dnssec.autosign" { zone "step3.enable-dnssec.@tld@" {
type primary; type primary;
file "step3.enable-dnssec.autosign.db"; file "step3.enable-dnssec.@tld@.db";
dnssec-policy "enable-dnssec"; dnssec-policy "enable-dnssec-@tld@";
}; };
zone "step4.enable-dnssec.autosign" { zone "step4.enable-dnssec.@tld@" {
type primary; type primary;
file "step4.enable-dnssec.autosign.db"; file "step4.enable-dnssec.@tld@.db";
dnssec-policy "enable-dnssec"; dnssec-policy "enable-dnssec-@tld@";
}; };
{% endfor %}

View File

@ -40,26 +40,27 @@ O="OMNIPRESENT"
U="UNRETENTIVE" 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. # initial signing of a zone.
# #
for tld in autosign manual; do
# Step 1: # Step 1:
# This is an unsigned zone and named should perform the initial steps of # This is an unsigned zone and named should perform the initial steps of
# introducing the DNSSEC records in the right order. # introducing the DNSSEC records in the right order.
setup step1.enable-dnssec.autosign setup step1.enable-dnssec.$tld
cp template.db.in $zonefile cp template.db.in $zonefile
# Step 2: # Step 2:
# The DNSKEY has been published long enough to become OMNIPRESENT. # The DNSKEY has been published long enough to become OMNIPRESENT.
setup step2.enable-dnssec.autosign setup step2.enable-dnssec.$tld
# DNSKEY TTL: 300 seconds # DNSKEY TTL: 300 seconds
# zone-propagation-delay: 5 minutes (300 seconds) # zone-propagation-delay: 5 minutes (300 seconds)
# publish-safety: 5 minutes (300 seconds) # publish-safety: 5 minutes (300 seconds)
# Total: 900 seconds # Total: 900 seconds
TpubN="now-900s" TpubN="now-900s"
keytimes="-P ${TpubN} -A ${TpubN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$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: # Step 3:
# The zone signatures have been published long enough to become OMNIPRESENT. # 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: # Passed time since publication:
# max-zone-ttl: 12 hours (43200 seconds) # max-zone-ttl: 12 hours (43200 seconds)
# zone-propagation-delay: 5 minutes (300 seconds) # zone-propagation-delay: 5 minutes (300 seconds)
TpubN="now-43500s" TpubN="now-43500s"
# We can submit the DS now. # We can submit the DS now.
keytimes="-P ${TpubN} -A ${TpubN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$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: # Step 4:
# The DS has been submitted long enough ago to become OMNIPRESENT. # 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) # DS TTL: 2 hour (7200 seconds)
# parent-propagation-delay: 1 hour (3600 seconds) # parent-propagation-delay: 1 hour (3600 seconds)
# Total aditional time: 10800 seconds # Total aditional time: 10800 seconds
@ -92,9 +93,10 @@ setup step4.enable-dnssec.autosign
TpubN="now-54300s" TpubN="now-54300s"
TsbmN="now-10800s" TsbmN="now-10800s"
keytimes="-P ${TpubN} -A ${TpubN} -P sync ${TsbmN}" 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 $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" cat template.db.in "${CSK}.key" >"$infile"
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >>"$infile"
cp $infile $zonefile 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 $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

View File

@ -11,8 +11,11 @@
# pylint: disable=redefined-outer-name,unused-import # pylint: disable=redefined-outer-name,unused-import
import pytest
import isctest import isctest
from isctest.kasp import Ipub, IpubC, Iret from isctest.kasp import Ipub, IpubC, Iret
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
alg, alg,
@ -44,11 +47,40 @@ OFFSETS["step3"] = -int(IRETZSK.total_seconds())
OFFSETS["step4"] = -int(IPUBC.total_seconds() + IRETKSK.total_seconds()) OFFSETS["step4"] = -int(IPUBC.total_seconds() + IRETKSK.total_seconds())
def test_rollover_enable_dnssec_step1(alg, size, ns3): @pytest.mark.parametrize(
zone = "step1.enable-dnssec.autosign" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -59,14 +91,24 @@ def test_rollover_enable_dnssec_step1(alg, size, ns3):
# after the publication interval. # after the publication interval.
"nextev": IPUB, "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): @pytest.mark.parametrize(
zone = "step2.enable-dnssec.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -81,14 +123,45 @@ def test_rollover_enable_dnssec_step2(alg, size, ns3):
# Minus the time already elapsed. # Minus the time already elapsed.
"nextev": IRETZSK - IPUB, "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): @pytest.mark.parametrize(
zone = "step3.enable-dnssec.autosign" "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) 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 = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "cdss": CDSS,
@ -102,14 +175,24 @@ def test_rollover_enable_dnssec_step3(alg, size, ns3):
# This is after the retire interval. # This is after the retire interval.
"nextev": IRETKSK, "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): @pytest.mark.parametrize(
zone = "step4.enable-dnssec.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
"zone": zone, "zone": zone,
"cdss": CDSS, "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. # established. So we fall back to the default loadkeys interval.
"nextev": TIMEDELTA["PT1H"], "nextev": TIMEDELTA["PT1H"],
} }
isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)

View File

@ -19,5 +19,5 @@ zone "three-is-a-crowd.kasp" {
file "three-is-a-crowd.kasp.db"; file "three-is-a-crowd.kasp.db";
inline-signing yes; inline-signing yes;
/* Use same policy as KSK rollover test zones. */ /* Use same policy as KSK rollover test zones. */
dnssec-policy "ksk-doubleksk"; dnssec-policy "ksk-doubleksk-autosign";
}; };

View File

@ -27,7 +27,7 @@ from rollover.common import (
CDSS = ["CDS (SHA-256)"] CDSS = ["CDS (SHA-256)"]
POLICY = "ksk-doubleksk" POLICY = "ksk-doubleksk-autosign"
OFFSET1 = -int(timedelta(days=60).total_seconds()) OFFSET1 = -int(timedelta(days=60).total_seconds())
OFFSET2 = -int(timedelta(hours=27).total_seconds()) OFFSET2 = -int(timedelta(hours=27).total_seconds())
TTL = int(KSK_CONFIG["dnskey-ttl"].total_seconds()) TTL = int(KSK_CONFIG["dnskey-ttl"].total_seconds())

View File

@ -11,7 +11,32 @@
* information regarding copyright ownership. * 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-refresh P1W;
signatures-validity P2W; signatures-validity P2W;
signatures-validity-dnskey P2W; signatures-validity-dnskey P2W;

View File

@ -11,36 +11,40 @@
* information regarding copyright ownership. * information regarding copyright ownership.
*/ */
{% set zones = ["autosign", "manual"] %}
include "kasp.conf"; include "kasp.conf";
include "named.common.conf"; include "named.common.conf";
zone "step1.ksk-doubleksk.autosign" { {% for tld in zones %}
zone "step1.ksk-doubleksk.@tld@" {
type primary; type primary;
file "step1.ksk-doubleksk.autosign.db"; file "step1.ksk-doubleksk.@tld@.db";
dnssec-policy "ksk-doubleksk"; dnssec-policy "ksk-doubleksk-@tld@";
}; };
zone "step2.ksk-doubleksk.autosign" { zone "step2.ksk-doubleksk.@tld@" {
type primary; type primary;
file "step2.ksk-doubleksk.autosign.db"; file "step2.ksk-doubleksk.@tld@.db";
dnssec-policy "ksk-doubleksk"; dnssec-policy "ksk-doubleksk-@tld@";
}; };
zone "step3.ksk-doubleksk.autosign" { zone "step3.ksk-doubleksk.@tld@" {
type primary; type primary;
file "step3.ksk-doubleksk.autosign.db"; file "step3.ksk-doubleksk.@tld@.db";
dnssec-policy "ksk-doubleksk"; dnssec-policy "ksk-doubleksk-@tld@";
}; };
zone "step4.ksk-doubleksk.autosign" { zone "step4.ksk-doubleksk.@tld@" {
type primary; type primary;
file "step4.ksk-doubleksk.autosign.db"; file "step4.ksk-doubleksk.@tld@.db";
dnssec-policy "ksk-doubleksk"; dnssec-policy "ksk-doubleksk-@tld@";
}; };
zone "step5.ksk-doubleksk.autosign" { zone "step5.ksk-doubleksk.@tld@" {
type primary; type primary;
file "step5.ksk-doubleksk.autosign.db"; file "step5.ksk-doubleksk.@tld@.db";
dnssec-policy "ksk-doubleksk"; dnssec-policy "ksk-doubleksk-@tld@";
}; };
zone "step6.ksk-doubleksk.autosign" { zone "step6.ksk-doubleksk.@tld@" {
type primary; type primary;
file "step6.ksk-doubleksk.autosign.db"; file "step6.ksk-doubleksk.@tld@.db";
dnssec-policy "ksk-doubleksk"; dnssec-policy "ksk-doubleksk-@tld@";
}; };
{% endfor %}

View File

@ -40,13 +40,14 @@ O="OMNIPRESENT"
U="UNRETENTIVE" 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. # Double-KSK rollover.
# #
for tld in autosign manual; do
# Step 1: # Step 1:
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
setup step1.ksk-doubleksk.autosign setup step1.ksk-doubleksk.$tld
TactN="now-7d" TactN="now-7d"
keytimes="-P ${TactN} -A ${TactN}" keytimes="-P ${TactN} -A ${TactN}"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $keytimes $zone 2>keygen.out.$zone.1) 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: # Step 2:
# It is time to submit the introduce the new KSK. # It is time to submit the introduce the new KSK.
setup step2.ksk-doubleksk.autosign setup step2.ksk-doubleksk.$tld
# Lksk: 60d # Lksk: 60d
# Dreg: n/a # Dreg: n/a
# DprpC: 1h # 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: # Step 3:
# It is time to submit the DS. # It is time to submit the DS.
setup step3.ksk-doubleksk.autosign setup step3.ksk-doubleksk.$tld
# According to RFC 7583: # According to RFC 7583:
# Iret = DprpP + TTLds (+retire-safety) # 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: # Step 4:
# The DS should be swapped now. # The DS should be swapped now.
setup step4.ksk-doubleksk.autosign setup step4.ksk-doubleksk.$tld
# Tpub(N) = now - Lksk - Iret = now - 60d - 50h # Tpub(N) = now - Lksk - Iret = now - 60d - 50h
# = now - 1440h - 50h = now - 1490h # = now - 1440h - 50h = now - 1490h
# Tact(N) = now - 1490h + 27h = now - 1463h # 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: # Step 5:
# The predecessor DNSKEY is removed long enough that is has become HIDDEN. # 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). # Subtract DNSKEY TTL + zone-propagation-delay from all the times (3h).
# Tpub(N) = now - 1490h - 3h = now - 1493h # Tpub(N) = now - 1490h - 3h = now - 1493h
# Tact(N) = now - 1463h - 3h = now - 1466h # 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: # Step 6:
# The predecessor DNSKEY can be purged. # 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). # Subtract purge-keys interval from all the times (1h).
TpubN="now-1494h" TpubN="now-1494h"
TactN="now-1467h" 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" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
cp $infile $zonefile 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 $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

View File

@ -13,7 +13,10 @@
from datetime import timedelta from datetime import timedelta
import pytest
import isctest import isctest
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
alg, 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()) OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(KSK_CONFIG["purge-keys"].total_seconds())
def test_ksk_doubleksk_step1(alg, size, ns3): @pytest.mark.parametrize(
zone = "step1.ksk-doubleksk.autosign" "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) 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 = { step = {
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
"zone": zone, "zone": zone,
@ -63,14 +77,46 @@ def test_ksk_doubleksk_step1(alg, size, ns3):
# already passed). # already passed).
"nextev": KSK_LIFETIME - KSK_IPUB - timedelta(days=7), "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): @pytest.mark.parametrize(
zone = "step2.ksk-doubleksk.autosign" "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) 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 = { step = {
# Successor KSK is prepublished (and signs DNSKEY RRset). # Successor KSK is prepublished (and signs DNSKEY RRset).
# KSK1 goal: omnipresent -> hidden # 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. # Next key event is when the successor KSK becomes OMNIPRESENT.
"nextev": KSK_IPUB, "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): @pytest.mark.parametrize(
zone = "step3.ksk-doubleksk.autosign" "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) 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 = { step = {
# The successor DNSKEY RRset has become omnipresent. The # The successor DNSKEY RRset has become omnipresent. The
# predecessor DS can be withdrawn and the successor DS can be # 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. # successor DS. This is the the retire interval.
"nextev": KSK_IRET, "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): @pytest.mark.parametrize(
zone = "step4.ksk-doubleksk.autosign" "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) 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 = { step = {
# The predecessor DNSKEY may be removed, the successor DS is # The predecessor DNSKEY may be removed, the successor DS is
# omnipresent. # omnipresent.
@ -145,14 +273,24 @@ def test_ksk_doubleksk_step4(alg, size, ns3):
# This is the DNSKEY TTL plus zone propagation delay. # This is the DNSKEY TTL plus zone propagation delay.
"nextev": KSK_KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step5.ksk-doubleksk.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
# The predecessor DNSKEY is long enough removed from the zone it # The predecessor DNSKEY is long enough removed from the zone it
# has become hidden. # 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. # This is the KSK lifetime minus Ipub minus Iret minus time elapsed.
"nextev": KSK_LIFETIME - KSK_IPUB - KSK_IRET - KSK_KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step6.ksk-doubleksk.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
# Predecessor KSK is now purged. # Predecessor KSK is now purged.
"zone": zone, "zone": zone,
@ -188,4 +336,4 @@ def test_ksk_doubleksk_step6(alg, size, ns3):
], ],
"nextev": None, "nextev": None,
} }
isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, POLICY, step) isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)

View File

@ -11,7 +11,28 @@
* information regarding copyright ownership. * 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-refresh P1W;
signatures-validity P2W; signatures-validity P2W;
signatures-validity-dnskey P2W; signatures-validity-dnskey P2W;

View File

@ -11,36 +11,40 @@
* information regarding copyright ownership. * information regarding copyright ownership.
*/ */
{% set zones = ["autosign", "manual"] %}
include "kasp.conf"; include "kasp.conf";
include "named.common.conf"; include "named.common.conf";
zone "step1.zsk-prepub.autosign" { {% for tld in zones %}
zone "step1.zsk-prepub.@tld@" {
type primary; type primary;
file "step1.zsk-prepub.autosign.db"; file "step1.zsk-prepub.@tld@.db";
dnssec-policy "zsk-prepub"; dnssec-policy "zsk-prepub-@tld@";
}; };
zone "step2.zsk-prepub.autosign" { zone "step2.zsk-prepub.@tld@" {
type primary; type primary;
file "step2.zsk-prepub.autosign.db"; file "step2.zsk-prepub.@tld@.db";
dnssec-policy "zsk-prepub"; dnssec-policy "zsk-prepub-@tld@";
}; };
zone "step3.zsk-prepub.autosign" { zone "step3.zsk-prepub.@tld@" {
type primary; type primary;
file "step3.zsk-prepub.autosign.db"; file "step3.zsk-prepub.@tld@.db";
dnssec-policy "zsk-prepub"; dnssec-policy "zsk-prepub-@tld@";
}; };
zone "step4.zsk-prepub.autosign" { zone "step4.zsk-prepub.@tld@" {
type primary; type primary;
file "step4.zsk-prepub.autosign.db"; file "step4.zsk-prepub.@tld@.db";
dnssec-policy "zsk-prepub"; dnssec-policy "zsk-prepub-@tld@";
}; };
zone "step5.zsk-prepub.autosign" { zone "step5.zsk-prepub.@tld@" {
type primary; type primary;
file "step5.zsk-prepub.autosign.db"; file "step5.zsk-prepub.@tld@.db";
dnssec-policy "zsk-prepub"; dnssec-policy "zsk-prepub-@tld@";
}; };
zone "step6.zsk-prepub.autosign" { zone "step6.zsk-prepub.@tld@" {
type primary; type primary;
file "step6.zsk-prepub.autosign.db"; file "step6.zsk-prepub.@tld@.db";
dnssec-policy "zsk-prepub"; dnssec-policy "zsk-prepub-@tld@";
}; };
{% endfor %}

View File

@ -40,13 +40,14 @@ O="OMNIPRESENT"
U="UNRETENTIVE" 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. # Pre-Publication rollover.
# #
for tld in autosign manual; do
# Step 1: # Step 1:
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
setup step1.zsk-prepub.autosign setup step1.zsk-prepub.$tld
TactN="now-7d" TactN="now-7d"
keytimes="-P ${TactN} -A ${TactN}" keytimes="-P ${TactN} -A ${TactN}"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1) 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: # Step 2:
# It is time to pre-publish the successor ZSK. # It is time to pre-publish the successor ZSK.
setup step2.zsk-prepub.autosign setup step2.zsk-prepub.$tld
# According to RFC 7583: # According to RFC 7583:
# Tact(N) = now + Ipub - Lzsk = now + 26h - 30d # Tact(N) = now + Ipub - Lzsk = now + 26h - 30d
# = now + 26h - 30d = now 694h # = 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: # Step 3:
# After the publication interval has passed the DNSKEY of the successor ZSK # 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. # 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: # According to RFC 7583:
# Tpub(N+1) <= Tact(N) + Lzsk - Ipub # Tpub(N+1) <= Tact(N) + Lzsk - Ipub
# Tact(N+1) = Tact(N) + Lzsk # 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: # Step 4:
# After the retire interval has passed the predecessor DNSKEY can be # After the retire interval has passed the predecessor DNSKEY can be
# removed from the zone. # removed from the zone.
setup step4.zsk-prepub.autosign setup step4.zsk-prepub.$tld
# Lzsk: 30d # Lzsk: 30d
# Ipub: 26h # Ipub: 26h
# Dsgn: 1w # 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: # Step 5:
# The predecessor DNSKEY is removed long enough that is has become HIDDEN. # 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). # Subtract DNSKEY TTL + zone-propagation-delay from all the times (2h).
# Tact(N) = now - 961h - 2h = now - 963h # Tact(N) = now - 961h - 2h = now - 963h
# Tpub(N+1) = now - 267h - 2h = now - 269h # 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: # Step 6:
# The predecessor DNSKEY can be purged. # 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). # Subtract purge-keys interval from all the times (1h).
TactN="now-964h" TactN="now-964h"
TremN="now-3h" 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" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile"
cp $infile $zonefile 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 $SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
done

View File

@ -13,8 +13,11 @@
from datetime import timedelta from datetime import timedelta
import pytest
import isctest import isctest
from isctest.kasp import Ipub, Iret from isctest.kasp import Ipub, Iret
from isctest.util import param
from rollover.common import ( from rollover.common import (
pytestmark, pytestmark,
alg, 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()) OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(CONFIG["purge-keys"].total_seconds())
def test_zsk_prepub_step1(alg, size, ns3): @pytest.mark.parametrize(
zone = "step1.zsk-prepub.autosign" "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) 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 = { step = {
# Introduce the first key. This will immediately be active. # Introduce the first key. This will immediately be active.
"zone": zone, "zone": zone,
@ -71,14 +85,45 @@ def test_zsk_prepub_step1(alg, size, ns3):
# already passed). # already passed).
"nextev": ZSK_LIFETIME - IPUB - timedelta(days=7), "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): @pytest.mark.parametrize(
zone = "step2.zsk-prepub.autosign" "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) 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 = { step = {
# it is time to pre-publish the successor zsk. # it is time to pre-publish the successor zsk.
# zsk1 goal: omnipresent -> hidden # 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 # that is the dnskey ttl plus the zone propagation delay
"nextev": IPUB, "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): @pytest.mark.parametrize(
zone = "step3.zsk-prepub.autosign" "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) 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 = { step = {
# predecessor zsk is no longer actively signing. successor zsk is # predecessor zsk is no longer actively signing. successor zsk is
# now actively signing. # now actively signing.
@ -124,14 +214,47 @@ def test_zsk_prepub_step3(alg, size, ns3):
# from the predecessor zsk. # from the predecessor zsk.
"smooth": True, "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): @pytest.mark.parametrize(
zone = "step4.zsk-prepub.autosign" "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) 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 = { step = {
# predecessor zsk is no longer needed. all rrsets are signed with # predecessor zsk is no longer needed. all rrsets are signed with
# the successor zsk. # the successor zsk.
@ -149,14 +272,24 @@ def test_zsk_prepub_step4(alg, size, ns3):
# this is the dnskey ttl plus zone propagation delay. # this is the dnskey ttl plus zone propagation delay.
"nextev": KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step5.zsk-prepub.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
# predecessor zsk is now removed. # predecessor zsk is now removed.
# zsk1 dnskey: unretentive -> hidden # zsk1 dnskey: unretentive -> hidden
@ -172,14 +305,24 @@ def test_zsk_prepub_step5(alg, size, ns3):
# elapsed. # elapsed.
"nextev": ZSK_LIFETIME - IRET - IPUB - KEYTTLPROP, "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): @pytest.mark.parametrize(
zone = "step6.zsk-prepub.autosign" "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) isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = { step = {
# predecessor zsk is now purged. # predecessor zsk is now purged.
"zone": zone, "zone": zone,
@ -189,4 +332,4 @@ def test_zsk_prepub_step6(alg, size, ns3):
], ],
"nextev": None, "nextev": None,
} }
isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step) isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)

View File

@ -6408,6 +6408,16 @@ keys
``insecure``. In this specific case, the existing key files should be moved ``insecure``. In this specific case, the existing key files should be moved
to the zone's ``key-directory`` from the new configuration. 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 .. namedconf:statement:: offline-ksk
:tags: dnssec :tags: dnssec
:short: Specifies whether the DNSKEY, CDS, and CDNSKEY RRsets are being signed offline. :short: Specifies whether the DNSKEY, CDS, and CDNSKEY RRsets are being signed offline.

View File

@ -33,6 +33,7 @@ dnssec-policy "default" {
signatures-validity-dnskey 14d; signatures-validity-dnskey 14d;
// Zone parameters // Zone parameters
manual-mode no;
inline-signing yes; inline-signing yes;
max-zone-ttl 86400; max-zone-ttl 86400;
zone-propagation-delay 300; zone-propagation-delay 300;

View File

@ -16,6 +16,7 @@ dnssec-policy <string> {
dnskey-ttl <duration>; dnskey-ttl <duration>;
inline-signing <boolean>; inline-signing <boolean>;
keys { ( csk | ksk | zsk ) [ key-directory | key-store <string> ] lifetime <duration_or_unlimited> algorithm <string> [ tag-range <integer> <integer> ] [ <integer> ]; ... }; 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>; max-zone-ttl <duration>;
nsec3param [ iterations <integer> ] [ optout <boolean> ] [ salt-length <integer> ]; nsec3param [ iterations <integer> ] [ optout <boolean> ] [ salt-length <integer> ];
offline-ksk <boolean>; offline-ksk <boolean>;

View File

@ -108,6 +108,7 @@ struct dns_kasp {
dns_ttl_t zone_max_ttl; dns_ttl_t zone_max_ttl;
uint32_t zone_propagation_delay; uint32_t zone_propagation_delay;
bool inline_signing; bool inline_signing;
bool manual_mode;
/* Parent settings */ /* Parent settings */
dns_ttl_t parent_ds_ttl; 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. *\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_ttl_t
dns_kasp_zonemaxttl(dns_kasp_t *kasp, bool fallback); dns_kasp_zonemaxttl(dns_kasp_t *kasp, bool fallback);
/*%< /*%<

View File

@ -21,6 +21,11 @@
#include <dst/dst.h> #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 void
dns_keymgr_settime_syncpublish(dst_key_t *key, dns_kasp_t *kasp, bool first); 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, dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
isc_mem_t *mctx, dns_dnsseckeylist_t *keyring, isc_mem_t *mctx, dns_dnsseckeylist_t *keyring,
dns_dnsseckeylist_t *dnskeys, const char *keydir, 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. * Manage keys in 'keyring' and update timing data according to 'kasp' policy.
* Create new keys for 'origin' if necessary. Append all such keys, along * 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 * Update key states and store changes back to disk. Store when to run next
* in 'nexttime'. * 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: * Requires:
*\li 'origin' is a valid FQDN. *\li 'origin' is a valid FQDN.
*\li 'mctx' is a valid memory context. *\li 'mctx' is a valid memory context.

View File

@ -104,6 +104,7 @@ typedef enum {
DNS_ZONEOPT_CHECKSVCB = 1 << 30, /*%< check SVBC records */ DNS_ZONEOPT_CHECKSVCB = 1 << 30, /*%< check SVBC records */
DNS_ZONEOPT_ZONEVERSION = 1U << 31, /*%< enable zoneversion */ DNS_ZONEOPT_ZONEVERSION = 1U << 31, /*%< enable zoneversion */
DNS_ZONEOPT_FULLSIGN = 1ULL << 32, /*%< fully sign zone */ 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___MAX = UINT64_MAX, /* trick to make the ENUM 64-bit wide */
} dns_zoneopt_t; } dns_zoneopt_t;
@ -2210,7 +2211,7 @@ dns_zone_getprivatetype(dns_zone_t *zone);
*/ */
void 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. * 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 * 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 * if the new keys are for algorithms that have already signed the
* zone, then the zone can be re-signed incrementally. * 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 isc_result_t

View File

@ -273,6 +273,22 @@ dns_kasp_setinlinesigning(dns_kasp_t *kasp, bool value) {
kasp->inline_signing = 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_ttl_t
dns_kasp_zonemaxttl(dns_kasp_t *kasp, bool fallback) { dns_kasp_zonemaxttl(dns_kasp_t *kasp, bool fallback) {
REQUIRE(DNS_KASP_VALID(kasp)); REQUIRE(DNS_KASP_VALID(kasp));

View File

@ -342,7 +342,8 @@ keymgr_prepublication_time(dns_dnsseckey_t *key, dns_kasp_t *kasp,
} }
static void 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]; char keystr[DST_KEY_FORMATSIZE];
isc_result_t ret; isc_result_t ret;
isc_stdtime_t retire; 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 != NULL);
REQUIRE(key->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. */ /* 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); ret = dst_key_gettime(key->key, DST_TIME_INACTIVE, &retire);
if (ret != ISC_R_SUCCESS || (retire > now)) { if (ret != ISC_R_SUCCESS || (retire > now)) {
dst_key_settime(key->key, DST_TIME_INACTIVE, now); dst_key_settime(key->key, DST_TIME_INACTIVE, now);
} }
dst_key_setstate(key->key, DST_KEY_GOAL, HIDDEN);
keymgr_settime_remove(key, kasp); 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) { if (dst_key_getstate(key->key, DST_KEY_DNSKEY, &s) != ISC_R_SUCCESS) {
dst_key_setstate(key->key, DST_KEY_DNSKEY, OMNIPRESENT); dst_key_setstate(key->key, DST_KEY_DNSKEY, OMNIPRESENT);
dst_key_settime(key->key, DST_TIME_DNSKEY, now); 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_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. */ /* Update lifetime and retire and remove time accordingly. */
@ -963,7 +981,7 @@ keymgr_dnskey_hidden_or_chained(dns_dnsseckeylist_t *keyring,
*/ */
static bool static bool
keymgr_have_ds(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, int type, 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) */ /* (3a) */
dst_key_state_t states[2][NUM_KEYSTATES] = { dst_key_state_t states[2][NUM_KEYSTATES] = {
/* DNSKEY, ZRRSIG, KRRSIG, DS */ /* 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) || states[0], na, false, false) ||
keymgr_key_exists_with_state(keyring, key, type, next_state, keymgr_key_exists_with_state(keyring, key, type, next_state,
states[1], na, false, false) || states[1], na, false, false) ||
(secure_to_insecure && ((opts & DNS_KEYMGRATTR_S2I) != 0 &&
keymgr_key_exists_with_state(keyring, key, type, next_state, na, keymgr_key_exists_with_state(keyring, key, type, next_state, na,
na, false, false)); na, false, false));
} }
@ -1220,17 +1238,14 @@ keymgr_policy_approval(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
*/ */
static bool static bool
keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
int type, dst_key_state_t next_state, int type, dst_key_state_t next_state, uint8_t opts) {
bool secure_to_insecure) {
/* Debug logging. */ /* Debug logging. */
if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) { if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) {
bool rule1a, rule1b, rule2a, rule2b, rule3a, rule3b; bool rule1a, rule1b, rule2a, rule2b, rule3a, rule3b;
char keystr[DST_KEY_FORMATSIZE]; char keystr[DST_KEY_FORMATSIZE];
dst_key_format(key->key, keystr, sizeof(keystr)); dst_key_format(key->key, keystr, sizeof(keystr));
rule1a = keymgr_have_ds(keyring, key, type, NA, rule1a = keymgr_have_ds(keyring, key, type, NA, opts);
secure_to_insecure); rule1b = keymgr_have_ds(keyring, key, type, next_state, opts);
rule1b = keymgr_have_ds(keyring, key, type, next_state,
secure_to_insecure);
rule2a = keymgr_have_dnskey(keyring, key, type, NA); rule2a = keymgr_have_dnskey(keyring, key, type, NA);
rule2b = keymgr_have_dnskey(keyring, key, type, next_state); rule2b = keymgr_have_dnskey(keyring, key, type, next_state);
rule3a = keymgr_have_rrsig(keyring, key, type, NA); 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 * invalid state. If the rule check passes, also check if
* the next state is also still a valid situation. * 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, NA, opts) ||
keymgr_have_ds(keyring, key, type, next_state, keymgr_have_ds(keyring, key, type, next_state, opts)) &&
secure_to_insecure)) &&
/* /*
* Rule 2: There must be a DNSKEY at all times. Again, first * Rule 2: There must be a DNSKEY at all times. Again, first
* check the current situation, then assess the next state. * 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 static isc_result_t
keymgr_update(dns_dnsseckeylist_t *keyring, dns_kasp_t *kasp, isc_stdtime_t now, 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 changed;
bool force = ((opts & DNS_KEYMGRATTR_FORCESTEP) != 0);
/* Repeat until nothing changed. */ /* Repeat until nothing changed. */
transition: transition:
@ -1534,8 +1550,7 @@ transition:
/* Is the transition DNSSEC safe? */ /* Is the transition DNSSEC safe? */
if (!keymgr_transition_allowed(keyring, dkey, i, if (!keymgr_transition_allowed(keyring, dkey, i,
next_state, next_state, opts))
secure_to_insecure))
{ {
/* No, this would make the zone bogus. */ /* No, this would make the zone bogus. */
isc_log_write( isc_log_write(
@ -1572,6 +1587,28 @@ transition:
continue; 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, isc_log_write(DNS_LOGCATEGORY_DNSSEC,
DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1), DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1),
"keymgr: transition %s %s type %s " "keymgr: transition %s %s type %s "
@ -1580,7 +1617,6 @@ transition:
keystatetags[i], keystatestrings[state], keystatetags[i], keystatestrings[state],
keystatestrings[next_state]); keystatestrings[next_state]);
/* It is safe to make the transition. */
dst_key_setstate(dkey->key, i, next_state); dst_key_setstate(dkey->key, i, next_state);
dst_key_settime(dkey->key, keystatetimes[i], now); dst_key_settime(dkey->key, keystatetimes[i], now);
INSIST(dst_key_ismodified(dkey->key)); INSIST(dst_key_ismodified(dkey->key));
@ -1590,10 +1626,13 @@ transition:
/* We changed something, continue processing. */ /* We changed something, continue processing. */
if (changed) { if (changed) {
result = ISC_R_SUCCESS;
/* No longer force for the next run */
force = false;
goto transition; 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, dns_dnsseckeylist_t *keyring, dns_dnsseckeylist_t *newkeys,
const dns_name_t *origin, dns_rdataclass_t rdclass, const dns_name_t *origin, dns_rdataclass_t rdclass,
dns_kasp_t *kasp, const char *keydir, uint32_t lifetime, 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) { isc_mem_t *mctx) {
char keystr[DST_KEY_FORMATSIZE]; char keystr[DST_KEY_FORMATSIZE];
char namestr[DNS_NAME_FORMATSIZE];
isc_stdtime_t retire = 0, active = 0, prepub = 0; isc_stdtime_t retire = 0, active = 0, prepub = 0;
dns_dnsseckey_t *new_key = NULL; dns_dnsseckey_t *new_key = NULL;
dst_key_t *dst_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 is not allowed, warn.
*/ */
if (!rollover) { if ((opts & DNS_KEYMGRATTR_NOROLL) != 0) {
dst_key_format(active_key->key, keystr, sizeof(keystr)); dst_key_format(active_key->key, keystr, sizeof(keystr));
isc_log_write(DNS_LOGCATEGORY_DNSSEC, isc_log_write(DNS_LOGCATEGORY_DNSSEC,
DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, 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; return ISC_R_SUCCESS;
} }
} else if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) { } else if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) {
char namestr[DNS_NAME_FORMATSIZE];
dns_name_format(origin, namestr, sizeof(namestr)); dns_name_format(origin, namestr, sizeof(namestr));
isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC,
ISC_LOG_DEBUG(1), 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) { if (new_key == NULL) {
/* No key available in keyring, create a new one. */ /* No key available in keyring, create a new one. */
bool csk = (dns_kasp_key_ksk(kaspkey) && 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, dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
isc_mem_t *mctx, dns_dnsseckeylist_t *keyring, isc_mem_t *mctx, dns_dnsseckeylist_t *keyring,
dns_dnsseckeylist_t *dnskeys, const char *keydir, 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 opts, isc_stdtime_t now,
isc_result_t result = ISC_R_SUCCESS; isc_stdtime_t *nexttime) {
isc_result_t result = DNS_R_UNCHANGED;
dns_dnsseckeylist_t newkeys; dns_dnsseckeylist_t newkeys;
bool secure_to_insecure = false;
int numkeys = 0; int numkeys = 0;
int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE); int options = (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE);
char keystr[DST_KEY_FORMATSIZE]; 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. */ /* No match, so retire unwanted retire key. */
if (!found_match) { if (!found_match) {
keymgr_key_retire(dkey, kasp, now); keymgr_key_retire(dkey, kasp, opts, now);
} }
/* Check purge-keys interval. */ /* 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) { ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) {
uint32_t lifetime = dns_kasp_key_lifetime(kkey); uint32_t lifetime = dns_kasp_key_lifetime(kkey);
dns_dnsseckey_t *active_key = NULL; dns_dnsseckey_t *active_key = NULL;
bool rollover_allowed = true;
/* Do we have keys available for this kasp key? */ /* Do we have keys available for this kasp key? */
ISC_LIST_FOREACH(*keyring, dkey, link) { 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. * Retire excess keys in use.
*/ */
keymgr_key_retire(dkey, kasp, keymgr_key_retire(dkey, kasp,
now); opts, now);
} }
continue; continue;
} }
@ -2206,7 +2287,7 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
keystr, keystr,
keymgr_keyrole(dnskey->key), keymgr_keyrole(dnskey->key),
dns_kasp_getname(kasp)); dns_kasp_getname(kasp));
rollover_allowed = false; opts |= DNS_KEYMGRATTR_NOROLL;
active_key = dnskey; active_key = dnskey;
break; break;
} }
@ -2214,10 +2295,11 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
} }
/* See if this key requires a rollover. */ /* See if this key requires a rollover. */
RETERR(keymgr_key_rollover(kkey, active_key, keyring, &newkeys, RETERR(keymgr_key_rollover(
origin, rdclass, kasp, keydir, kkey, active_key, keyring, &newkeys, origin, rdclass,
lifetime, rollover_allowed, now, kasp, keydir, lifetime, opts, now, nexttime, mctx));
nexttime, mctx));
opts &= ~DNS_KEYMGRATTR_NOROLL;
} }
/* Walked all kasp key configurations. Append new keys. */ /* 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 * If the policy has an empty key list, this means the zone is going
* back to unsigned. * 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. */ /* 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. */ /* Store key states and update hints. */
ISC_LIST_FOREACH(*keyring, dkey, link) { 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)) { if (dst_key_getttl(dkey->key) != dns_kasp_dnskeyttl(kasp)) {
dst_key_setttl(dkey->key, dns_kasp_dnskeyttl(kasp)); dst_key_setttl(dkey->key, dns_kasp_dnskeyttl(kasp));
modified = true; modified = true;
retval = ISC_R_SUCCESS;
} }
if (modified && !dkey->purge) { if (modified && !dkey->purge) {
const char *directory = dst_key_directory(dkey->key); 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); dst_key_setmodified(dkey->key, false);
} }
result = ISC_R_SUCCESS; result = retval;
failure: failure:
if (result != ISC_R_SUCCESS) { if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
ISC_LIST_FOREACH(newkeys, newkey, link) { ISC_LIST_FOREACH(newkeys, newkey, link) {
ISC_LIST_UNLINK(newkeys, newkey, link); ISC_LIST_UNLINK(newkeys, newkey, link);
INSIST(newkey->key != NULL); INSIST(newkey->key != NULL);

View File

@ -21211,7 +21211,7 @@ checkds_done(void *arg) {
/* Rekey after checkds. */ /* Rekey after checkds. */
if (rekey) { if (rekey) {
dns_zone_rekey(zone, false); dns_zone_rekey(zone, false, false);
} }
failure: failure:
@ -22233,6 +22233,8 @@ zone_rekey(dns_zone_t *zone) {
bool newalg = false; bool newalg = false;
bool fullsign; bool fullsign;
bool offlineksk = false; bool offlineksk = false;
bool kasp_change = false;
uint8_t options = 0;
uint32_t sigval = 0; uint32_t sigval = 0;
dns_ttl_t ttl = 3600; dns_ttl_t ttl = 3600;
const char *dir = NULL; const char *dir = NULL;
@ -22351,6 +22353,14 @@ zone_rekey(dns_zone_t *zone) {
*/ */
fullsign = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FULLSIGN); 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) { if (offlineksk) {
/* Lookup the correct bundle in the SKR. */ /* Lookup the correct bundle in the SKR. */
LOCK_ZONE(zone); LOCK_ZONE(zone);
@ -22456,10 +22466,14 @@ zone_rekey(dns_zone_t *zone) {
dns_zone_lock_keyfiles(zone); dns_zone_lock_keyfiles(zone);
result = dns_keymgr_run(&zone->origin, zone->rdclass, result = dns_keymgr_run(&zone->origin, zone->rdclass,
mctx, &keys, &dnskeys, dir, mctx, &keys, &dnskeys, dir,
kasp, now, &nexttime); kasp, options, now, &nexttime);
dns_zone_unlock_keyfiles(zone); 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, dnssec_log(zone, ISC_LOG_ERROR,
"zone_rekey:dns_keymgr_run " "zone_rekey:dns_keymgr_run "
"failed: %s", "failed: %s",
@ -22677,7 +22691,7 @@ zone_rekey(dns_zone_t *zone) {
"allowed"); "allowed");
} }
if (newactive || fullsign || sane_diff) { if (newactive || fullsign || sane_diff || kasp_change) {
CHECK(dns_diff_apply(&diff, db, ver)); CHECK(dns_diff_apply(&diff, db, ver));
CHECK(clean_nsec3param(zone, db, ver, &diff)); CHECK(clean_nsec3param(zone, db, ver, &diff));
CHECK(add_signing_records(db, zone->privatetype, ver, CHECK(add_signing_records(db, zone->privatetype, ver,
@ -22936,6 +22950,13 @@ failure:
0); 0);
isc_time_nowplusinterval(&zone->refreshkeytime, &ival); 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); UNLOCK_ZONE(zone);
dns_diff_clear(&diff); dns_diff_clear(&diff);
@ -22974,7 +22995,7 @@ failure:
} }
void 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; isc_time_t now;
if (zone->type == dns_zone_primary && zone->loop != NULL) { if (zone->type == dns_zone_primary && zone->loop != NULL) {
@ -22983,6 +23004,9 @@ dns_zone_rekey(dns_zone_t *zone, bool fullsign) {
if (fullsign) { if (fullsign) {
DNS_ZONE_SETOPTION(zone, DNS_ZONEOPT_FULLSIGN); DNS_ZONE_SETOPTION(zone, DNS_ZONEOPT_FULLSIGN);
} }
if (forcekeymgr) {
DNS_ZONE_SETOPTION(zone, DNS_ZONEOPT_FORCEKEYMGR);
}
now = isc_time_now(); now = isc_time_now();
zone->refreshkeytime = now; zone->refreshkeytime = now;

View File

@ -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 zonepropdelay = 0, parentpropdelay = 0;
uint32_t ipub = 0, iret = 0; uint32_t ipub = 0, iret = 0;
uint32_t ksk_min_lifetime = 0, zsk_min_lifetime = 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(config != NULL);
REQUIRE(kaspp != NULL && *kaspp == 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); 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); maxttl = get_duration(maps, "max-zone-ttl", DNS_KASP_ZONE_MAXTTL);
dns_kasp_setzonemaxttl(kasp, maxttl); dns_kasp_setzonemaxttl(kasp, maxttl);

View File

@ -2213,6 +2213,7 @@ static cfg_clausedef_t dnssecpolicy_clauses[] = {
{ "dnskey-ttl", &cfg_type_duration, 0 }, { "dnskey-ttl", &cfg_type_duration, 0 },
{ "inline-signing", &cfg_type_boolean, 0 }, { "inline-signing", &cfg_type_boolean, 0 },
{ "keys", &cfg_type_kaspkeys, 0 }, { "keys", &cfg_type_kaspkeys, 0 },
{ "manual-mode", &cfg_type_boolean, 0 },
{ "max-zone-ttl", &cfg_type_duration, 0 }, { "max-zone-ttl", &cfg_type_duration, 0 },
{ "nsec3param", &cfg_type_nsec3, 0 }, { "nsec3param", &cfg_type_nsec3, 0 },
{ "offline-ksk", &cfg_type_boolean, 0 }, { "offline-ksk", &cfg_type_boolean, 0 },