return True
+def verify_rrsig_is_refreshed(
+ server, fqdn, zonefile, qname, qtype, ksks, zsks, tsig=None
+):
+ """
+ Verify signature for RRset has been refreshed.
+ """
+ response = _query(server, qname, qtype, tsig=tsig)
+
+ if response.rcode() != dns.rcode.NOERROR:
+ return False
+
+ rrtype = dns.rdatatype.to_text(qtype)
+ match = f"{qname}. {DEFAULT_TTL} IN {rrtype}"
+ rrsigs = []
+ for rrset in response.answer:
+ if rrset.match(
+ dns.name.from_text(qname), dns.rdataclass.IN, dns.rdatatype.RRSIG, qtype
+ ):
+ rrsigs.append(rrset)
+ elif not match in rrset.to_text():
+ return False
+
+ if len(rrsigs) == 0:
+ return False
+
+ tmp_zonefile = f"{zonefile}.tmp"
+ isctest.run.cmd(
+ [
+ os.environ["CHECKZONE"],
+ "-D",
+ "-q",
+ "-o",
+ tmp_zonefile,
+ "-f",
+ "raw",
+ fqdn,
+ zonefile,
+ ],
+ )
+
+ zone = dns.zone.from_file(tmp_zonefile, fqdn)
+ for rrsig in rrsigs:
+ if isctest.util.zone_contains(zone, rrsig):
+ return False
+
+ # Zone is updated, ready to verify the signatures.
+ check_signatures(rrsigs, qtype, fqdn, ksks, zsks)
+
+ return True
+
+
+def verify_rrsig_is_reused(server, fqdn, zonefile, qname, qtype, ksks, zsks, tsig=None):
+ """
+ Verify signature for RRset has been reused.
+ """
+ response = _query(server, qname, qtype, tsig=tsig)
+
+ assert response.rcode() == dns.rcode.NOERROR
+
+ rrtype = dns.rdatatype.to_text(qtype)
+ match = f"{qname}. {DEFAULT_TTL} IN {rrtype}"
+ rrsigs = []
+ for rrset in response.answer:
+ if rrset.match(
+ dns.name.from_text(qname), dns.rdataclass.IN, dns.rdatatype.RRSIG, qtype
+ ):
+ rrsigs.append(rrset)
+ else:
+ assert match in rrset.to_text()
+
+ tmp_zonefile = f"{zonefile}.tmp"
+ isctest.run.cmd(
+ [
+ os.environ["CHECKZONE"],
+ "-D",
+ "-q",
+ "-o",
+ tmp_zonefile,
+ "-f",
+ "raw",
+ fqdn,
+ zonefile,
+ ],
+ )
+
+ zone = dns.zone.from_file(tmp_zonefile, dns.name.from_text(fqdn), relativize=False)
+ for rrsig in rrsigs:
+ assert isctest.util.zone_contains(zone, rrsig)
+
+ check_signatures(rrsigs, qtype, fqdn, ksks, zsks)
+
+
def next_key_event_equals(server, zone, next_event):
if next_event is None:
# No next key event check.
Then, optional data for specific tests may follow:
- "goal", "dnskey", "krrsig", "zrrsig", "ds", followed by a value,
sets the given state to the specific value
+ - "missing", set if the private key file for this key is not available.
- "offset", an offset for testing key rollover timings
"""
proplist = []
elif line[i].startswith("offset:"):
keyval = line[i].split(":")
keyprop.properties["offset"] = timedelta(seconds=int(keyval[1]))
+ elif line[i] == "missing":
+ keyprop.properties["private"] = False
else:
assert False, f"undefined optional data {line[i]}"
cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
$SIGNER -PS -z -x -s now-2w -e now-1mi -o $zone -f "${zonefile}" $infile >signer.out.$zone.1 2>&1
+# Treat the next zones as if they were signed six months ago.
+T="now-6mo"
+keytimes="-P $T -A $T"
+
# These signatures are set to expire long in the past, update immediately.
setup expired-sigs.autosign
-T="now-6mo"
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $zsktimes $zone 2>keygen.out.$zone.2)
+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"
# The DNSKEY's TTLs do not match the policy.
setup dnskey-ttl-mismatch.autosign
-T="now-6mo"
-keytimes="-P $T -A $T"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 30 -f KSK $keytimes $zone 2>keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 30 $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
# These signatures are still good, and can be reused.
setup fresh-sigs.autosign
-T="now-6mo"
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $zsktimes $zone 2>keygen.out.$zone.2)
+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"
# These signatures are still good, but not fresh enough, update immediately.
setup unfresh-sigs.autosign
-T="now-6mo"
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $zsktimes $zone 2>keygen.out.$zone.2)
+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"
# These signatures are still good, but the private KSK is missing.
setup ksk-missing.autosign
-T="now-6mo"
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T"
+# KSK file will be gone missing, so we set expected times during setup.
+TI="now+550d" # Lifetime of 2 years minus 6 months equals 550 days
+TD="now+13226h" # 550 days plus retire time of 1 day 2 hours equals 13226 hours
+TS="now-257755mi" # 6 months minus 1 day, 5 minutes equals 257695 minutes
+ksktimes="$keytimes -P sync $TS -I $TI -D $TD"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $zsktimes $zone 2>keygen.out.$zone.2)
+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"
# These signatures are still good, but the private ZSK is missing.
setup zsk-missing.autosign
-T="now-6mo"
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
+# ZSK file will be gone missing, so we set expected times during setup.
+TI="now+185d" # Lifetime of 1 year minus 6 months equals 185 days
+TD="now+277985mi" # 185 days plus retire time (sign delay, retire safety, propagation, zone TTL)
+zsktimes="$keytimes -I $TI -D $TD"
+KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $keytimes $zone 2>keygen.out.$zone.1)
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $zsktimes $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
# These signatures are still good, but the key files will be removed
# before a second run of reconfiguring keys.
setup keyfiles-missing.autosign
-T="now-6mo"
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 $zsktimes $zone 2>keygen.out.$zone.2)
+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"
# These signatures are already expired, and the private ZSK is retired.
setup zsk-retired.autosign
-T="now-6mo"
ksktimes="-P $T -A $T -P sync $T"
zsktimes="-P $T -A $T -I now"
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 300 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
check_subdomain
dnssec_verify
-#
-# Zone: secondary.kasp.
-#
-set_zone "secondary.kasp"
-set_policy "rsasha256" "3" "1234"
-set_server "ns3" "10.53.0.3"
-# Key properties, timings and states same as above.
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_algorithm_policy
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-
-# Update zone.
-n=$((n + 1))
-echo_i "check that we correctly sign the zone after IXFR for zone ${ZONE} ($n)"
-ret=0
-cp ns2/secondary.kasp.db.in2 ns2/secondary.kasp.db
-rndccmd 10.53.0.2 reload "$ZONE" >/dev/null || log_error "rndc reload zone ${ZONE} failed"
-
-_wait_for_done_subdomains() {
- ret=0
- dig_with_opts "a.${ZONE}" "@${SERVER}" A >"dig.out.$DIR.test$n.a" || return 1
- grep "status: NOERROR" "dig.out.$DIR.test$n.a" >/dev/null || return 1
- grep "a.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.11" "dig.out.$DIR.test$n.a" >/dev/null || return 1
- check_signatures $_qtype "dig.out.$DIR.test$n.a" "ZSK"
- if [ $ret -gt 0 ]; then return $ret; fi
-
- dig_with_opts "d.${ZONE}" "@${SERVER}" A >"dig.out.$DIR.test$n.d" || return 1
- grep "status: NOERROR" "dig.out.$DIR.test$n.d" >/dev/null || return 1
- grep "d.${ZONE}\..*${DEFAULT_TTL}.*IN.*A.*10\.0\.0\.4" "dig.out.$DIR.test$n.d" >/dev/null || return 1
- check_signatures $_qtype "dig.out.$DIR.test$n.d" "ZSK"
- return $ret
-}
-retry_quiet 5 _wait_for_done_subdomains || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
# TODO: we might want to test:
# - configuring a zone with too many active keys (should trigger retire).
# - configuring a zone with keys not matching the policy.
}
#
-# Zone: expired-sigs.autosign.
+# Zone: zsk-retired.autosign.
#
-set_zone "expired-sigs.autosign"
-set_policy "autosign" "2" "300"
+set_zone "zsk-retired.autosign"
+set_policy "autosign" "3" "300"
set_server "ns3" "10.53.0.3"
# Key properties.
key_clear "KEY1"
# Expect only two keys.
key_clear "KEY3"
key_clear "KEY4"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_autosign_policy
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-
-# Verify all signatures have been refreshed.
-check_rrsig_refresh() {
- # Apex.
- _qtypes="DNSKEY SOA NS NSEC"
- for _qtype in $_qtypes; do
- n=$((n + 1))
- echo_i "check ${_qtype} rrsig is refreshed correctly for zone ${ZONE} ($n)"
- ret=0
- dig_with_opts "$ZONE" "@${SERVER}" "$_qtype" >"dig.out.$DIR.test$n" || log_error "dig ${ZONE} ${_qtype} failed"
- grep "status: NOERROR" "dig.out.$DIR.test$n" >/dev/null || log_error "mismatch status in DNS response"
- grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" "dig.out.$DIR.test$n" >"rrsig.out.$ZONE.$_qtype" || log_error "missing RRSIG (${_qtype}) record in response"
- # If this exact RRSIG is also in the zone file it is not refreshed.
- _rrsig=$(cat "rrsig.out.$ZONE.$_qtype")
- grep "${_rrsig}" "${DIR}/${ZONE}.db" >/dev/null && log_error "RRSIG (${_qtype}) not refreshed in zone ${ZONE}"
- test "$ret" -eq 0 || echo_i "failed"
- status=$((status + ret))
- done
-
- # Below apex.
- _labels="a b c ns3"
- for _label in $_labels; do
- _qtypes="A NSEC"
- for _qtype in $_qtypes; do
- n=$((n + 1))
- echo_i "check ${_label} ${_qtype} rrsig is refreshed correctly for zone ${ZONE} ($n)"
- ret=0
- dig_with_opts "${_label}.${ZONE}" "@${SERVER}" "$_qtype" >"dig.out.$DIR.test$n" || log_error "dig ${_label}.${ZONE} ${_qtype} failed"
- grep "status: NOERROR" "dig.out.$DIR.test$n" >/dev/null || log_error "mismatch status in DNS response"
- grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" "dig.out.$DIR.test$n" >"rrsig.out.$ZONE.$_qtype" || log_error "missing RRSIG (${_qtype}) record in response"
- _rrsig=$(cat "rrsig.out.$ZONE.$_qtype")
- grep "${_rrsig}" "${DIR}/${ZONE}.db" >/dev/null && log_error "RRSIG (${_qtype}) not refreshed in zone ${ZONE}"
- test "$ret" -eq 0 || echo_i "failed"
- status=$((status + ret))
- done
- done
-}
-
-check_rrsig_refresh
-
-#
-# Zone: fresh-sigs.autosign.
-#
-set_zone "fresh-sigs.autosign"
-set_policy "autosign" "2" "300"
-set_server "ns3" "10.53.0.3"
-# Key properties, timings and states same as above.
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_autosign_policy
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-
-# Verify signature reuse.
-check_rrsig_reuse() {
- # Apex.
- _qtypes="NS NSEC"
- for _qtype in $_qtypes; do
- n=$((n + 1))
- echo_i "check ${_qtype} rrsig is reused correctly for zone ${ZONE} ($n)"
- ret=0
- dig_with_opts "$ZONE" "@${SERVER}" "$_qtype" >"dig.out.$DIR.test$n" || log_error "dig ${ZONE} ${_qtype} failed"
- grep "status: NOERROR" "dig.out.$DIR.test$n" >/dev/null || log_error "mismatch status in DNS response"
- grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" "dig.out.$DIR.test$n" >"rrsig.out.$ZONE.$_qtype" || log_error "missing RRSIG (${_qtype}) record in response"
- # If this exact RRSIG is also in the signed zone file it is not refreshed.
- _rrsig=$(awk '{print $5, $6, $7, $8, $9, $10, $11, $12, $13, $14;}' <"rrsig.out.$ZONE.$_qtype")
- $CHECKZONE -f raw -F text -s full -o zone.out.${ZONE}.test$n "${ZONE}" "${DIR}/${ZONE}.db.signed" >/dev/null
- grep "${_rrsig}" zone.out.${ZONE}.test$n >/dev/null || log_error "RRSIG (${_qtype}) not reused in zone ${ZONE}"
- test "$ret" -eq 0 || echo_i "failed"
- status=$((status + ret))
- done
-
- # Below apex.
- _labels="a b c ns3"
- for _label in $_labels; do
- _qtypes="A NSEC"
- for _qtype in $_qtypes; do
- n=$((n + 1))
- echo_i "check ${_label} ${_qtype} rrsig is reused correctly for zone ${ZONE} ($n)"
- ret=0
- dig_with_opts "${_label}.${ZONE}" "@${SERVER}" "$_qtype" >"dig.out.$DIR.test$n" || log_error "dig ${_label}.${ZONE} ${_qtype} failed"
- grep "status: NOERROR" "dig.out.$DIR.test$n" >/dev/null || log_error "mismatch status in DNS response"
- grep "${ZONE}\..*IN.*RRSIG.*${_qtype}.*${ZONE}" "dig.out.$DIR.test$n" >"rrsig.out.$ZONE.$_qtype" || log_error "missing RRSIG (${_qtype}) record in response"
- # If this exact RRSIG is also in the signed zone file it is not refreshed.
- _rrsig=$(awk '{print $5, $6, $7, $8, $9, $10, $11, $12, $13, $14;}' <"rrsig.out.$ZONE.$_qtype")
- $CHECKZONE -f raw -F text -s full -o zone.out.${ZONE}.test$n "${ZONE}" "${DIR}/${ZONE}.db.signed" >/dev/null
- grep "${_rrsig}" zone.out.${ZONE}.test$n >/dev/null || log_error "RRSIG (${_qtype}) not reused in zone ${ZONE}"
- test "$ret" -eq 0 || echo_i "failed"
- status=$((status + ret))
- done
- done
-}
-
-check_rrsig_reuse
-
-#
-# Zone: unfresh-sigs.autosign.
-#
-set_zone "unfresh-sigs.autosign"
-set_policy "autosign" "2" "300"
-set_server "ns3" "10.53.0.3"
-# Key properties, timings and states same as above.
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_autosign_policy
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-check_rrsig_refresh
-
-#
-# Zone: ksk-missing.autosign.
-#
-set_zone "ksk-missing.autosign"
-set_policy "autosign" "2" "300"
-set_server "ns3" "10.53.0.3"
-# Key properties, timings and states same as above.
-# Skip checking the private file, because it is missing.
-key_set "KEY1" "PRIVATE" "no"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-check_apex
-check_subdomain
-dnssec_verify
-
-# Restore the PRIVATE variable.
-key_set "KEY1" "PRIVATE" "yes"
-
-#
-# Zone: zsk-missing.autosign.
-#
-set_zone "zsk-missing.autosign"
-set_policy "autosign" "2" "300"
-set_server "ns3" "10.53.0.3"
-# Key properties, timings and states same as above.
-# Skip checking the private file, because it is missing.
-key_set "KEY2" "PRIVATE" "no"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-# For the apex, we expect the SOA to be signed with the KSK because the ZSK is
-# offline. Temporary treat KEY1 as a zone signing key too.
-set_keyrole "KEY1" "csk"
-set_zonesigning "KEY1" "yes"
-set_zonesigning "KEY2" "no"
-check_apex
-set_keyrole "KEY1" "ksk"
-set_zonesigning "KEY1" "no"
-set_zonesigning "KEY2" "yes"
-check_subdomain
-dnssec_verify
-
-# Restore the PRIVATE variable.
-key_set "KEY2" "PRIVATE" "yes"
-
-#
-# Zone: zsk-retired.autosign.
-#
-set_zone "zsk-retired.autosign"
-set_policy "autosign" "3" "300"
-set_server "ns3" "10.53.0.3"
# The third key is not yet expected to be signing.
set_keyrole "KEY3" "zsk"
set_keylifetime "KEY3" "31536000"
check_apex
check_subdomain
dnssec_verify
-check_rrsig_refresh
+#check_rrsig_refresh
# Load again, make sure the purged key is not an issue when verifying keys.
echo_i "load keys for $ZONE, making sure a recently purged key is not an issue when verifying keys ($n)"
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
-#
-# Zone: legacy-keys.kasp.
-#
-set_zone "legacy-keys.kasp"
-# This zone has two active keys and two old keys left in key directory, so
-# expect 4 key files.
-set_policy "migrate-to-dnssec-policy" "4" "1234"
-set_server "ns3" "10.53.0.3"
-
-# Key properties.
-key_clear "KEY1"
-set_keyrole "KEY1" "ksk"
-set_keylifetime "KEY1" "16070400"
-set_keyalgorithm "KEY1" "8" "RSASHA256" "2048"
-set_keysigning "KEY1" "yes"
-set_zonesigning "KEY1" "no"
-
-key_clear "KEY2"
-set_keyrole "KEY2" "zsk"
-set_keylifetime "KEY2" "16070400"
-set_keyalgorithm "KEY2" "8" "RSASHA256" "2048"
-set_keysigning "KEY2" "no"
-set_zonesigning "KEY2" "yes"
-# KSK: DNSKEY, RRSIG (ksk) published. DS needs to wait.
-# ZSK: DNSKEY, RRSIG (zsk) published.
-set_keystate "KEY1" "GOAL" "omnipresent"
-set_keystate "KEY1" "STATE_DNSKEY" "rumoured"
-set_keystate "KEY1" "STATE_KRRSIG" "rumoured"
-set_keystate "KEY1" "STATE_DS" "hidden"
-
-set_keystate "KEY2" "GOAL" "omnipresent"
-set_keystate "KEY2" "STATE_DNSKEY" "rumoured"
-set_keystate "KEY2" "STATE_ZRRSIG" "rumoured"
-# Two keys only.
-key_clear "KEY3"
-key_clear "KEY4"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-
-# Make sure the correct legacy keys were used (and not the removed predecessor
-# keys).
-n=$((n + 1))
-echo_i "check correct keys were used when migrating zone ${ZONE} to dnssec-policy ($n)"
-ret=0
-kskfile=$(cat ns3/legacy-keys.kasp.ksk)
-basefile=$(key_get KEY1 BASEFILE)
-echo_i "filename: $basefile (expect $kskfile)"
-test "$DIR/$kskfile" = "$basefile" || ret=1
-zskfile=$(cat ns3/legacy-keys.kasp.zsk)
-basefile=$(key_get KEY2 BASEFILE)
-echo_i "filename: $basefile (expect $zskfile)"
-test "$DIR/$zskfile" = "$basefile" || ret=1
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
-# KSK times.
-created=$(key_get KEY1 CREATED)
-keyfile=$(key_get KEY1 BASEFILE)
-grep "; Publish:" "${keyfile}.key" >published.test${n}.key1
-published=$(awk '{print $3}' <published.test${n}.key1)
-set_keytime "KEY1" "PUBLISHED" "${published}"
-set_keytime "KEY1" "ACTIVE" "${published}"
-published=$(key_get KEY1 PUBLISHED)
-# The DS can be published if the zone is fully signed.
-# This happens after max-zone-ttl (1d) plus
-# zone-propagation-delay (300s) = 86400 + 300 = 86700.
-set_addkeytime "KEY1" "SYNCPUBLISH" "${published}" 86700
-# Key lifetime is 6 months, 315360000 seconds.
-set_addkeytime "KEY1" "RETIRED" "${published}" 16070400
-# The key is removed after the retire time plus DS TTL (1d), parent
-# propagation delay (1h), and retire safety (1h) = 86400 + 3600 + 3600 = 93600.
-retired=$(key_get KEY1 RETIRED)
-set_addkeytime "KEY1" "REMOVED" "${retired}" 93600
-
-# ZSK times.
-created=$(key_get KEY2 CREATED)
-keyfile=$(key_get KEY2 BASEFILE)
-grep "; Publish:" "${keyfile}.key" >published.test${n}.key2
-published=$(awk '{print $3}' <published.test${n}.key2)
-set_keytime "KEY2" "PUBLISHED" "${published}"
-set_keytime "KEY2" "ACTIVE" "${published}"
-published=$(key_get KEY2 PUBLISHED)
-# Key lifetime is 6 months, 315360000 seconds.
-set_addkeytime "KEY2" "RETIRED" "${published}" 16070400
-# The key is removed after the retire time plus max zone ttl (1d), zone
-# propagation delay (300s), retire safety (1h), and sign delay (signature
-# validity minus refresh, 9d) = 86400 + 300 + 3600 + 777600 = 867900.
-retired=$(key_get KEY2 RETIRED)
-set_addkeytime "KEY2" "REMOVED" "${retired}" 867900
-
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-
-#
-# Zone: keyfiles-missing.autosign.
-#
-set_zone "keyfiles-missing.autosign"
-set_policy "autosign" "2" "300"
-set_server "ns3" "10.53.0.3"
-# Key properties.
-key_clear "KEY1"
-set_keyrole "KEY1" "ksk"
-set_keylifetime "KEY1" "63072000"
-set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
-set_keysigning "KEY1" "yes"
-set_zonesigning "KEY1" "no"
-
-key_clear "KEY2"
-set_keyrole "KEY2" "zsk"
-set_keylifetime "KEY2" "31536000"
-set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
-set_keysigning "KEY2" "no"
-set_zonesigning "KEY2" "yes"
-
-# Both KSK and ZSK stay OMNIPRESENT.
-set_keystate "KEY1" "GOAL" "omnipresent"
-set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
-set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
-set_keystate "KEY1" "STATE_DS" "omnipresent"
-
-set_keystate "KEY2" "GOAL" "omnipresent"
-set_keystate "KEY2" "STATE_DNSKEY" "omnipresent"
-set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-set_keytimes_autosign_policy
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-# All good, now remove key files and reload keys.
-rm_keyfiles() {
- _basefile=$(key_get "$1" BASEFILE)
- echo_i "remove key files $_basefile"
- _keyfile="${_basefile}.key"
- _privatefile="${_basefile}.private"
- _statefile="${_basefile}.state"
- rm -f $_keyfile
- rm -f $_privatefile
- rm -f $_statefile
-}
-rm_keyfiles "KEY1"
-rm_keyfiles "KEY2"
-
-rndccmd 10.53.0.3 loadkeys "$ZONE" >/dev/null || log_error "rndc loadkeys zone ${ZONE} failed"
-wait_for_log 3 "zone $ZONE/IN (signed): zone_rekey:zone_verifykeys failed: some key files are missing" $DIR/named.run || ret=1
-# Check keys again, make sure no new keys are created.
-set_policy "autosign" "0" "300"
-key_clear "KEY1"
-key_clear "KEY2"
-check_keys
-# Zone is still signed correctly.
-dnssec_verify
-
#
# Test dnssec-policy inheritance.
#
)
-def check_all(server, zone, policy, ksks, zsks, tsig=None):
+def check_all(server, zone, policy, ksks, zsks, zsk_missing=False, tsig=None):
isctest.kasp.check_dnssecstatus(server, zone, ksks + zsks, policy=policy)
- isctest.kasp.check_apex(server, zone, ksks, zsks, tsig=tsig)
+ isctest.kasp.check_apex(
+ server, zone, ksks, zsks, zsk_missing=zsk_missing, tsig=tsig
+ )
isctest.kasp.check_subdomain(server, zone, ksks, zsks, tsig=tsig)
isctest.kasp.check_dnssec_verify(server, zone, tsig=tsig)
f"zsk {lifetime['P1Y']} {alg} {sizes[2]} goal:omnipresent dnskey:rumoured zrrsig:rumoured",
]
+ # Additional test functions.
+ def test_ixfr_is_signed(
+ expected_updates, zone=None, policy=None, ksks=None, zsks=None
+ ):
+ isctest.log.info(f"check that the zone {zone} is correctly signed after ixfr")
+ isctest.log.debug(
+ f"expected updates {expected_updates} policy {policy} ksks {ksks} zsks {zsks}"
+ )
+
+ shutil.copyfile(f"ns2/{zone}.db.in2", f"ns2/{zone}.db")
+ servers["ns2"].rndc(f"reload {zone}", log=False)
+
+ def update_is_signed():
+ parts = update.split()
+ qname = parts[0]
+ qtype = dns.rdatatype.from_text(parts[1])
+ rdata = parts[2]
+ return isctest.kasp.verify_update_is_signed(
+ server, zone, qname, qtype, rdata, ksks, zsks
+ )
+
+ for update in expected_updates:
+ isctest.run.retry_with_timeout(update_is_signed, timeout=5)
+
+ def test_rrsig_refresh(zone=None, policy=None, ksks=None, zsks=None):
+ # pylint: disable=unused-argument
+ isctest.log.info(f"check that the zone {zone} refreshes expired signatures")
+
+ def rrsig_is_refreshed():
+ parts = query.split()
+ qname = parts[0]
+ qtype = dns.rdatatype.from_text(parts[1])
+ return isctest.kasp.check_rrsig_is_refreshed(
+ server, zone, f"ns3/{zone}.db.signed", qname, qtype, ksks, zsks
+ )
+
+ queries = [
+ f"{zone} DNSKEY",
+ f"{zone} SOA",
+ f"{zone} NS",
+ f"{zone} NSEC",
+ f"a.{zone} A",
+ f"a.{zone} NSEC",
+ f"b.{zone} A",
+ f"b.{zone} NSEC",
+ f"c.{zone} A",
+ f"c.{zone} NSEC",
+ f"ns3.{zone} A",
+ f"ns3.{zone} NSEC",
+ ]
+
+ for query in queries:
+ isctest.run.retry_with_timeout(rrsig_is_refreshed, timeout=5)
+
+ def test_rrsig_reuse(zone=None, policy=None, ksks=None, zsks=None):
+ # pylint: disable=unused-argument
+ isctest.log.info(f"check that the zone {zone} reuses fresh signatures")
+
+ def rrsig_is_reused():
+ parts = query.split()
+ qname = parts[0]
+ qtype = dns.rdatatype.from_text(parts[1])
+ return isctest.kasp.check_rrsig_is_reused(
+ server, zone, f"{keydir}/{zone}.db.signed", qname, qtype, ksks, zsks
+ )
+
+ queries = [
+ f"{zone} NS",
+ f"{zone} NSEC",
+ f"a.{zone} A",
+ f"a.{zone} NSEC",
+ f"b.{zone} A",
+ f"b.{zone} NSEC",
+ f"c.{zone} A",
+ f"c.{zone} NSEC",
+ f"ns3.{zone} A",
+ f"ns3.{zone} NSEC",
+ ]
+
+ for query in queries:
+ rrsig_is_reused()
+
+ def test_legacy_keys(zone=None, policy=None, ksks=None, zsks=None):
+ # pylint: disable=unused-argument
+ isctest.log.info(f"check that the zone {zone} uses correct legacy keys")
+
+ assert len(ksks) == 1
+ assert len(zsks) == 1
+
+ # This assumes the zone has a policy that dictates one KSK and one ZSK.
+ # The right keys to be used are stored in "{zone}.ksk" and "{zone}.zsk".
+ with open(f"{keydir}/{zone}.ksk", "r", encoding="utf-8") as file:
+ kskfile = file.read()
+ with open(f"{keydir}/{zone}.zsk", "r", encoding="utf-8") as file:
+ zskfile = file.read()
+
+ assert f"{keydir}/{kskfile}".strip() == ksks[0].path
+ assert f"{keydir}/{zskfile}".strip() == zsks[0].path
+
+ def test_remove_keyfiles(zone=None, policy=None, ksks=None, zsks=None):
+ # pylint: disable=unused-argument
+ isctest.log.info(
+ "check that removing key files does not create new keys to be generated"
+ )
+
+ for k in ksks + zsks:
+ os.remove(k.keyfile)
+ os.remove(k.privatefile)
+ os.remove(k.statefile)
+
+ with server.watch_log_from_here() as watcher:
+ server.rndc(f"loadkeys {zone}", log=False)
+ watcher.wait_for_line(
+ f"zone {zone}/IN (signed): zone_rekey:zone_verifykeys failed: some key files are missing"
+ )
+
+ # Check keys again, make sure no new keys are created.
+ keys = isctest.kasp.keydir_to_keylist(zone, keydir)
+ isctest.kasp.check_keys(zone, keys, [])
+ # Zone is still signed correctly.
+ isctest.kasp.check_dnssec_verify(server, zone)
+
# Test case function.
def test_case():
zone = test["zone"]
pregenerated = False
if test.get("pregenerated"):
pregenerated = test["pregenerated"]
+ zsk_missing = zone == "zsk-missing.autosign"
isctest.log.info(f"check test case zone {zone} policy {policy}")
isctest.kasp.check_keytimes(keys, expected)
- check_all(server, zone, policy, ksks, zsks)
+ check_all(server, zone, policy, ksks, zsks, zsk_missing=zsk_missing)
+
+ if "additional-tests" in test:
+ for additional_test in test["additional-tests"]:
+ callback = additional_test["callback"]
+ arguments = additional_test["arguments"]
+ callback(*arguments, zone=zone, policy=policy, ksks=ksks, zsks=zsks)
# Test cases.
rsa_cases = []
"offset": -timedelta(days=30 * 6),
"key-properties": autosign_properties,
},
+ {
+ "zone": "expired-sigs.autosign",
+ "policy": "autosign",
+ "config": autosign_config,
+ "offset": -timedelta(days=30 * 6),
+ "key-properties": autosign_properties,
+ "additional-tests": [
+ {
+ "callback": test_rrsig_refresh,
+ "arguments": [],
+ },
+ ],
+ },
+ {
+ "zone": "fresh-sigs.autosign",
+ "policy": "autosign",
+ "config": autosign_config,
+ "offset": -timedelta(days=30 * 6),
+ "key-properties": autosign_properties,
+ "additional-tests": [
+ {
+ "callback": test_rrsig_reuse,
+ "arguments": [],
+ },
+ ],
+ },
+ {
+ "zone": "unfresh-sigs.autosign",
+ "policy": "autosign",
+ "config": autosign_config,
+ "offset": -timedelta(days=30 * 6),
+ "key-properties": autosign_properties,
+ "additional-tests": [
+ {
+ "callback": test_rrsig_refresh,
+ "arguments": [],
+ },
+ ],
+ },
+ {
+ "zone": "keyfiles-missing.autosign",
+ "policy": "autosign",
+ "config": autosign_config,
+ "offset": -timedelta(days=30 * 6),
+ "key-properties": autosign_properties,
+ "additional-tests": [
+ {
+ "callback": test_remove_keyfiles,
+ "arguments": [],
+ },
+ ],
+ },
+ {
+ "zone": "ksk-missing.autosign",
+ "policy": "autosign",
+ "config": autosign_config,
+ "offset": -timedelta(days=30 * 6),
+ "key-properties": [
+ f"ksk 63072000 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent missing",
+ f"zsk 31536000 {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent",
+ ],
+ },
+ {
+ "zone": "zsk-missing.autosign",
+ "policy": "autosign",
+ "config": autosign_config,
+ "offset": -timedelta(days=30 * 6),
+ "key-properties": [
+ f"ksk 63072000 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent",
+ f"zsk 31536000 {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent missing",
+ ],
+ },
{
"zone": "dnssec-keygen.kasp",
"policy": "rsasha256",
"config": kasp_config,
"key-properties": fips_properties(8),
},
+ {
+ "zone": "legacy-keys.kasp",
+ "policy": "migrate-to-dnssec-policy",
+ "config": kasp_config,
+ "pregenerated": True,
+ "key-properties": [
+ "ksk 16070400 8 2048 goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden",
+ "zsk 16070400 8 2048 goal:omnipresent dnskey:rumoured zrrsig:rumoured",
+ ],
+ "additional-tests": [
+ {
+ "callback": test_legacy_keys,
+ "arguments": [],
+ },
+ ],
+ },
{
"zone": "pregenerated.kasp",
"policy": "rsasha256",
"config": kasp_config,
"key-properties": fips_properties(10),
},
+ {
+ "zone": "secondary.kasp",
+ "policy": "rsasha256",
+ "config": kasp_config,
+ "key-properties": fips_properties(8),
+ "additional-tests": [
+ {
+ "callback": test_ixfr_is_signed,
+ "arguments": [
+ [
+ "a.secondary.kasp. A 10.0.0.11",
+ "d.secondary.kasp. A 10.0.0.4",
+ ],
+ ],
+ },
+ ],
+ },
{
"zone": "some-keys.kasp",
"policy": "rsasha256",