From 00ced2d2e793d880daa5571c4320718db30dd8c6 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 13 Feb 2020 11:35:38 +0100 Subject: [PATCH 01/11] Prepare kasp for algorithm rollover test Algorithm rollover will require four keys so introduce KEY4. Also it requires to look at key files for multiple algorithms so change getting key ids to be algorithm rollover agnostic (adjusting count checks). The algorithm will be verified in check_key so relaxing 'get_keyids' is fine. Replace '${_alg_num}' with '$(key_get KEY[1-4] ALG_NUM)' in checks to deal with multiple algorithms. --- bin/tests/system/kasp/tests.sh | 157 +++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 54 deletions(-) diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 47d8e05747..6988613d6e 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -94,6 +94,8 @@ key_clear() { } # Start clear. +# There can be at most 4 keys at the same time during a rollover: +# 2x KSK, 2x ZSK key_clear "KEY1" key_clear "KEY2" key_clear "KEY3" @@ -125,18 +127,13 @@ get_keys_which_signed() { awk -v qt="$_qtype" '$4 == "RRSIG" && $5 == qt {print $11}' < "$_output" } -# Get the key ids from key files for zone $2 in directory $1 -# that matches algorithm $3. +# Get the key ids from key files for zone $2 in directory $1. get_keyids() { _dir=$1 _zone=$2 - _algorithm=$(printf "%03d" "$3") - _start="K${_zone}.+${_algorithm}+" - _end=".key" + _regex="K${_zone}.+*+*.key" - if [ "$_algorithm" -ne 0 ]; then - find "${_dir}" -mindepth 1 -maxdepth 1 -name "${_start}*${_end}" | sed "s,$_dir/K${_zone}.+${_algorithm}+\([0-9]\{5\}\)${_end},\1," - fi + find "${_dir}" -mindepth 1 -maxdepth 1 -name "${_regex}" | sed "s,$_dir/K${_zone}.+\([0-9]\{3\}\)+\([0-9]\{5\}\).key,\2," } # By default log errors and don't quit immediately. @@ -289,6 +286,12 @@ check_key() { STATE_FILE="${BASE_FILE}.state" KEY_ID="${_key_id}" + # Check file existence. + [ -s "$KEY_FILE" ] || ret=1 + [ -s "$PRIVATE_FILE" ] || ret=1 + [ -s "$STATE_FILE" ] || ret=1 + [ "$ret" -eq 0 ] || return + test $_log -eq 1 && echo_i "check key $BASE_FILE" # Check the public key file. @@ -415,7 +418,7 @@ key_unused() { _zone=$ZONE _key_idpad=$1 _key_id=$(echo "$_key_idpad" | sed 's/^0\{0,4\}//') - _alg_num=$(key_get KEY1 ALG_NUM) + _alg_num=$2 _alg_numpad=$(printf "%03d" "$_alg_num") BASE_FILE="${_dir}/K${_zone}.+${_alg_numpad}+${_key_idpad}" @@ -426,6 +429,12 @@ key_unused() { test $_log -eq 1 && echo_i "key unused $KEY_ID?" + # Check file existence. + [ -s "$KEY_FILE" ] || ret=1 + [ -s "$PRIVATE_FILE" ] || ret=1 + [ -s "$STATE_FILE" ] || ret=1 + [ "$ret" -eq 0 ] || return + # Check timing metadata. grep "; Publish:" "$KEY_FILE" > /dev/null && log_error "unexpected publish comment in $KEY_FILE" grep "Publish:" "$PRIVATE_FILE" > /dev/null && log_error "unexpected publish in $PRIVATE_FILE" @@ -473,32 +482,29 @@ lines=$(wc -l < "keygen.out.$POLICY.test$n") test "$lines" -eq 4 || log_error "wrong number of keys created for policy kasp: $lines" # Temporarily don't log errors because we are searching multiple files. _log=0 -# Check one algorithm. + key_properties "KEY1" "csk" "31536000" "13" "ECDSAP256SHA256" "256" "yes" "yes" key_timings "KEY1" "none" "none" "none" "none" "none" key_states "KEY1" "none" "none" "none" "none" "none" -ids=$(get_keyids "$DIR" "$ZONE" "$(key_get KEY1 ALG_NUM)") -for id in $ids; do - check_key "KEY1" "$id" -done -test "$ret" -eq 0 || echo_i "failed" -status=$((status+ret)) -# Check the other algorithm. -key_properties "KEY1" "ksk" "31536000" "8" "RSASHA256" "2048" "no" "yes" -key_timings "KEY1" "none" "none" "none" "none" "none" -key_states "KEY1" "none" "none" "none" "none" "none" -key_properties "KEY2" "zsk" "2592000" "8" "RSASHA256" "1024" "yes" "no" +key_properties "KEY2" "ksk" "31536000" "8" "RSASHA256" "2048" "no" "yes" key_timings "KEY2" "none" "none" "none" "none" "none" key_states "KEY2" "none" "none" "none" "none" "none" -key_properties "KEY3" "zsk" "16070400" "8" "RSASHA256" "2000" "yes" "no" +key_properties "KEY3" "zsk" "2592000" "8" "RSASHA256" "1024" "yes" "no" key_timings "KEY3" "none" "none" "none" "none" "none" key_states "KEY3" "none" "none" "none" "none" "none" -ids=$(get_keyids "$DIR" "$ZONE" "$(key_get KEY1 ALG_NUM)") +key_properties "KEY4" "zsk" "16070400" "8" "RSASHA256" "2000" "yes" "no" +key_timings "KEY4" "none" "none" "none" "none" "none" +key_states "KEY4" "none" "none" "none" "none" "none" + +lines=$(get_keyids "$DIR" "$ZONE" | wc -l) +test "$lines" -eq 4 || log_error "bad number of key ids" + +ids=$(get_keyids "$DIR" "$ZONE") for id in $ids; do - # There are three key files with the same algorithm. + # There are four key files with the same algorithm. # Check them until a match is found. ret=0 && check_key "KEY1" "$id" test "$ret" -eq 0 && continue @@ -507,6 +513,9 @@ for id in $ids; do test "$ret" -eq 0 && continue ret=0 && check_key "KEY3" "$id" + test "$ret" -eq 0 && continue + + ret=0 && check_key "KEY4" "$id" # If ret is still non-zero, non of the files matched. test "$ret" -eq 0 || echo_i "failed" @@ -525,7 +534,7 @@ key_states "KEY1" "none" "none" "none" "none" "none" $KEYGEN -k "$POLICY" "$ZONE" > "keygen.out.$POLICY.test$n" 2>/dev/null || ret=1 lines=$(wc -l < "keygen.out.default.test$n") test "$lines" -eq 1 || log_error "wrong number of keys created for policy default: $lines" -ids=$(get_keyids "$DIR" "$ZONE" "$(key_get KEY1 ALG_NUM)") +ids=$(get_keyids "$DIR" "$ZONE") for id in $ids; do check_key "KEY1" "$id" done @@ -542,7 +551,7 @@ key_states "KEY1" "none" "none" "none" "none" "none" $KEYGEN -k "$POLICY" "$ZONE" > "keygen.out.$POLICY.test$n" 2>/dev/null || ret=1 lines=$(wc -l < "keygen.out.$POLICY.test$n") test "$lines" -eq 1 || log_error "wrong number of keys created for policy default: $lines" -ids=$(get_keyids "$DIR" "$ZONE" "$(key_get KEY1 ALG_NUM)") +ids=$(get_keyids "$DIR" "$ZONE") for id in $ids; do check_key "KEY1" "$id" done @@ -624,6 +633,14 @@ do grep "NS SOA" "dig.out.ns3.test$n.$zone" > /dev/null || ret=1 grep "$zone\..*IN.*RRSIG" "dig.out.ns3.test$n.$zone" > /dev/null || ret=1 done < ns3/zones + + while read -r zone + do + dig_with_opts "$zone" @10.53.0.6 nsec > "dig.out.ns6.test$n.$zone" || ret=1 + grep "NS SOA" "dig.out.ns6.test$n.$zone" > /dev/null || ret=1 + grep "$zone\..*IN.*RRSIG" "dig.out.ns6.test$n.$zone" > /dev/null || ret=1 + done < ns6/zones + i=$((i+1)) if [ $ret = 0 ]; then break; fi echo_i "waiting ... ($i)" @@ -647,7 +664,7 @@ key_states "KEY1" "omnipresent" "rumoured" "rumoured" "rumoured" "hidden" n=$((n+1)) echo_i "check key is created for zone ${ZONE} ($n)" ret=0 -ids=$(get_keyids "$DIR" "$ZONE" "$(key_get KEY1 ALG_NUM)") +ids=$(get_keyids "$DIR" "$ZONE") for id in $ids; do check_key "KEY1" "$id" done @@ -664,7 +681,7 @@ echo_i "check ${qtype} rrset is signed 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}\..*${DNSKEY_TTL}.*IN.*${qtype}.*257.*.3.*${KEY1__ALG_NUM}" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${qtype} record in response" +grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${qtype}.*257.*.3.*$(key_get KEY1 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${qtype} record in response" lines=$(get_keys_which_signed $qtype "dig.out.$DIR.test$n" | wc -l) test "$lines" -eq 1 || log_error "bad number ($lines) of RRSIG records in DNS response" get_keys_which_signed $qtype "dig.out.$DIR.test$n" | grep "^${KEY_ID}$" > /dev/null || log_error "${qtype} RRset not signed with key ${KEY_ID}" @@ -737,29 +754,24 @@ key_timings "KEY3" "published" "active" "retired" "none" "none" key_states "KEY1" "omnipresent" "rumoured" "none" "rumoured" "hidden" key_states "KEY2" "omnipresent" "rumoured" "rumoured" "none" "none" key_states "KEY3" "omnipresent" "rumoured" "rumoured" "none" "none" +key_clear "KEY4" # Check keys for a configured zone. This verifies: # 1. The right number of keys exist in the key pool ($1). -# 2. The right number of keys is active (always expect three keys). -# The algorithm expected is set with $2 (string) and $3 (number), and the -# expected sizes for the keys are set with $4 (ksk), $5 and $6 (zsk). -# A size set to 0 means the corresponding key (KEY1, KEY2 or KEY3) is not -# expected. +# 2. The right number of keys is active. Checks KEY1, KEY2, KEY3, and KEY4. # -# It is expected that KEY1, KEY2 and KEY3 arrays are set correctly. Found key -# identifiers are stored in the right key array. +# It is expected that KEY1, KEY2, KEY3, and KEY4 arrays are set correctly. +# Found key identifiers are stored in the right key array. check_keys() { n=$((n+1)) echo_i "check keys are created for zone ${ZONE} ($n)" ret=0 - _key_algnum=$(key_get KEY1 ALG_NUM) - n=$((n+1)) - echo_i "check number of keys with algorithm ${_key_algnum} for zone ${ZONE} in dir ${DIR} ($n)" + echo_i "check number of keys for zone ${ZONE} in dir ${DIR} ($n)" ret=0 - _numkeys=$(get_keyids "$DIR" "$ZONE" "$_key_algnum" | wc -l) + _numkeys=$(get_keyids "$DIR" "$ZONE" | wc -l) test "$_numkeys" -eq "$NUM_KEYS" || log_error "bad number ($_numkeys) of key files for zone $ZONE (expected $NUM_KEYS)" test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) @@ -771,9 +783,10 @@ check_keys() key_set KEY1 ID "no" key_set KEY2 ID "no" key_set KEY3 ID "no" + key_set KEY4 ID "no" # Check key files. - _ids=$(get_keyids "$DIR" "$ZONE" "$_key_algnum") + _ids=$(get_keyids "$DIR" "$ZONE") for _id in $_ids; do # There are three key files with the same algorithm. # Check them until a match is found. @@ -794,9 +807,14 @@ check_keys() check_key "KEY3" "$_id" test "$ret" -eq 0 && key_set KEY3 ID "$KEY_ID" && continue fi + if [ "no" = "$(key_get KEY4 ID)" ] && [ "$(key_get KEY4 EXPECT)" = "yes" ]; then + ret=0 + check_key "KEY4" "$_id" + test "$ret" -eq 0 && key_set KEY4 ID "$KEY_ID" && continue + fi - # This may be an unused key. - ret=0 && key_unused "$_id" + # This may be an unused key. Assume algorithm of KEY1. + ret=0 && key_unused "$_id" "$(key_get KEY1 ALG_NUM)" test "$ret" -eq 0 && continue # If ret is still non-zero, non of the files matched. @@ -817,6 +835,9 @@ check_keys() if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then test "no" = "$(key_get KEY3 ID)" && log_error "No KEY3 found for zone ${ZONE}" fi + if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then + test "no" = "$(key_get KEY4 ID)" && log_error "No KEY4 found for zone ${ZONE}" + fi test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) } @@ -851,6 +872,12 @@ check_signatures() { elif [ "$(key_get KEY3 EXPECT)" = "yes" ]; then get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY3 ID)$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with key $(key_get KEY3 ID)" fi + + if [ "$(key_get KEY4 "$_expect_type")" = "yes" ] && [ "$(key_get KEY4 "$_role")" = "yes" ]; then + get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY4 ID)$" > /dev/null || log_error "${_qtype} RRset not signed with key $(key_get KEY4 ID)" + elif [ "$(key_get KEY4 EXPECT)" = "yes" ]; then + get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY4 ID)$" > /dev/null && log_error "${_qtype} RRset signed unexpectedly with key $(key_get KEY4 ID)" + fi } response_has_cds_for_key() ( @@ -858,7 +885,7 @@ response_has_cds_for_key() ( -v ttl="${DNSKEY_TTL}" \ -v qtype="CDS" \ -v keyid="$(key_get "${1}" ID)" \ - -v keyalg="${_key_algnum}" \ + -v keyalg="$(key_get "${1}" ALG_NUM)" \ -v hashalg="2" \ 'BEGIN { ret=1; } $1 == zone && $2 == ttl && $4 == qtype && $5 == keyid && $6 == keyalg && $7 == hashalg { ret=0; exit; } @@ -871,7 +898,7 @@ response_has_cdnskey_for_key() ( -v ttl="${DNSKEY_TTL}" \ -v qtype="CDNSKEY" \ -v flags="257" \ - -v keyalg="${_key_algnum}" \ + -v keyalg="$(key_get "${1}" ALG_NUM)" \ 'BEGIN { ret=1; } $1 == zone && $2 == ttl && $4 == qtype && $5 == flags && $7 == keyalg { ret=0; exit; } END { exit ret; }' \ @@ -881,8 +908,6 @@ response_has_cdnskey_for_key() ( # Test CDS and CDNSKEY publication. check_cds() { - _key_algnum="$(key_get KEY1 ALG_NUM)" - n=$((n+1)) echo_i "check CDS and CDNSKEY rrset are signed correctly for zone ${ZONE} ($n)" ret=0 @@ -932,6 +957,19 @@ check_cds() { # so let's skip this check for now. fi + if [ "$(key_get KEY4 STATE_DS)" = "rumoured" ] || [ "$(key_get KEY4 STATE_DS)" = "omnipresent" ]; then + response_has_cds_for_key KEY4 "dig.out.$DIR.test$n.cds" || log_error "missing CDS record in response for key $(key_get KEY4 ID)" + check_signatures "CDS" "dig.out.$DIR.test$n.cds" "KSK" + response_has_cdnskey_for_key KEY4 "dig.out.$DIR.test$n.cdnskey" || log_error "missing CDNSKEY record in response for key $(key_get KEY4 ID)" + check_signatures "CDNSKEY" "dig.out.$DIR.test$n.cdnskey" "KSK" + elif [ "$(key_get KEY4 EXPECT)" = "yes" ]; then + response_has_cds_for_key KEY4 "dig.out.$DIR.test$n.cds" && log_error "unexpected CDS record in response for key $(key_get KEY4 ID)" + # KEY4 should not have an associated CDNSKEY, but there may be + # one for another key. Since the CDNSKEY has no field for key + # id, it is hard to check what key the CDNSKEY may belong to + # so let's skip this check for now. + fi + test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) } @@ -939,38 +977,45 @@ check_cds() { # Test the apex of a configured zone. This checks that the SOA and DNSKEY # RRsets are signed correctly and with the appropriate keys. check_apex() { - # Test DNSKEY query. _qtype="DNSKEY" - _key_algnum="$(key_get KEY1 ALG_NUM)" n=$((n+1)) echo_i "check ${_qtype} rrset is signed 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" + if [ "$(key_get KEY1 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY1 STATE_DNSKEY)" = "omnipresent" ]; then - grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*${_key_algnum}" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${_qtype} record in response for key $(key_get KEY1 ID)" + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY1 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${_qtype} record in response for key $(key_get KEY1 ID)" check_signatures $_qtype "dig.out.$DIR.test$n" "KSK" numkeys=$((numkeys+1)) elif [ "$(key_get KEY1 EXPECT)" = "yes" ]; then - grep "${ZONE}\.*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*${_key_algnum}" "dig.out.$DIR.test$n" > /dev/null && log_error "unexpected ${_qtype} record in response for key $(key_get KEY1 ID)" + grep "${ZONE}\.*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY1 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null && log_error "unexpected ${_qtype} record in response for key $(key_get KEY1 ID)" fi if [ "$(key_get KEY2 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY2 STATE_DNSKEY)" = "omnipresent" ]; then - grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*${_key_algnum}" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${_qtype} record in response for key $(key_get KEY2 ID)" + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY2 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${_qtype} record in response for key $(key_get KEY2 ID)" check_signatures $_qtype "dig.out.$DIR.test$n" "KSK" numkeys=$((numkeys+1)) elif [ "$(key_get KEY2 EXPECT)" = "yes" ]; then - grep "${ZONE}\.*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*${_key_algnum}" "dig.out.$DIR.test$n" > /dev/null && log_error "unexpected ${_qtype} record in response for key $(key_get KEY2 ID)" + grep "${ZONE}\.*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY2 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null && log_error "unexpected ${_qtype} record in response for key $(key_get KEY2 ID)" fi if [ "$(key_get KEY3 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY3 STATE_DNSKEY)" = "omnipresent" ]; then - grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*${_key_algnum}" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${_qtype} record in response for key $(key_get KEY3 ID)" + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY3 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${_qtype} record in response for key $(key_get KEY3 ID)" check_signatures $_qtype "dig.out.$DIR.test$n" "KSK" numkeys=$((numkeys+1)) elif [ "$(key_get KEY3 EXPECT)" = "yes" ]; then - grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*${_key_algnum}" "dig.out.$DIR.test$n" > /dev/null && log_error "unexpected ${_qtype} record in response for key $(key_get KEY3 ID)" + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY3 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null && log_error "unexpected ${_qtype} record in response for key $(key_get KEY3 ID)" + fi + + if [ "$(key_get KEY4 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY4 STATE_DNSKEY)" = "omnipresent" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY4 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null || log_error "missing ${_qtype} record in response for key $(key_get KEY4 ID)" + check_signatures $_qtype "dig.out.$DIR.test$n" "KSK" + numkeys=$((numkeys+1)) + elif [ "$(key_get KEY4 EXPECT)" = "yes" ]; then + grep "${ZONE}\..*${DNSKEY_TTL}.*IN.*${_qtype}.*257.*.3.*$(key_get KEY4 ALG_NUM)" "dig.out.$DIR.test$n" > /dev/null && log_error "unexpected ${_qtype} record in response for key $(key_get KEY4 ID)" fi lines=$(get_keys_which_signed $_qtype "dig.out.$DIR.test$n" | wc -l) @@ -1022,6 +1067,7 @@ zone_properties "ns3" "unsigned.kasp" "none" "0" "0" "10.53.0.3" key_clear "KEY1" key_clear "KEY2" key_clear "KEY3" +key_clear "KEY4" check_keys check_apex check_subdomain @@ -1033,6 +1079,7 @@ zone_properties "ns3" "unlimited.kasp" "unlimited" "1234" "1" "10.53.0.3" key_properties "KEY1" "csk" "0" "13" "ECDSAP256SHA256" "256" "yes" "yes" key_clear "KEY2" key_clear "KEY3" +key_clear "KEY4" # The first key is immediately published and activated. key_timings "KEY1" "published" "active" "none" "none" "none" # DNSKEY, RRSIG (ksk), RRSIG (zsk) are published. DS needs to wait. @@ -1059,6 +1106,7 @@ key_timings "KEY3" "published" "active" "retired" "none" "none" key_states "KEY1" "omnipresent" "rumoured" "none" "rumoured" "hidden" key_states "KEY2" "omnipresent" "rumoured" "rumoured" "none" "none" key_states "KEY3" "omnipresent" "rumoured" "rumoured" "none" "none" +key_clear "KEY4" check_keys check_apex check_subdomain @@ -1243,6 +1291,7 @@ key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "none" "none" key_timings "KEY2" "published" "active" "retired" "none" "none" # Expect only two keys. key_clear "KEY3" +key_clear "KEY4" check_keys check_apex From fdb3f6f40019e0010cb320a8425399d3026c29a2 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 18 Feb 2020 16:33:41 +0100 Subject: [PATCH 02/11] Introduce enable dnssec test case --- bin/tests/system/kasp/ns3/named.conf.in | 24 +++++ .../system/kasp/ns3/policies/autosign.conf | 21 +++++ bin/tests/system/kasp/ns3/setup.sh | 47 ++++++++++ bin/tests/system/kasp/tests.sh | 93 ++++++++++++++++--- 4 files changed, 174 insertions(+), 11 deletions(-) diff --git a/bin/tests/system/kasp/ns3/named.conf.in b/bin/tests/system/kasp/ns3/named.conf.in index 38a656b0d3..84451742bf 100644 --- a/bin/tests/system/kasp/ns3/named.conf.in +++ b/bin/tests/system/kasp/ns3/named.conf.in @@ -202,6 +202,30 @@ zone "zsk-retired.autosign" { dnssec-policy "autosign"; }; +/* + * Zones for testing enabling DNSSEC. + */ +zone "step1.enable-dnssec.autosign" { + type master; + file "step1.enable-dnssec.autosign.db"; + dnssec-policy "enable-dnssec"; +}; +zone "step2.enable-dnssec.autosign" { + type master; + file "step2.enable-dnssec.autosign.db"; + dnssec-policy "enable-dnssec"; +}; +zone "step3.enable-dnssec.autosign" { + type master; + file "step3.enable-dnssec.autosign.db"; + dnssec-policy "enable-dnssec"; +}; +zone "step4.enable-dnssec.autosign" { + type master; + file "step4.enable-dnssec.autosign.db"; + dnssec-policy "enable-dnssec"; +}; + /* * Zones for testing ZSK Pre-Publication steps. */ diff --git a/bin/tests/system/kasp/ns3/policies/autosign.conf b/bin/tests/system/kasp/ns3/policies/autosign.conf index 751783ee0e..bafbe859ef 100644 --- a/bin/tests/system/kasp/ns3/policies/autosign.conf +++ b/bin/tests/system/kasp/ns3/policies/autosign.conf @@ -23,6 +23,27 @@ dnssec-policy "autosign" { }; }; +dnssec-policy "enable-dnssec" { + + 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-registration-delay P1D; + parent-ds-ttl 2h; + + keys { + csk lifetime unlimited algorithm 13; + }; +}; + dnssec-policy "zsk-prepub" { signatures-refresh P1W; diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index e1f065dce2..5be0f0b0c0 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -149,6 +149,53 @@ private_type_record $zone 13 "$ZSK" >> "$infile" $SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 $SETTIME -s -I now -g HIDDEN "$ZSK" > settime.out.$zone.3 2>&1 +# +# The zones at enable-dnssec.autosign represent the various steps of the +# initial signing of a zone. +# + +# Step 1: +# This is an unsigned zone and named should perform the initial steps of +# introducing the DNSSEC records in the right order. +setup step1.enable-dnssec.autosign +cp template.db.in $zonefile + +# Step 2: +# The DNSKEY has been published long enough to become OMNIPRESENT. +setup step2.enable-dnssec.autosign +CSK=$($KEYGEN -k enable-dnssec -l policies/autosign.conf $zone 2> keygen.out.$zone.1) +TpubN="now-900s" +$SETTIME -s -P $TpubN -A $TpubN -g $O -k $R $TpubN -r $R $TpubN -d $H $TpubN -z $R $TpubN "$CSK" > settime.out.$zone.1 2>&1 +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone 13 "$CSK" >> "$infile" +$SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 3: +# The zone signatures have been published long enough to become OMNIPRESENT. +setup step3.enable-dnssec.autosign +CSK=$($KEYGEN -k enable-dnssec -l policies/autosign.conf $zone 2> keygen.out.$zone.1) +TpubN="now-44700s" +TactN="now-43800s" +$SETTIME -s -P $TpubN -A $TpubN -g $O -k $O $TactN -r $O $TactN -d $H $TpubN -z $R $TpubN "$CSK" > settime.out.$zone.1 2>&1 +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone 13 "$CSK" >> "$infile" +$SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 +setup step3.enable-dnssec.autosign + +# Step 4: +# The DS has been submitted long enough ago to become OMNIPRESENT. +# Add 27 hour plus retire safety of 20 minutes (98400 seconds) to the times. +setup step4.enable-dnssec.autosign +CSK=$($KEYGEN -k enable-dnssec -l policies/autosign.conf $zone 2> keygen.out.$zone.1) +TpubN="now-143100s" +TactN="now-142200s" +TomnN="now-98400s" +$SETTIME -s -P $TpubN -A $TpubN -g $O -k $O $TactN -r $O $TactN -d $R $TomnN -z $O $TomnN "$CSK" > settime.out.$zone.1 2>&1 +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone 13 "$CSK" >> "$infile" +$SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 +setup step3.enable-dnssec.autosign + # # The zones at zsk-prepub.autosign represent the various steps of a ZSK # Pre-Publication rollover. diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index 6988613d6e..e6c3028904 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -1603,21 +1603,18 @@ dnssec_verify TSIG="" # -# Testing ZSK Pre-Publication rollover. +# Testing DNSSEC introduction. # # -# Zone: step1.zsk-prepub.autosign. +# Zone: step1.enable-dnssec.autosign. # -zone_properties "ns3" "step1.zsk-prepub.autosign" "zsk-prepub" "3600" "2" "10.53.0.3" -# Both KSK (KEY1) and ZSK (KEY2) start in OMNIPRESENT. -key_properties "KEY1" "ksk" "63072000" "13" "ECDSAP256SHA256" "256" "no" "yes" -key_timings "KEY1" "published" "active" "retired" "none" "none" -key_states "KEY1" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent" -key_properties "KEY2" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "yes" "no" -key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "none" "none" -key_timings "KEY2" "published" "active" "retired" "none" "none" -# Initially only two keys. +zone_properties "ns3" "step1.enable-dnssec.autosign" "enable-dnssec" "300" "1" "10.53.0.3" +# The DNSKEY and signatures are introduced first, the DS remains hidden. +key_properties "KEY1" "csk" "0" "13" "ECDSAP256SHA256" "256" "yes" "yes" +key_timings "KEY1" "published" "active" "none" "none" "none" +key_states "KEY1" "omnipresent" "rumoured" "rumoured" "rumoured" "hidden" +key_clear "KEY2" key_clear "KEY3" check_keys check_apex @@ -1646,11 +1643,85 @@ check_next_key_event() { status=$((status+ret)) } +# Next key event is when the DNSKEY RRset becomes OMNIPRESENT: DNSKEY TTL plus +# publish safety plus the zone propagation delay: 900 seconds. +check_next_key_event 900 + +# +# Zone: step2.enable-dnssec.autosign. +# +zone_properties "ns3" "step2.enable-dnssec.autosign" "enable-dnssec" "300" "1" "10.53.0.3" +# The DNSKEY and signatures are introduced first, the DS remains hidden. +key_states "KEY1" "omnipresent" "omnipresent" "rumoured" "omnipresent" "hidden" +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the zone signatures become OMNIPRESENT: max-zone-ttl +# plus zone propagation delay plus retire safety minus the already elapsed +# 900 seconds: 12h + 300s + 20m - 900 = 44700 - 900 = 43800 seconds +check_next_key_event 43800 + +# +# Zone: step3.enable-dnssec.autosign. +# +zone_properties "ns3" "step3.enable-dnssec.autosign" "enable-dnssec" "300" "1" "10.53.0.3" +# The DS can be introduced. +key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "rumoured" +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the DS can move to the OMNIPRESENT state. This occurs +# when the parent registration and propagation delay have passed, plus the +# DS TTL and retire safety delay: 1d + 1h + 2h + 20m = 27h20m = 98400 seconds +check_next_key_event 98400 + +# +# Zone: step4.enable-dnssec.autosign. +# +zone_properties "ns3" "step4.enable-dnssec.autosign" "enable-dnssec" "300" "1" "10.53.0.3" +# The DS is omnipresent. +key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent" +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is never, the zone dnssec-policy has been established. So we +# fall back to the default loadkeys interval. +check_next_key_event 3600 + +# +# Testing ZSK Pre-Publication rollover. +# + +# +# Zone: step1.zsk-prepub.autosign. +# +zone_properties "ns3" "step1.zsk-prepub.autosign" "zsk-prepub" "3600" "2" "10.53.0.3" +# Both KSK (KEY1) and ZSK (KEY2) start in OMNIPRESENT. +key_properties "KEY1" "ksk" "63072000" "13" "ECDSAP256SHA256" "256" "no" "yes" +key_timings "KEY1" "published" "active" "retired" "none" "none" +key_states "KEY1" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent" +key_properties "KEY2" "zsk" "2592000" "13" "ECDSAP256SHA256" "256" "yes" "no" +key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "none" "none" +key_timings "KEY2" "published" "active" "retired" "none" "none" +# Initially only two keys. +key_clear "KEY3" +check_keys +check_apex +check_subdomain +dnssec_verify + # Next key event is when the successor ZSK needs to be published. That is # the ZSK lifetime - prepublication time. The prepublication time is DNSKEY # TTL plus publish safety plus the zone propagation delay. For the # zsk-prepub policy that means: 30d - 3600s + 1d + 1h = 2498400 seconds. check_next_key_event 2498400 + # # Zone: step2.zsk-prepub.autosign. # From cc2afe853b34a6ea452f9b9a9d456a84eedddc51 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 18 Feb 2020 16:34:20 +0100 Subject: [PATCH 03/11] Remove unneeded step6 zone The zone 'step6.ksk-doubleksk.autosign' is configured but is not set up nor tested. Remove the unneeded configured zone. --- bin/tests/system/kasp/ns3/named.conf.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bin/tests/system/kasp/ns3/named.conf.in b/bin/tests/system/kasp/ns3/named.conf.in index 84451742bf..da7c04734e 100644 --- a/bin/tests/system/kasp/ns3/named.conf.in +++ b/bin/tests/system/kasp/ns3/named.conf.in @@ -283,11 +283,6 @@ zone "step5.ksk-doubleksk.autosign" { file "step5.ksk-doubleksk.autosign.db"; dnssec-policy "ksk-doubleksk"; }; -zone "step6.ksk-doubleksk.autosign" { - type master; - file "step6.ksk-doubleksk.autosign.db"; - dnssec-policy "ksk-doubleksk"; -}; /* * Zones for testing CSK rollover steps. From 88ebe9581b3d061b8fd3006a91976021ffb26fd5 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 18 Feb 2020 16:36:31 +0100 Subject: [PATCH 04/11] Add algorithm rollover test case Add a test case for algorithm rollover. This is triggered by changing the dnssec-policy. A new nameserver ns6 is introduced for tests related to dnssec-policy changes. This requires a slight change in check_next_key_event to only check the last occurrence. Also, change the debug log message in lib/dns/zone.c to deal with checks when no next scheduled key event exists (and default to loadkeys interval 3600). --- bin/tests/system/kasp/README | 2 + bin/tests/system/kasp/clean.sh | 3 +- bin/tests/system/kasp/ns6/named.conf.in | 41 ++++ bin/tests/system/kasp/ns6/named2.conf.in | 71 +++++++ bin/tests/system/kasp/ns6/policies/kasp.conf | 50 +++++ bin/tests/system/kasp/ns6/setup.sh | 185 +++++++++++++++++++ bin/tests/system/kasp/ns6/template.db.in | 25 +++ bin/tests/system/kasp/setup.sh | 5 + bin/tests/system/kasp/tests.sh | 163 +++++++++++++++- lib/dns/zone.c | 19 +- util/copyrights | 1 + 11 files changed, 557 insertions(+), 8 deletions(-) create mode 100644 bin/tests/system/kasp/ns6/named.conf.in create mode 100644 bin/tests/system/kasp/ns6/named2.conf.in create mode 100644 bin/tests/system/kasp/ns6/policies/kasp.conf create mode 100644 bin/tests/system/kasp/ns6/setup.sh create mode 100644 bin/tests/system/kasp/ns6/template.db.in diff --git a/bin/tests/system/kasp/README b/bin/tests/system/kasp/README index ceafd19772..61c0a2d095 100644 --- a/bin/tests/system/kasp/README +++ b/bin/tests/system/kasp/README @@ -11,3 +11,5 @@ ns2 is running primary service for ns3. ns3 is an authoritative server for the various test domains. ns4 and ns5 are authoritative servers for various test domains related to views. + +ns6 is an authoritative server that tests changes in dnssec-policy. diff --git a/bin/tests/system/kasp/clean.sh b/bin/tests/system/kasp/clean.sh index 803dd703cd..98239093c5 100644 --- a/bin/tests/system/kasp/clean.sh +++ b/bin/tests/system/kasp/clean.sh @@ -22,5 +22,4 @@ rm -f ns*/dsset-* ns*/*.db ns*/*.db.signed rm -f ns*/keygen.out.* ns*/settime.out.* ns*/signer.out.* rm -f ns*/managed-keys.bind rm -f ns*/*.mkeys -# NS3 specific -rm -f ns3/zones ns3/*.db.infile +rm -f ns*/zones ns*/*.db.infile diff --git a/bin/tests/system/kasp/ns6/named.conf.in b/bin/tests/system/kasp/ns6/named.conf.in new file mode 100644 index 0000000000..5bb8185e47 --- /dev/null +++ b/bin/tests/system/kasp/ns6/named.conf.in @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS6 + +include "policies/kasp.conf"; + +options { + query-source address 10.53.0.6; + notify-source 10.53.0.6; + transfer-source 10.53.0.6; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.6; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "step1.algorithm-roll.kasp" { + type master; + file "step1.algorithm-roll.kasp.db"; + dnssec-policy "rsasha1"; +}; diff --git a/bin/tests/system/kasp/ns6/named2.conf.in b/bin/tests/system/kasp/ns6/named2.conf.in new file mode 100644 index 0000000000..b2810e1bb9 --- /dev/null +++ b/bin/tests/system/kasp/ns6/named2.conf.in @@ -0,0 +1,71 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS6 + +include "policies/kasp.conf"; + +options { + query-source address 10.53.0.6; + notify-source 10.53.0.6; + transfer-source 10.53.0.6; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.6; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "step1.algorithm-roll.kasp" { + type master; + file "step1.algorithm-roll.kasp.db"; + dnssec-policy "ecdsa256"; +}; + +zone "step2.algorithm-roll.kasp" { + type master; + file "step2.algorithm-roll.kasp.db"; + dnssec-policy "ecdsa256"; +}; + +zone "step3.algorithm-roll.kasp" { + type master; + file "step3.algorithm-roll.kasp.db"; + dnssec-policy "ecdsa256"; +}; + +zone "step4.algorithm-roll.kasp" { + type master; + file "step4.algorithm-roll.kasp.db"; + dnssec-policy "ecdsa256"; +}; + +zone "step5.algorithm-roll.kasp" { + type master; + file "step5.algorithm-roll.kasp.db"; + dnssec-policy "ecdsa256"; +}; + +zone "step6.algorithm-roll.kasp" { + type master; + file "step6.algorithm-roll.kasp.db"; + dnssec-policy "ecdsa256"; +}; diff --git a/bin/tests/system/kasp/ns6/policies/kasp.conf b/bin/tests/system/kasp/ns6/policies/kasp.conf new file mode 100644 index 0000000000..ad7028ed0c --- /dev/null +++ b/bin/tests/system/kasp/ns6/policies/kasp.conf @@ -0,0 +1,50 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-policy "rsasha1" { + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + keys { + ksk lifetime unlimited algorithm rsasha1; + zsk lifetime unlimited algorithm rsasha1; + }; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + zone-propagation-delay 3600; + max-zone-ttl 6h; + parent-registration-delay 1d; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; + +dnssec-policy "ecdsa256" { + 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-registration-delay 1d; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; diff --git a/bin/tests/system/kasp/ns6/setup.sh b/bin/tests/system/kasp/ns6/setup.sh new file mode 100644 index 0000000000..2616d8e952 --- /dev/null +++ b/bin/tests/system/kasp/ns6/setup.sh @@ -0,0 +1,185 @@ +#!/bin/sh -e +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. "$SYSTEMTESTTOP/conf.sh" + +echo_i "ns6/setup.sh" + +setup() { + zone="$1" + echo_i "setting up zone: $zone" + zonefile="${zone}.db" + infile="${zone}.db.infile" +} + +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + + +# Make lines shorter by storing key states in environment variables. +H="HIDDEN" +R="RUMOURED" +O="OMNIPRESENT" +U="UNRETENTIVE" + +# +# The zones at algorithm-roll.kasp represent the various steps of a ZSK/KSK +# algorithm rollover. +# + +# Step 1: +# Introduce the first key. This will immediately be active. +setup step1.algorithm-roll.kasp +echo "$zone" >> zones +KSK=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) +TactN="now" +$SETTIME -s -P $TactN -A $TactN -g $O -k $O $TactN -r $O $TactN -d $O $TactN "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TactN -A $TactN -g $O -k $O $TactN -z $O $TactN "$ZSK" > settime.out.$zone.2 2>&1 +cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile" +private_type_record $zone 5 "$KSK" >> "$infile" +private_type_record $zone 5 "$ZSK" >> "$infile" +$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 2: +# After the publication interval has passed the DNSKEY is OMNIPRESENT. +setup step2.algorithm-roll.kasp +KSK1=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK1=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) +KSK2=$($KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK2=$($KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2) +# The time passed since the new algorithm keys have been introduced is 3 hours. +TactN="now-3h" +TpubN1="now-3h" +TactN1="now+6h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -z $O $TactN "$ZSK1" > settime.out.$zone.2 2>&1 +$SETTIME -s -P $TpubN1 -A $TpubN1 -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $R $TpubN1 -z $R $TpubN1 "$ZSK2" > settime.out.$zone.2 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${KSK1}.state" +echo "Lifetime: 0" >> "${ZSK1}.state" +cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" > "$infile" +private_type_record $zone 5 "$KSK1" >> "$infile" +private_type_record $zone 5 "$ZSK1" >> "$infile" +private_type_record $zone 13 "$KSK2" >> "$infile" +private_type_record $zone 13 "$ZSK2" >> "$infile" +$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 3: +# The zone signatures are also OMNIPRESENT. +setup step3.algorithm-roll.kasp +KSK1=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK1=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) +KSK2=$($KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK2=$($KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2) +# The time passed since the new algorithm keys have been introduced is 9 hours. +TactN="now-9h" +TpubN1="now-9h" +TactN1="now" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -z $O $TactN "$ZSK1" > settime.out.$zone.2 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -r $O $TpubN1 -d $H $TpubN1 "$KSK2" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" > settime.out.$zone.2 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${KSK1}.state" +echo "Lifetime: 0" >> "${ZSK1}.state" +cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" > "$infile" +private_type_record $zone 5 "$KSK1" >> "$infile" +private_type_record $zone 5 "$ZSK1" >> "$infile" +private_type_record $zone 13 "$KSK2" >> "$infile" +private_type_record $zone 13 "$ZSK2" >> "$infile" +$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 4: +# The DS is swapped and can become OMNIPRESENT. +setup step4.algorithm-roll.kasp +KSK1=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK1=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) +KSK2=$($KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK2=$($KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2) +# The time passed since the DS has been swapped is 29 hours. +TactN="now-38h" +TpubN1="now-38h" +TactN1="now-29h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -r $O $TactN -d $U $TactN1 "$KSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -z $O $TactN "$ZSK1" > settime.out.$zone.2 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -r $O $TpubN1 -d $R $TactN1 "$KSK2" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" > settime.out.$zone.2 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${KSK1}.state" +echo "Lifetime: 0" >> "${ZSK1}.state" +cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" > "$infile" +private_type_record $zone 5 "$KSK1" >> "$infile" +private_type_record $zone 5 "$ZSK1" >> "$infile" +private_type_record $zone 13 "$KSK2" >> "$infile" +private_type_record $zone 13 "$ZSK2" >> "$infile" +$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 5: +# The DNSKEY is removed long enough to be HIDDEN. +setup step5.algorithm-roll.kasp +KSK1=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK1=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) +KSK2=$($KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK2=$($KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2) +# The time passed since the DNSKEY has been removed is 2 hours. +TactN="now-40h" +TpubN1="now-40h" +TactN1="now-31h" +TremN="now-2h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $U $TremN -r $U $TremN -d $H $TremN "$KSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $U $TremN -z $U $TremN "$ZSK1" > settime.out.$zone.2 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -r $O $TpubN1 -d $O $TremN "$KSK2" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" > settime.out.$zone.2 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${KSK1}.state" +echo "Lifetime: 0" >> "${ZSK1}.state" +cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" > "$infile" +private_type_record $zone 5 "$KSK1" >> "$infile" +private_type_record $zone 5 "$ZSK1" >> "$infile" +private_type_record $zone 13 "$KSK2" >> "$infile" +private_type_record $zone 13 "$ZSK2" >> "$infile" +$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 6: +# The RRSIGs have been removed long enough to be HIDDEN. +setup step6.algorithm-roll.kasp +KSK1=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK1=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) +KSK2=$($KEYGEN -a ECDSAP256SHA256 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) +ZSK2=$($KEYGEN -a ECDSAP256SHA256 -L 3600 $zone 2> keygen.out.$zone.2) +# Additional time passed: 7h. +TactN="now-47h" +TpubN1="now-47h" +TactN1="now-38h" +TremN="now-9h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $U $TremN -r $U $TremN -d $H $TremN "$KSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $U $TremN -z $U $TremN "$ZSK1" > settime.out.$zone.2 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -r $O $TpubN1 -d $O $TremN "$KSK2" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TactN1 -g $O -k $O $TpubN1 -z $R $TpubN1 "$ZSK2" > settime.out.$zone.2 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${KSK1}.state" +echo "Lifetime: 0" >> "${ZSK1}.state" +cat template.db.in "${KSK1}.key" "${ZSK1}.key" "${KSK2}.key" "${ZSK2}.key" > "$infile" +private_type_record $zone 5 "$KSK1" >> "$infile" +private_type_record $zone 5 "$ZSK1" >> "$infile" +private_type_record $zone 13 "$KSK2" >> "$infile" +private_type_record $zone 13 "$ZSK2" >> "$infile" +$SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 diff --git a/bin/tests/system/kasp/ns6/template.db.in b/bin/tests/system/kasp/ns6/template.db.in new file mode 100644 index 0000000000..051a312891 --- /dev/null +++ b/bin/tests/system/kasp/ns6/template.db.in @@ -0,0 +1,25 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns3 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 + diff --git a/bin/tests/system/kasp/setup.sh b/bin/tests/system/kasp/setup.sh index 0d93046ae1..a41b0ca946 100644 --- a/bin/tests/system/kasp/setup.sh +++ b/bin/tests/system/kasp/setup.sh @@ -22,6 +22,7 @@ copy_setports ns2/named.conf.in ns2/named.conf copy_setports ns3/named.conf.in ns3/named.conf copy_setports ns4/named.conf.in ns4/named.conf copy_setports ns5/named.conf.in ns5/named.conf +copy_setports ns6/named.conf.in ns6/named.conf # Setup zones ( @@ -40,3 +41,7 @@ copy_setports ns5/named.conf.in ns5/named.conf cd ns5 $SHELL setup.sh ) +( + cd ns6 + $SHELL setup.sh +) diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index e6c3028904..ddd1524232 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -1629,7 +1629,8 @@ check_next_key_event() { ret=0 grep "zone ${ZONE}.*: next key event in .* seconds" "${DIR}/named.run" > "keyevent.out.$ZONE.test$n" || log_error "no next key event for zone ${ZONE}" - _time=$(awk '{print $10}' < "keyevent.out.$ZONE.test$n") + # Get the latest next key event. + _time=$(awk '{print $10}' < "keyevent.out.$ZONE.test$n" | tail -1) # The next key event time must within 60 seconds of the # expected time. @@ -2081,7 +2082,7 @@ dnssec_verify check_next_key_event 13708800 # -# Testing CSK key rollover (1). +# Testing CSK key rollover (2). # # @@ -2218,5 +2219,163 @@ dnssec_verify # Next key event is when the new successor needs to be published. check_next_key_event 14684400 +# +# Testing algorithm rollover. +# + +# +# Zone: step1.algorithm-roll.kasp +# +zone_properties "ns6" "step1.algorithm-roll.kasp" "rsasha1" "3600" "2" "10.53.0.6" +# The KSK (KEY1) and ZSK (KEY2) start in OMNIPRESENT. +key_properties "KEY1" "ksk" "0" "5" "RSASHA1" "2048" "no" "yes" +key_timings "KEY1" "published" "active" "none" "none" "none" +key_states "KEY1" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent" +key_properties "KEY2" "zsk" "0" "5" "RSASHA1" "2048" "yes" "no" +key_timings "KEY2" "published" "active" "none" "none" "none" +key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "none" "none" +key_clear "KEY3" +key_clear "KEY4" +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the successor keys need to be published. +# Since the lifetime of the keys are unlimited, so default to loadkeys +# interval. +check_next_key_event 3600 + +# Reconfig dnssec-policy (triggering algorithm roll). +echo_i "reconfig dnssec-policy to trigger algorithm rollover" +copy_setports ns6/named2.conf.in ns6/named.conf +rndc_reconfig ns6 10.53.0.6 + +zone_properties "ns6" "step1.algorithm-roll.kasp" "ecdsa256" "3600" "4" "10.53.0.6" +# The RSAHSHA1 keys are outroducing. +key_timings "KEY1" "published" "active" "retired" "none" "none" +key_states "KEY1" "hidden" "omnipresent" "none" "omnipresent" "omnipresent" +key_timings "KEY2" "published" "active" "retired" "none" "none" +key_states "KEY2" "hidden" "omnipresent" "omnipresent" "none" "none" +# The ECDSAP256SHA256 keys are introducing. +key_properties "KEY3" "ksk" "0" "13" "ECDSAP256SHA256" "256" "no" "yes" +key_timings "KEY3" "published" "active" "none" "none" "none" +key_states "KEY3" "omnipresent" "rumoured" "none" "rumoured" "hidden" +key_properties "KEY4" "zsk" "0" "13" "ECDSAP256SHA256" "256" "yes" "no" +key_timings "KEY4" "published" "active" "none" "none" "none" +key_states "KEY4" "omnipresent" "rumoured" "rumoured" "none" "none" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the ecdsa256 keys have been propagated. +# This is the DNSKEY TTL plus publish safety plus zone propagation delay: +# 3 times an hour: 10800 seconds. +check_next_key_event 10800 + +# +# Zone: step2.algorithm-roll.kasp +# +zone_properties "ns6" "step2.algorithm-roll.kasp" "ecdsa256" "3600" "4" "10.53.0.6" +# The RSAHSHA1 keys are outroducing, but need to stay present until the new +# algorithm chain of trust has been established. Thus the properties, timings +# and states of the KEY1 and KEY2 are the same as above. +# +# The ECDSAP256SHA256 keys are introducing. The DNSKEY RRset is omnipresent, +# but the zone signatures are not. +key_states "KEY3" "omnipresent" "omnipresent" "none" "omnipresent" "hidden" +key_states "KEY4" "omnipresent" "omnipresent" "rumoured" "none" "none" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when all zone signatures are signed with the new +# algorithm. This is the max-zone-ttl plus zone propagation delay +# plus retire safety: 6h + 1h + 2h. But three hours have already passed +# (the time it took to make the DNSKEY omnipresent), so the next event +# should be scheduled in 6 hour: 21600 seconds. +check_next_key_event 21600 + +# +# Zone: step3.algorithm-roll.kasp +# +zone_properties "ns6" "step3.algorithm-roll.kasp" "ecdsa256" "3600" "4" "10.53.0.6" +# The RSAHSHA1 keys are outroducing, and it is time to swap the DS. +key_states "KEY1" "hidden" "omnipresent" "none" "omnipresent" "unretentive" +# The ECDSAP256SHA256 keys are introducing. The DNSKEY RRset and all signatures +# are now omnipresent, so the DS can be introduced. +key_states "KEY3" "omnipresent" "omnipresent" "none" "omnipresent" "rumoured" +key_states "KEY4" "omnipresent" "omnipresent" "omnipresent" "none" "none" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the DS becomes OMNIPRESENT. This happens after the +# parent registration delay, parent propagation delay, retire safety delay, +# and DS TTL: 24h + 1h + 2h + 2h = 29h = 104400 seconds. +check_next_key_event 104400 + +# +# Zone: step4.algorithm-roll.kasp +# +zone_properties "ns6" "step4.algorithm-roll.kasp" "ecdsa256" "3600" "4" "10.53.0.6" +# The old DS is HIDDEN, we can remove the old algorithm DNSKEY/RRSIG records. +key_properties "KEY1" "ksk" "0" "5" "RSASHA1" "2048" "no" "no" +key_states "KEY1" "hidden" "unretentive" "none" "unretentive" "hidden" +key_properties "KEY2" "zsk" "0" "5" "RSASHA1" "2048" "no" "no" +key_states "KEY2" "hidden" "unretentive" "unretentive" "none" "none" +# The ECDSAP256SHA256 DS is now OMNIPRESENT. +key_states "KEY3" "omnipresent" "omnipresent" "none" "omnipresent" "omnipresent" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the old DNSKEY becomes HIDDEN. This happens after the +# DNSKEY TTL plus zone propagation delay (2h). +check_next_key_event 7200 + +# +# Zone: step5.algorithm-roll.kasp +# +zone_properties "ns6" "step5.algorithm-roll.kasp" "ecdsa256" "3600" "4" "10.53.0.6" +# The DNSKEY becomes HIDDEN. +key_states "KEY1" "hidden" "hidden" "none" "hidden" "hidden" +key_states "KEY2" "hidden" "hidden" "unretentive" "none" "none" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the RSASHA1 signatures become HIDDEN. This happens +# after the max-zone-ttl plus zone propagation delay plus retire safety +# (6h + 1h + 2h) minus the time already passed since the UNRETENTIVE state has +# been reached (2h): 9h - 2h = 7h = 25200 +check_next_key_event 25200 + +# +# Zone: step6.algorithm-roll.kasp +# +zone_properties "ns6" "step6.algorithm-roll.kasp" "ecdsa256" "3600" "4" "10.53.0.6" +# The zone signatures should now also be HIDDEN. +key_states "KEY2" "hidden" "hidden" "hidden" "none" "none" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is never since we established the policy and the keys have +# an unlimited lifetime. Fallback to the default loadkeys interval. +check_next_key_event 3600 + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 17cbdeff24..ff02549c78 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -19657,17 +19657,28 @@ zone_rekey(dns_zone_t *zone) { /* * If keymgr provided a next time, use the calculated next rekey time. */ - if (kasp != NULL && nexttime > 0) { + if (kasp != NULL) { isc_time_t timenext; + uint32_t nexttime_seconds; - DNS_ZONE_TIME_ADD(&timenow, nexttime - now, &timenext); + /* + * Set the key refresh timer to the next scheduled key event + * or to 'dnssec-loadkeys-interval' seconds in the future + * if no next key event is scheduled (nexttime == 0). + */ + if (nexttime > 0) { + nexttime_seconds = nexttime - now; + } else { + nexttime_seconds = zone->refreshkeyinterval; + } + + DNS_ZONE_TIME_ADD(&timenow, nexttime_seconds, &timenext); zone->refreshkeytime = timenext; zone_settimer(zone, &timenow); isc_time_formattimestamp(&zone->refreshkeytime, timebuf, 80); dnssec_log(zone, ISC_LOG_DEBUG(3), - "next key event in %u seconds: %s", (nexttime - now), - timebuf); + "next key event in %u seconds", nexttime_seconds); dnssec_log(zone, ISC_LOG_INFO, "next key event: %s", timebuf); } /* diff --git a/util/copyrights b/util/copyrights index f5f9b4c02c..65f2e15d5a 100644 --- a/util/copyrights +++ b/util/copyrights @@ -697,6 +697,7 @@ ./bin/tests/system/kasp/ns3/setup.sh SH 2019,2020 ./bin/tests/system/kasp/ns4/setup.sh SH 2019,2020 ./bin/tests/system/kasp/ns5/setup.sh SH 2019,2020 +./bin/tests/system/kasp/ns6/setup.sh SH 2020 ./bin/tests/system/kasp/setup.sh SH 2019,2020 ./bin/tests/system/kasp/tests.sh SH 2019,2020 ./bin/tests/system/keepalive/clean.sh SH 2017,2018,2019,2020 From a8542b8cab05f90445c201d39c7c0c351244c2d3 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 18 Feb 2020 16:55:36 +0100 Subject: [PATCH 05/11] [#1626] Fix stuck algorithm rollover Algorithm rollover was stuck on submitting DS because keymgr thought it would move to an invalid state. It did not match the current key because it checked it against the current key in the next state. Fixed by when checking the current key, check it against the desired state, not the existing state. --- lib/dns/keymgr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 40d38ff835..9253c4fd0c 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -546,8 +546,15 @@ keymgr_ds_hidden_or_chained(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, * chain of trust (can be this key). */ dnskey_omnipresent[DST_KEY_DS] = NA; - (void)dst_key_getstate(dkey->key, DST_KEY_DS, - &dnskey_omnipresent[DST_KEY_DS]); + if (next_state != NA && + dst_key_id(dkey->key) == dst_key_id(key->key)) + { + /* Check next state rather than current state. */ + dnskey_omnipresent[DST_KEY_DS] = next_state; + } else { + (void)dst_key_getstate(dkey->key, DST_KEY_DS, + &dnskey_omnipresent[DST_KEY_DS]); + } if (!keymgr_key_exists_with_state( keyring, key, type, next_state, dnskey_omnipresent, na, false, match_algorithms)) From 28506159f03886f4952dec83acc66307736c13af Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 18 Feb 2020 16:57:37 +0100 Subject: [PATCH 06/11] [#1625] Algorithm rollover waited too long Algorithm rollover waited too long before introducing zone signatures. It waited to make sure all signatures were resigned, but when introducing a new algorithm, all signatures are resigned immediately. Only add the sign delay if there is a predecessor key. --- lib/dns/keymgr.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 9253c4fd0c..a91173d646 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -1000,14 +1000,22 @@ keymgr_transition_time(dns_dnsseckey_t *key, int type, * TTLsig is the maximum TTL of all zone RRSIG * records. This translates to: * - * Dsgn + zone-propragation-delay + max-zone-ttl. + * Dsgn + zone-propagation-delay + max-zone-ttl. * * We will also add the retire-safety interval. */ - nexttime = lastchange + dns_kasp_signdelay(kasp) + - dns_kasp_zonemaxttl(kasp) + + nexttime = lastchange + dns_kasp_zonemaxttl(kasp) + dns_kasp_zonepropagationdelay(kasp) + dns_kasp_retiresafety(kasp); + /* + * Only add the sign delay Dsgn if there is an actual + * predecessor key. + */ + uint32_t pre; + if (dst_key_getnum(key->key, DST_NUM_PREDECESSOR, + &pre) == ISC_R_SUCCESS) { + nexttime += dns_kasp_signdelay(kasp); + } break; default: nexttime = now; From 3905a03205075934d5aca134a0e76a1170faf2ce Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 18 Feb 2020 16:58:56 +0100 Subject: [PATCH 07/11] [#1624] dnssec-policy change retire unwanted keys When changing a dnssec-policy, existing keys with properties that no longer match were not being retired. --- lib/dns/keymgr.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index a91173d646..335c038d72 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -1297,6 +1297,39 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, } } + /* Do we need to remove keys? */ + for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); + dkey != NULL; dkey = ISC_LIST_NEXT(dkey, link)) + { + bool found_match = false; + + /* Make sure this key knows about roles. */ + keymgr_key_init_role(dkey); + + for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL; + kkey = ISC_LIST_NEXT(kkey, link)) + { + if (keymgr_dnsseckey_kaspkey_match(dkey, kkey)) { + found_match = true; + dst_key_format(dkey->key, keystr, + sizeof(keystr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, + ISC_LOG_DEBUG(1), + "keymgr: DNSKEY %s (%s) matches " + "policy %s", keystr, + keymgr_keyrole(dkey->key), + dns_kasp_getname(kasp)); + break; + } + } + + /* No match, so retire unwanted retire key. */ + if (!found_match) { + keymgr_key_retire(dkey, now); + } + } + /* Create keys according to the policy, if come in short. */ for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL; kkey = ISC_LIST_NEXT(kkey, link)) @@ -1309,9 +1342,6 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; dkey = ISC_LIST_NEXT(dkey, link)) { - /* Make sure this key knows about roles. */ - keymgr_key_init_role(dkey); - if (keymgr_dnsseckey_kaspkey_match(dkey, kkey)) { /* Found a match. */ From 917cf5f86fadb187c78a13823407eb858190c451 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 19 Feb 2020 12:28:36 +0100 Subject: [PATCH 08/11] Add CSK algorithm rollover test --- bin/tests/system/kasp/ns6/named.conf.in | 7 + bin/tests/system/kasp/ns6/named2.conf.in | 40 +++++ bin/tests/system/kasp/ns6/policies/csk1.conf | 29 ++++ bin/tests/system/kasp/ns6/policies/csk2.conf | 29 ++++ bin/tests/system/kasp/ns6/setup.sh | 110 +++++++++++++ bin/tests/system/kasp/tests.sh | 156 +++++++++++++++++++ 6 files changed, 371 insertions(+) create mode 100644 bin/tests/system/kasp/ns6/policies/csk1.conf create mode 100644 bin/tests/system/kasp/ns6/policies/csk2.conf diff --git a/bin/tests/system/kasp/ns6/named.conf.in b/bin/tests/system/kasp/ns6/named.conf.in index 5bb8185e47..5a6ca042c4 100644 --- a/bin/tests/system/kasp/ns6/named.conf.in +++ b/bin/tests/system/kasp/ns6/named.conf.in @@ -12,6 +12,7 @@ // NS6 include "policies/kasp.conf"; +include "policies/csk1.conf"; options { query-source address 10.53.0.6; @@ -39,3 +40,9 @@ zone "step1.algorithm-roll.kasp" { file "step1.algorithm-roll.kasp.db"; dnssec-policy "rsasha1"; }; + +zone "step1.csk-algorithm-roll.kasp" { + type master; + file "step1.csk-algorithm-roll.kasp.db"; + dnssec-policy "csk-algoroll"; +}; diff --git a/bin/tests/system/kasp/ns6/named2.conf.in b/bin/tests/system/kasp/ns6/named2.conf.in index b2810e1bb9..52660c6084 100644 --- a/bin/tests/system/kasp/ns6/named2.conf.in +++ b/bin/tests/system/kasp/ns6/named2.conf.in @@ -12,6 +12,7 @@ // NS6 include "policies/kasp.conf"; +include "policies/csk2.conf"; options { query-source address 10.53.0.6; @@ -69,3 +70,42 @@ zone "step6.algorithm-roll.kasp" { file "step6.algorithm-roll.kasp.db"; dnssec-policy "ecdsa256"; }; + +/* + * Zones for testing CSK algorithm roll. + */ +zone "step1.csk-algorithm-roll.kasp" { + type master; + file "step1.csk-algorithm-roll.kasp.db"; + dnssec-policy "csk-algoroll"; +}; + +zone "step2.csk-algorithm-roll.kasp" { + type master; + file "step2.csk-algorithm-roll.kasp.db"; + dnssec-policy "csk-algoroll"; +}; + +zone "step3.csk-algorithm-roll.kasp" { + type master; + file "step3.csk-algorithm-roll.kasp.db"; + dnssec-policy "csk-algoroll"; +}; + +zone "step4.csk-algorithm-roll.kasp" { + type master; + file "step4.csk-algorithm-roll.kasp.db"; + dnssec-policy "csk-algoroll"; +}; + +zone "step5.csk-algorithm-roll.kasp" { + type master; + file "step5.csk-algorithm-roll.kasp.db"; + dnssec-policy "csk-algoroll"; +}; + +zone "step6.csk-algorithm-roll.kasp" { + type master; + file "step6.csk-algorithm-roll.kasp.db"; + dnssec-policy "csk-algoroll"; +}; diff --git a/bin/tests/system/kasp/ns6/policies/csk1.conf b/bin/tests/system/kasp/ns6/policies/csk1.conf new file mode 100644 index 0000000000..8f93444807 --- /dev/null +++ b/bin/tests/system/kasp/ns6/policies/csk1.conf @@ -0,0 +1,29 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-policy "csk-algoroll" { + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + keys { + csk lifetime unlimited algorithm rsasha1; + }; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + zone-propagation-delay 3600; + max-zone-ttl 6h; + parent-registration-delay 1d; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; diff --git a/bin/tests/system/kasp/ns6/policies/csk2.conf b/bin/tests/system/kasp/ns6/policies/csk2.conf new file mode 100644 index 0000000000..f379c0574f --- /dev/null +++ b/bin/tests/system/kasp/ns6/policies/csk2.conf @@ -0,0 +1,29 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-policy "csk-algoroll" { + signatures-refresh P5D; + signatures-validity 30d; + signatures-validity-dnskey 30d; + + keys { + csk lifetime unlimited algorithm 13; + }; + + dnskey-ttl 1h; + publish-safety PT1H; + retire-safety 2h; + zone-propagation-delay 3600; + max-zone-ttl 6h; + parent-registration-delay 1d; + parent-propagation-delay pt1h; + parent-ds-ttl 7200; +}; diff --git a/bin/tests/system/kasp/ns6/setup.sh b/bin/tests/system/kasp/ns6/setup.sh index 2616d8e952..09d8bd0d79 100644 --- a/bin/tests/system/kasp/ns6/setup.sh +++ b/bin/tests/system/kasp/ns6/setup.sh @@ -183,3 +183,113 @@ private_type_record $zone 5 "$ZSK1" >> "$infile" private_type_record $zone 13 "$KSK2" >> "$infile" private_type_record $zone 13 "$ZSK2" >> "$infile" $SIGNER -S -x -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# +# The zones at csk-algorithm-roll.kasp represent the various steps of a CSK +# algorithm rollover. +# + +# Step 1: +# Introduce the first key. This will immediately be active. +setup step1.csk-algorithm-roll.kasp +echo "$zone" >> zones +CSK=$($KEYGEN -k csk-algoroll -l policies/csk1.conf $zone 2> keygen.out.$zone.1) +TactN="now" +$SETTIME -s -P $TactN -A $TactN -g $O -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK" > settime.out.$zone.1 2>&1 +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone 5 "$CSK" >> "$infile" +$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 2: +# After the publication interval has passed the DNSKEY is OMNIPRESENT. +setup step2.csk-algorithm-roll.kasp +CSK1=$($KEYGEN -k csk-algoroll -l policies/csk1.conf $zone 2> keygen.out.$zone.1) +CSK2=$($KEYGEN -k csk-algoroll -l policies/csk2.conf $zone 2> keygen.out.$zone.1) +# The time passed since the new algorithm keys have been introduced is 3 hours. +TactN="now-3h" +TpubN1="now-3h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TpubN1 -g $O -k $R $TpubN1 -r $R $TpubN1 -z $R $TpubN1 -d $H $TpubN1 "$CSK2" > settime.out.$zone.1 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${CSK1}.state" +cat template.db.in "${CSK1}.key" "${CSK2}.key" > "$infile" +private_type_record $zone 5 "$CSK1" >> "$infile" +private_type_record $zone 13 "$CSK2" >> "$infile" +$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 3: +# The zone signatures are also OMNIPRESENT. +setup step3.csk-algorithm-roll.kasp +CSK1=$($KEYGEN -k csk-algoroll -l policies/csk1.conf $zone 2> keygen.out.$zone.1) +CSK2=$($KEYGEN -k csk-algoroll -l policies/csk2.conf $zone 2> keygen.out.$zone.1) +# The time passed since the new algorithm keys have been introduced is 9 hours. +TactN="now-9h" +TpubN1="now-9h" +TactN1="now-6h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -r $O $TactN -z $O $TactN -d $O $TactN "$CSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TpubN1 -g $O -k $O $TactN1 -r $O $TactN1 -z $R $TpubN1 -d $H $TpubN1 "$CSK2" > settime.out.$zone.1 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${CSK1}.state" +cat template.db.in "${CSK1}.key" "${CSK2}.key" > "$infile" +private_type_record $zone 5 "$CSK1" >> "$infile" +private_type_record $zone 13 "$CSK2" >> "$infile" +$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 4: +# The DS is swapped and can become OMNIPRESENT. +setup step4.csk-algorithm-roll.kasp +CSK1=$($KEYGEN -k csk-algoroll -l policies/csk1.conf $zone 2> keygen.out.$zone.1) +CSK2=$($KEYGEN -k csk-algoroll -l policies/csk2.conf $zone 2> keygen.out.$zone.1) +# The time passed since the DS has been swapped is 29 hours. +TactN="now-38h" +TpubN1="now-38h" +TactN1="now-35h" +TsubN1="now-29h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $O $TactN -r $O $TactN -z $O $TactN -d $U $TactN1 "$CSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TpubN1 -g $O -k $O $TactN1 -r $O $TactN1 -z $O $TsubN1 -d $R $TsubN1 "$CSK2" > settime.out.$zone.1 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${CSK1}.state" +cat template.db.in "${CSK1}.key" "${CSK2}.key" > "$infile" +private_type_record $zone 5 "$CSK1" >> "$infile" +private_type_record $zone 13 "$CSK2" >> "$infile" +$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 5: +# The DNSKEY is removed long enough to be HIDDEN. +setup step5.csk-algorithm-roll.kasp +CSK1=$($KEYGEN -k csk-algoroll -l policies/csk1.conf $zone 2> keygen.out.$zone.1) +CSK2=$($KEYGEN -k csk-algoroll -l policies/csk2.conf $zone 2> keygen.out.$zone.1) +# The time passed since the DNSKEY has been removed is 2 hours. +TactN="now-40h" +TpubN1="now-40h" +TactN1="now-37h" +TsubN1="now-31h" +TremN="now-2h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $U $TremN -r $U $TremN -z $U $TremN -d $H $TremN "$CSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TpubN1 -g $O -k $O $TactN1 -r $O $TactN1 -z $O $TsubN1 -d $O $TremN "$CSK2" > settime.out.$zone.1 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${CSK1}.state" +cat template.db.in "${CSK1}.key" "${CSK2}.key" > "$infile" +private_type_record $zone 5 "$CSK1" >> "$infile" +private_type_record $zone 13 "$CSK2" >> "$infile" +$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 + +# Step 6: +# The RRSIGs have been removed long enough to be HIDDEN. +setup step6.csk-algorithm-roll.kasp +CSK1=$($KEYGEN -k csk-algoroll -l policies/csk1.conf $zone 2> keygen.out.$zone.1) +CSK2=$($KEYGEN -k csk-algoroll -l policies/csk2.conf $zone 2> keygen.out.$zone.1) +# Additional time passed: 7h. +TactN="now-47h" +TpubN1="now-47h" +TactN1="now-44h" +TsubN1="now-38h" +TremN="now-9h" +$SETTIME -s -P $TactN -A $TactN -I now -g $H -k $U $TremN -r $U $TremN -z $U $TremN -d $H $TremN "$CSK1" > settime.out.$zone.1 2>&1 +$SETTIME -s -P $TpubN1 -A $TpubN1 -g $O -k $O $TactN1 -r $O $TactN1 -z $O $TsubN1 -d $O $TremN "$CSK2" > settime.out.$zone.1 2>&1 +# Fake lifetime of old algorithm keys. +echo "Lifetime: 0" >> "${CSK1}.state" +cat template.db.in "${CSK1}.key" "${CSK2}.key" > "$infile" +private_type_record $zone 5 "$CSK1" >> "$infile" +private_type_record $zone 13 "$CSK2" >> "$infile" +$SIGNER -S -x -z -s now-1h -e now+2w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index ddd1524232..f1cef6cd52 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -2246,15 +2246,45 @@ dnssec_verify # interval. check_next_key_event 3600 +# +# Zone: step1.csk-algorithm-roll.kasp +# +zone_properties "ns6" "step1.csk-algorithm-roll.kasp" "csk-algoroll" "3600" "1" "10.53.0.6" +# The CSK (KEY1) starta in OMNIPRESENT. +key_properties "KEY1" "csk" "0" "5" "RSASHA1" "2048" "yes" "yes" +key_timings "KEY1" "published" "active" "none" "none" "none" +key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent" +key_clear "KEY2" +key_clear "KEY3" +key_clear "KEY4" +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the successor keys need to be published. +# Since the lifetime of the keys are unlimited, so default to loadkeys +# interval. +check_next_key_event 3600 + # Reconfig dnssec-policy (triggering algorithm roll). echo_i "reconfig dnssec-policy to trigger algorithm rollover" copy_setports ns6/named2.conf.in ns6/named.conf rndc_reconfig ns6 10.53.0.6 +# +# Testing KSK/ZSK algorithm rollover. +# + +# +# Zone: step1.algorithm-roll.kasp +# zone_properties "ns6" "step1.algorithm-roll.kasp" "ecdsa256" "3600" "4" "10.53.0.6" # The RSAHSHA1 keys are outroducing. +key_properties "KEY1" "ksk" "0" "5" "RSASHA1" "2048" "no" "yes" key_timings "KEY1" "published" "active" "retired" "none" "none" key_states "KEY1" "hidden" "omnipresent" "none" "omnipresent" "omnipresent" +key_properties "KEY2" "zsk" "0" "5" "RSASHA1" "2048" "yes" "no" key_timings "KEY2" "published" "active" "retired" "none" "none" key_states "KEY2" "hidden" "omnipresent" "omnipresent" "none" "none" # The ECDSAP256SHA256 keys are introducing. @@ -2377,5 +2407,131 @@ dnssec_verify # an unlimited lifetime. Fallback to the default loadkeys interval. check_next_key_event 3600 +# +# Testing CSK algorithm rollover. +# + +# +# Zone: step1.csk-algorithm-roll.kasp +# +zone_properties "ns6" "step1.csk-algorithm-roll.kasp" "csk-algoroll" "3600" "2" "10.53.0.6" +# The RSAHSHA1 key is outroducing. +key_properties "KEY1" "csk" "0" "5" "RSASHA1" "2048" "yes" "yes" +key_timings "KEY1" "published" "active" "retired" "none" "none" +key_states "KEY1" "hidden" "omnipresent" "omnipresent" "omnipresent" "omnipresent" +# The ECDSAP256SHA256 key is introducing. +key_properties "KEY2" "csk" "0" "13" "ECDSAP256SHA256" "256" "yes" "yes" +key_timings "KEY2" "published" "active" "none" "none" "none" +key_states "KEY2" "omnipresent" "rumoured" "rumoured" "rumoured" "hidden" +key_clear "KEY3" +key_clear "KEY4" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the new key has been propagated. +# This is the DNSKEY TTL plus publish safety plus zone propagation delay: +# 3 times an hour: 10800 seconds. +check_next_key_event 10800 + +# +# Zone: step2.csk-algorithm-roll.kasp +# +zone_properties "ns6" "step2.csk-algorithm-roll.kasp" "csk-algoroll" "3600" "2" "10.53.0.6" +# The RSAHSHA1 key is outroducing, but need to stay present until the new +# algorithm chain of trust has been established. Thus the properties, timings +# and states of KEY1 is the same as above. +# +# The ECDSAP256SHA256 keys are introducing. The DNSKEY RRset is omnipresent, +# but the zone signatures are not. +key_states "KEY2" "omnipresent" "omnipresent" "rumoured" "omnipresent" "hidden" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when all zone signatures are signed with the new +# algorithm. This is the max-zone-ttl plus zone propagation delay +# plus retire safety: 6h + 1h + 2h. But three hours have already passed +# (the time it took to make the DNSKEY omnipresent), so the next event +# should be scheduled in 6 hour: 21600 seconds. +check_next_key_event 21600 + +# +# Zone: step3.csk-algorithm-roll.kasp +# +zone_properties "ns6" "step3.csk-algorithm-roll.kasp" "csk-algoroll" "3600" "2" "10.53.0.6" +# The RSAHSHA1 key is outroducing, and it is time to swap the DS. +key_states "KEY1" "hidden" "omnipresent" "omnipresent" "omnipresent" "unretentive" +# The ECDSAP256SHA256 key is introducing. The DNSKEY RRset and all signatures +# are now omnipresent, so the DS can be introduced. +key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "rumoured" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the DS becomes OMNIPRESENT. This happens after the +# parent registration delay, parent propagation delay, retire safety delay, +# and DS TTL: 24h + 1h + 2h + 2h = 29h = 104400 seconds. +check_next_key_event 104400 + +# +# Zone: step4.csk-algorithm-roll.kasp +# +zone_properties "ns6" "step4.csk-algorithm-roll.kasp" "csk-algoroll" "3600" "2" "10.53.0.6" +# The old DS is HIDDEN, we can remove the old algorithm DNSKEY/RRSIG records. +key_properties "KEY1" "csk" "0" "5" "RSASHA1" "2048" "no" "no" +key_states "KEY1" "hidden" "unretentive" "unretentive" "unretentive" "hidden" +# The ECDSAP256SHA256 DS is now OMNIPRESENT. +key_states "KEY2" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the old DNSKEY becomes HIDDEN. This happens after the +# DNSKEY TTL plus zone propagation delay (2h). +check_next_key_event 7200 + +# +# Zone: step5.csk-algorithm-roll.kasp +# +zone_properties "ns6" "step5.csk-algorithm-roll.kasp" "csk-algoroll" "3600" "2" "10.53.0.6" +# The DNSKEY becomes HIDDEN. +key_states "KEY1" "hidden" "hidden" "unretentive" "hidden" "hidden" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is when the RSASHA1 signatures become HIDDEN. This happens +# after the max-zone-ttl plus zone propagation delay plus retire safety +# (6h + 1h + 2h) minus the time already passed since the UNRETENTIVE state has +# been reached (2h): 9h - 2h = 7h = 25200 +check_next_key_event 25200 + +# +# Zone: step6.csk-algorithm-roll.kasp +# +zone_properties "ns6" "step6.csk-algorithm-roll.kasp" "csk-algoroll" "3600" "2" "10.53.0.6" +# The zone signatures should now also be HIDDEN. +key_states "KEY1" "hidden" "hidden" "hidden" "hidden" "hidden" + +check_keys +check_apex +check_subdomain +dnssec_verify + +# Next key event is never since we established the policy and the keys have +# an unlimited lifetime. Fallback to the default loadkeys interval. +check_next_key_event 3600 + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 From 6ddfed3de0531d91b43fbdd28e258527dc35f331 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 19 Feb 2020 12:28:52 +0100 Subject: [PATCH 09/11] update CHANGES --- CHANGES | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGES b/CHANGES index a6a44f5125..fac1565025 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,20 @@ +5365. [bug] Algorithm rollover was stuck on submitting DS + because keymgr thought it would move to an invalid + state. Fixed by when checking the current key, + check it against the desired state, not the existing + state. [GL #1626] + +5364. [bug] Algorithm rollover waited too long before introducing + zone signatures. It waited to make sure all signatures + were resigned, but when introducing a new algorithm, + all signatures are resigned immediately. Only + add the sign delay if there is a predecessor key. + [GL #1625] + +5363. [bug] When changing a dnssec-policy, existing keys with + properties that no longer match were not being retired. + [GL #1624] + 5362. [func] Limit the size of IXFR responses so that AXFR will be used instead if it would be smaller. This is controlled by the "max-ixfr-ratio" option, which From 53bd81ad1992520087a05edfd51fdb4ed627b789 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 20 Feb 2020 11:04:08 +0100 Subject: [PATCH 10/11] Make clang-format happy --- lib/dns/keymgr.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 335c038d72..8a54ea9771 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -547,8 +547,7 @@ keymgr_ds_hidden_or_chained(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key, */ dnskey_omnipresent[DST_KEY_DS] = NA; if (next_state != NA && - dst_key_id(dkey->key) == dst_key_id(key->key)) - { + dst_key_id(dkey->key) == dst_key_id(key->key)) { /* Check next state rather than current state. */ dnskey_omnipresent[DST_KEY_DS] = next_state; } else { @@ -1298,8 +1297,8 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, } /* Do we need to remove keys? */ - for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); - dkey != NULL; dkey = ISC_LIST_NEXT(dkey, link)) + for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL; + dkey = ISC_LIST_NEXT(dkey, link)) { bool found_match = false; @@ -1317,8 +1316,8 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass, DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1), "keymgr: DNSKEY %s (%s) matches " - "policy %s", keystr, - keymgr_keyrole(dkey->key), + "policy %s", + keystr, keymgr_keyrole(dkey->key), dns_kasp_getname(kasp)); break; } From d16520532ffebbb9bbcabf60fc26e4806c03e6a8 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 20 Feb 2020 16:00:50 +0100 Subject: [PATCH 11/11] Add additional wait period for algorithm rollover We may be checking the algorithm steps too fast: the reconfig command may still be in progress. Make sure the zones are signed and loaded by digging the NSEC records for these zones. --- bin/tests/system/kasp/clean.sh | 2 +- bin/tests/system/kasp/ns6/setup.sh | 2 +- bin/tests/system/kasp/tests.sh | 40 +++++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/bin/tests/system/kasp/clean.sh b/bin/tests/system/kasp/clean.sh index 98239093c5..cf2f2452ce 100644 --- a/bin/tests/system/kasp/clean.sh +++ b/bin/tests/system/kasp/clean.sh @@ -22,4 +22,4 @@ rm -f ns*/dsset-* ns*/*.db ns*/*.db.signed rm -f ns*/keygen.out.* ns*/settime.out.* ns*/signer.out.* rm -f ns*/managed-keys.bind rm -f ns*/*.mkeys -rm -f ns*/zones ns*/*.db.infile +rm -f ns*/zones* ns*/*.db.infile diff --git a/bin/tests/system/kasp/ns6/setup.sh b/bin/tests/system/kasp/ns6/setup.sh index 09d8bd0d79..cae3475535 100644 --- a/bin/tests/system/kasp/ns6/setup.sh +++ b/bin/tests/system/kasp/ns6/setup.sh @@ -19,6 +19,7 @@ setup() { echo_i "setting up zone: $zone" zonefile="${zone}.db" infile="${zone}.db.infile" + echo "$zone" >> zones.2 } private_type_record() { @@ -46,7 +47,6 @@ U="UNRETENTIVE" # Step 1: # Introduce the first key. This will immediately be active. setup step1.algorithm-roll.kasp -echo "$zone" >> zones KSK=$($KEYGEN -a RSASHA1 -f KSK -L 3600 $zone 2> keygen.out.$zone.1) ZSK=$($KEYGEN -a RSASHA1 -L 3600 $zone 2> keygen.out.$zone.2) TactN="now" diff --git a/bin/tests/system/kasp/tests.sh b/bin/tests/system/kasp/tests.sh index f1cef6cd52..d5f6b1808a 100644 --- a/bin/tests/system/kasp/tests.sh +++ b/bin/tests/system/kasp/tests.sh @@ -465,6 +465,9 @@ dnssec_verify() status=$((status+ret)) } +# Default next key event threshold. May be extended by wait periods. +next_key_event_threshold=100 + ############################################################################### # Tests # ############################################################################### @@ -611,7 +614,6 @@ check_key "KEY1" "$id" test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) - # # named # @@ -649,6 +651,8 @@ done test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) +next_key_event_threshold=$((next_key_event_threshold+i)) + # # Zone: default.kasp. # @@ -1632,10 +1636,10 @@ check_next_key_event() { # Get the latest next key event. _time=$(awk '{print $10}' < "keyevent.out.$ZONE.test$n" | tail -1) - # The next key event time must within 60 seconds of the + # The next key event time must within threshold of the # expected time. - _expectmin=$((_expect-60)) - _expectmax=$((_expect+60)) + _expectmin=$((_expect-next_key_event_threshold)) + _expectmax=$((_expect+next_key_event_threshold)) test $_expectmin -le "$_time" || log_error "bad next key event time ${_time} for zone ${ZONE} (expect ${_expect})" test $_expectmax -ge "$_time" || log_error "bad next key event time ${_time} for zone ${ZONE} (expect ${_expect})" @@ -2272,6 +2276,34 @@ echo_i "reconfig dnssec-policy to trigger algorithm rollover" copy_setports ns6/named2.conf.in ns6/named.conf rndc_reconfig ns6 10.53.0.6 +# The NSEC record at the apex of the zone and its RRSIG records are +# added as part of the last step in signing a zone. We wait for the +# NSEC records to appear before proceeding with a counter to prevent +# infinite loops if there is a error. +# +n=$((n+1)) +echo_i "waiting for reconfig signing changes to take effect ($n)" +i=0 +while [ $i -lt 30 ] +do + ret=0 + while read -r zone + do + dig_with_opts "$zone" @10.53.0.6 nsec > "dig.out.ns6.test$n.$zone" || ret=1 + grep "NS SOA" "dig.out.ns6.test$n.$zone" > /dev/null || ret=1 + grep "$zone\..*IN.*RRSIG" "dig.out.ns6.test$n.$zone" > /dev/null || ret=1 + done < ns6/zones.2 + + i=$((i+1)) + if [ $ret = 0 ]; then break; fi + echo_i "waiting ... ($i)" + sleep 1 +done +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +next_key_event_threshold=$((next_key_event_threshold+i)) + # # Testing KSK/ZSK algorithm rollover. #