Add kasp tests for offline keys
Add a test for default.kasp that if we remove the private key file,
no successor key is created for it. We need to update the kasp script
to deal with a missing private key. If this is the case, skip checks
for private key files.
Add a test with a zone for which the private key of the ZSK is missing.
Add a test with a zone for which the private key of the KSK is missing.
(cherry picked from commit 4a8ad0a77f)
This commit is contained in:
+52
-17
@@ -59,6 +59,7 @@ VIEW2="4xILSZQnuO1UKubXHkYUsvBRPu8="
|
||||
# EXPECT_ZRRSIG
|
||||
# EXPECT_KRRSIG
|
||||
# LEGACY
|
||||
# PRIVATE
|
||||
|
||||
key_key() {
|
||||
echo "${1}__${2}"
|
||||
@@ -112,6 +113,7 @@ key_clear() {
|
||||
key_set "$1" "EXPECT_ZRRSIG" 'no'
|
||||
key_set "$1" "EXPECT_KRRSIG" 'no'
|
||||
key_set "$1" "LEGACY" 'no'
|
||||
key_set "$1" "PRIVATE" 'yes'
|
||||
}
|
||||
|
||||
# Start clear.
|
||||
@@ -303,6 +305,7 @@ check_key() {
|
||||
_dnskey_ttl="$DNSKEY_TTL"
|
||||
_lifetime=$(key_get "$1" LIFETIME)
|
||||
_legacy=$(key_get "$1" LEGACY)
|
||||
_private=$(key_get "$1" PRIVATE)
|
||||
|
||||
_published=$(key_get "$1" PUBLISHED)
|
||||
_active=$(key_get "$1" ACTIVE)
|
||||
@@ -341,7 +344,9 @@ check_key() {
|
||||
|
||||
# Check file existence.
|
||||
[ -s "$KEY_FILE" ] || ret=1
|
||||
[ -s "$PRIVATE_FILE" ] || ret=1
|
||||
if [ "$_private" = "yes" ]; then
|
||||
[ -s "$PRIVATE_FILE" ] || ret=1
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
[ -s "$STATE_FILE" ] || ret=1
|
||||
fi
|
||||
@@ -352,7 +357,9 @@ check_key() {
|
||||
grep "; Created:" "$KEY_FILE" > "${ZONE}.${KEY_ID}.${_alg_num}.created" || _log_error "mismatch created comment in $KEY_FILE"
|
||||
KEY_CREATED=$(awk '{print $3}' < "${ZONE}.${KEY_ID}.${_alg_num}.created")
|
||||
|
||||
grep "Created: ${KEY_CREATED}" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch created in $PRIVATE_FILE"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Created: ${KEY_CREATED}" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch created in $PRIVATE_FILE"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Generated: ${KEY_CREATED}" "$STATE_FILE" > /dev/null || _log_error "mismatch generated in $STATE_FILE"
|
||||
fi
|
||||
@@ -363,8 +370,10 @@ check_key() {
|
||||
grep "This is a ${_role2} key, keyid ${_key_id}, for ${_zone}." "$KEY_FILE" > /dev/null || _log_error "mismatch top comment in $KEY_FILE"
|
||||
grep "${_zone}\. ${_dnskey_ttl} IN DNSKEY ${_flags} 3 ${_alg_num}" "$KEY_FILE" > /dev/null || _log_error "mismatch DNSKEY record in $KEY_FILE"
|
||||
# Now check the private key file.
|
||||
grep "Private-key-format: v1.3" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch private key format in $PRIVATE_FILE"
|
||||
grep "Algorithm: ${_alg_num} (${_alg_string})" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch algorithm in $PRIVATE_FILE"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Private-key-format: v1.3" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch private key format in $PRIVATE_FILE"
|
||||
grep "Algorithm: ${_alg_num} (${_alg_string})" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch algorithm in $PRIVATE_FILE"
|
||||
fi
|
||||
# Now check the key state file.
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "This is the state of key ${_key_id}, for ${_zone}." "$STATE_FILE" > /dev/null || _log_error "mismatch top comment in $STATE_FILE"
|
||||
@@ -444,6 +453,8 @@ check_timingmetadata() {
|
||||
_key_file="${_base_file}.key"
|
||||
_private_file="${_base_file}.private"
|
||||
_state_file="${_base_file}.state"
|
||||
_legacy=$(key_get "$1" LEGACY)
|
||||
_private=$(key_get "$1" PRIVATE)
|
||||
|
||||
_published=$(key_get "$1" PUBLISHED)
|
||||
_syncpublish=$(key_get "$1" SYNCPUBLISH)
|
||||
@@ -459,13 +470,17 @@ check_timingmetadata() {
|
||||
|
||||
if [ "$_published" = "none" ]; then
|
||||
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}"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Publish:" "${_private_file}" > /dev/null && _log_error "unexpected publish in ${_private_file}"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Published: " "${_state_file}" > /dev/null && _log_error "unexpected publish in ${_state_file}"
|
||||
fi
|
||||
else
|
||||
grep "; Publish: $_published" "${_key_file}" > /dev/null || _log_error "mismatch publish comment in ${_key_file} (expected ${_published})"
|
||||
grep "Publish: $_published" "${_private_file}" > /dev/null || _log_error "mismatch publish in ${_private_file} (expected ${_published})"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Publish: $_published" "${_private_file}" > /dev/null || _log_error "mismatch publish in ${_private_file} (expected ${_published})"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Published: $_published" "${_state_file}" > /dev/null || _log_error "mismatch publish in ${_state_file} (expected ${_published})"
|
||||
fi
|
||||
@@ -473,13 +488,17 @@ check_timingmetadata() {
|
||||
|
||||
if [ "$_syncpublish" = "none" ]; then
|
||||
grep "; SyncPublish:" "${_key_file}" > /dev/null && _log_error "unexpected syncpublish comment in ${_key_file}"
|
||||
grep "SyncPublish:" "${_private_file}" > /dev/null && _log_error "unexpected syncpublish in ${_private_file}"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "SyncPublish:" "${_private_file}" > /dev/null && _log_error "unexpected syncpublish in ${_private_file}"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "PublishCDS: " "${_state_file}" > /dev/null && _log_error "unexpected syncpublish in ${_state_file}"
|
||||
fi
|
||||
else
|
||||
grep "; SyncPublish: $_syncpublish" "${_key_file}" > /dev/null || _log_error "mismatch syncpublish comment in ${_key_file} (expected ${_syncpublish})"
|
||||
grep "SyncPublish: $_syncpublish" "${_private_file}" > /dev/null || _log_error "mismatch syncpublish in ${_private_file} (expected ${_syncpublish})"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "SyncPublish: $_syncpublish" "${_private_file}" > /dev/null || _log_error "mismatch syncpublish in ${_private_file} (expected ${_syncpublish})"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "PublishCDS: $_syncpublish" "${_state_file}" > /dev/null || _log_error "mismatch syncpublish in ${_state_file} (expected ${_syncpublish})"
|
||||
fi
|
||||
@@ -487,13 +506,17 @@ check_timingmetadata() {
|
||||
|
||||
if [ "$_active" = "none" ]; then
|
||||
grep "; Activate:" "${_key_file}" > /dev/null && _log_error "unexpected active comment in ${_key_file}"
|
||||
grep "Activate:" "${_private_file}" > /dev/null && _log_error "unexpected active in ${_private_file}"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Activate:" "${_private_file}" > /dev/null && _log_error "unexpected active in ${_private_file}"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Active: " "${_state_file}" > /dev/null && _log_error "unexpected active in ${_state_file}"
|
||||
fi
|
||||
else
|
||||
grep "; Activate: $_active" "${_key_file}" > /dev/null || _log_error "mismatch active comment in ${_key_file} (expected ${_active})"
|
||||
grep "Activate: $_active" "${_private_file}" > /dev/null || _log_error "mismatch active in ${_private_file} (expected ${_active})"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Activate: $_active" "${_private_file}" > /dev/null || _log_error "mismatch active in ${_private_file} (expected ${_active})"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Active: $_active" "${_state_file}" > /dev/null || _log_error "mismatch active in ${_state_file} (expected ${_active})"
|
||||
fi
|
||||
@@ -501,13 +524,17 @@ check_timingmetadata() {
|
||||
|
||||
if [ "$_retired" = "none" ]; then
|
||||
grep "; Inactive:" "${_key_file}" > /dev/null && _log_error "unexpected retired comment in ${_key_file}"
|
||||
grep "Inactive:" "${_private_file}" > /dev/null && _log_error "unexpected retired in ${_private_file}"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Inactive:" "${_private_file}" > /dev/null && _log_error "unexpected retired in ${_private_file}"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Retired: " "${_state_file}" > /dev/null && _log_error "unexpected retired in ${_state_file}"
|
||||
fi
|
||||
else
|
||||
grep "; Inactive: $_retired" "${_key_file}" > /dev/null || _log_error "mismatch retired comment in ${_key_file} (expected ${_retired})"
|
||||
grep "Inactive: $_retired" "${_private_file}" > /dev/null || _log_error "mismatch retired in ${_private_file} (expected ${_retired})"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Inactive: $_retired" "${_private_file}" > /dev/null || _log_error "mismatch retired in ${_private_file} (expected ${_retired})"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Retired: $_retired" "${_state_file}" > /dev/null || _log_error "mismatch retired in ${_state_file} (expected ${_retired})"
|
||||
fi
|
||||
@@ -515,13 +542,17 @@ check_timingmetadata() {
|
||||
|
||||
if [ "$_revoked" = "none" ]; then
|
||||
grep "; Revoke:" "${_key_file}" > /dev/null && _log_error "unexpected revoked comment in ${_key_file}"
|
||||
grep "Revoke:" "${_private_file}" > /dev/null && _log_error "unexpected revoked in ${_private_file}"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Revoke:" "${_private_file}" > /dev/null && _log_error "unexpected revoked in ${_private_file}"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Revoked: " "${_state_file}" > /dev/null && _log_error "unexpected revoked in ${_state_file}"
|
||||
fi
|
||||
else
|
||||
grep "; Revoke: $_revoked" "${_key_file}" > /dev/null || _log_error "mismatch revoked comment in ${_key_file} (expected ${_revoked})"
|
||||
grep "Revoke: $_revoked" "${_private_file}" > /dev/null || _log_error "mismatch revoked in ${_private_file} (expected ${_revoked})"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Revoke: $_revoked" "${_private_file}" > /dev/null || _log_error "mismatch revoked in ${_private_file} (expected ${_revoked})"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Revoked: $_revoked" "${_state_file}" > /dev/null || _log_error "mismatch revoked in ${_state_file} (expected ${_revoked})"
|
||||
fi
|
||||
@@ -529,13 +560,17 @@ check_timingmetadata() {
|
||||
|
||||
if [ "$_removed" = "none" ]; then
|
||||
grep "; Delete:" "${_key_file}" > /dev/null && _log_error "unexpected removed comment in ${_key_file}"
|
||||
grep "Delete:" "${_private_file}" > /dev/null && _log_error "unexpected removed in ${_private_file}"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Delete:" "${_private_file}" > /dev/null && _log_error "unexpected removed in ${_private_file}"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Removed: " "${_state_file}" > /dev/null && _log_error "unexpected removed in ${_state_file}"
|
||||
fi
|
||||
else
|
||||
grep "; Delete: $_removed" "${_key_file}" > /dev/null || _log_error "mismatch removed comment in ${_key_file} (expected ${_removed})"
|
||||
grep "Delete: $_removed" "${_private_file}" > /dev/null || _log_error "mismatch removed in ${_private_file} (expected ${_removed})"
|
||||
if [ "$_private" = "yes" ]; then
|
||||
grep "Delete: $_removed" "${_private_file}" > /dev/null || _log_error "mismatch removed in ${_private_file} (expected ${_removed})"
|
||||
fi
|
||||
if [ "$_legacy" = "no" ]; then
|
||||
grep "Removed: $_removed" "${_state_file}" > /dev/null || _log_error "mismatch removed in ${_state_file} (expected ${_removed})"
|
||||
fi
|
||||
@@ -672,7 +707,7 @@ _check_keys() {
|
||||
# Check key files.
|
||||
_ids=$(get_keyids "$DIR" "$ZONE")
|
||||
for _id in $_ids; do
|
||||
# There are three key files with the same algorithm.
|
||||
# There are multiple key files with the same algorithm.
|
||||
# Check them until a match is found.
|
||||
ret=0
|
||||
echo_i "check key id $_id"
|
||||
|
||||
@@ -252,6 +252,15 @@ zone "unfresh-sigs.autosign" {
|
||||
dnssec-policy "autosign";
|
||||
};
|
||||
|
||||
/*
|
||||
* Zone that has missing private KSK.
|
||||
*/
|
||||
zone "ksk-missing.autosign" {
|
||||
type primary;
|
||||
file "ksk-missing.autosign.db";
|
||||
dnssec-policy "autosign";
|
||||
};
|
||||
|
||||
/*
|
||||
* Zone that has missing private ZSK.
|
||||
*/
|
||||
|
||||
@@ -194,7 +194,25 @@ private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >> "$infile"
|
||||
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >> "$infile"
|
||||
$SIGNER -S -x -s now-1w -e now+1w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
|
||||
|
||||
# These signatures are already expired, and the private ZSK is missing.
|
||||
# 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=$($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)
|
||||
$SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" > settime.out.$zone.1 2>&1
|
||||
$SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" > settime.out.$zone.2 2>&1
|
||||
cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
|
||||
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >> "$infile"
|
||||
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >> "$infile"
|
||||
$SIGNER -S -x -s now-1w -e now+1w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
|
||||
echo "KSK: yes" >> "${KSK}".state
|
||||
echo "ZSK: no" >> "${KSK}".state
|
||||
echo "Lifetime: 63072000" >> "${KSK}".state # PT2Y
|
||||
rm -f "${KSK}".private
|
||||
|
||||
# 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"
|
||||
@@ -206,7 +224,10 @@ $SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" > settime.out.$zone.2 2>&1
|
||||
cat template.db.in "${KSK}.key" "${ZSK}.key" > "$infile"
|
||||
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >> "$infile"
|
||||
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >> "$infile"
|
||||
$SIGNER -PS -x -s now-2w -e now-1mi -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
|
||||
$SIGNER -S -x -s now-1w -e now+1w -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1
|
||||
echo "KSK: no" >> "${ZSK}".state
|
||||
echo "ZSK: yes" >> "${ZSK}".state
|
||||
echo "Lifetime: 31536000" >> "${ZSK}".state # PT1Y
|
||||
rm -f "${ZSK}".private
|
||||
|
||||
# These signatures are already expired, and the private ZSK is retired.
|
||||
|
||||
@@ -326,6 +326,27 @@ retry_quiet 10 update_is_signed "10.0.0.11" "10.0.0.44" || ret=1
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status+ret))
|
||||
|
||||
# Move the private key file, a rekey event should not introduce replacement
|
||||
# keys.
|
||||
ret=0
|
||||
echo_i "test that if private key files are inaccessible this doesn't trigger a rollover ($n)"
|
||||
basefile=$(key_get KEY1 BASEFILE)
|
||||
mv "${basefile}.private" "${basefile}.offline"
|
||||
rndccmd 10.53.0.3 loadkeys "$ZONE" > /dev/null || log_error "rndc loadkeys zone ${ZONE} failed"
|
||||
wait_for_log 3 "offline, policy default" $DIR/named.run || ret=1
|
||||
mv "${basefile}.offline" "${basefile}.private"
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status+ret))
|
||||
|
||||
# Nothing has changed.
|
||||
check_keys
|
||||
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
|
||||
set_keytimes_csk_policy
|
||||
check_keytimes
|
||||
check_apex
|
||||
check_subdomain
|
||||
dnssec_verify
|
||||
|
||||
#
|
||||
# Zone: dynamic.kasp
|
||||
#
|
||||
@@ -1340,6 +1361,25 @@ 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.
|
||||
#
|
||||
@@ -1347,7 +1387,25 @@ 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.
|
||||
# TODO.
|
||||
# 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.
|
||||
|
||||
Reference in New Issue
Block a user