diff --git a/bin/tests/system/dnssec/clean.sh b/bin/tests/system/dnssec/clean.sh index 2010bada16..c49ee9a1ac 100644 --- a/bin/tests/system/dnssec/clean.sh +++ b/bin/tests/system/dnssec/clean.sh @@ -15,7 +15,7 @@ rm -f ./*/K* ./*/keyset-* ./*/dsset-* ./*/dlvset-* ./*/signedkey-* ./*/*.signed rm -f ./*/example.bk rm -f ./*/named.conf rm -f ./*/named.memstats -rm -f ./*/named.run +rm -f ./*/named.run ./*/named.run.prev rm -f ./*/named.secroots rm -f ./*/tmp* ./*/*.jnl ./*/*.bk ./*/*.jbk rm -f ./*/trusted.conf ./*/managed.conf ./*/revoked.conf @@ -49,6 +49,7 @@ rm -f ./ns2/in-addr.arpa.db rm -f ./ns2/nsec3chain-test.db rm -f ./ns2/private.secure.example.db rm -f ./ns2/single-nsec3.db +rm -f ./ns2/updatecheck-kskonly.secure.* rm -f ./ns3/secure.example.db ./ns3/*.managed.db ./ns3/*.trusted.db rm -f ./ns3/unsupported.managed.db.tmp ./ns3/unsupported.trusted.db.tmp rm -f ./ns3/auto-nsec.example.db ./ns3/auto-nsec3.example.db diff --git a/bin/tests/system/dnssec/ns2/named.conf.in b/bin/tests/system/dnssec/ns2/named.conf.in index 189e275014..7a4bcf0cdc 100644 --- a/bin/tests/system/dnssec/ns2/named.conf.in +++ b/bin/tests/system/dnssec/ns2/named.conf.in @@ -26,6 +26,15 @@ options { minimal-responses no; }; +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + zone "." { type hint; file "../../common/root.hint"; @@ -167,6 +176,18 @@ zone "cdnskey-auto.secure" { allow-update { any; }; }; +zone "updatecheck-kskonly.secure" { + type master; + auto-dnssec maintain; + key-directory "."; + dnssec-dnskey-kskonly yes; + update-check-ksk yes; + sig-validity-interval 10; + dnskey-sig-validity 40; + file "updatecheck-kskonly.secure.db.signed"; + allow-update { any; }; +}; + zone "corp" { type master; file "corp.db"; diff --git a/bin/tests/system/dnssec/ns2/sign.sh b/bin/tests/system/dnssec/ns2/sign.sh index d0a978b61c..eb2721598f 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -314,3 +314,20 @@ key1=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone -f KSK "$ key2=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") sed 's/DNSKEY/CDNSKEY/' "$key1.key" > "$key1.cds" cat "$infile" "$key1.cds" > "$zonefile.signed" + +zone=updatecheck-kskonly.secure +infile=template.secure.db.in +zonefile=${zone}.db +key1=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone -f KSK "$zone") +key2=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") +# Save key id's for checking active key usage +echo "$key1" | sed -e 's/.*[+]//' -e 's/^0*//' > $zone.ksk.id +echo "$key2" | sed -e 's/.*[+]//' -e 's/^0*//' > $zone.zsk.id +echo "${key1}" > $zone.ksk.key +echo "${key2}" > $zone.zsk.key +# Add CDS and CDNSKEY records +sed 's/DNSKEY/CDNSKEY/' "$key1.key" > "$key1.cdnskey" +"$DSFROMKEY" -C "$key1.key" > "$key1.cds" +cat "$infile" "$key1.key" "$key2.key" "$key1.cdnskey" "$key1.cds" > "$zonefile" +# Don't sign, let auto-dnssec maintain do it. +mv $zonefile "$zonefile.signed" diff --git a/bin/tests/system/dnssec/ns2/template.secure.db.in b/bin/tests/system/dnssec/ns2/template.secure.db.in new file mode 100644 index 0000000000..e42cb4a29e --- /dev/null +++ b/bin/tests/system/dnssec/ns2/template.secure.db.in @@ -0,0 +1,12 @@ +; 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 3600 +@ SOA ns2.example. . 1 3600 1200 86400 1200 +@ NS ns2.example. diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 3aa6747229..17b4ceb5ab 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -40,6 +40,26 @@ rndccmd() { "$RNDC" -c "$SYSTEMTESTTOP/common/rndc.conf" -p "$CONTROLPORT" -s "$@" } +# TODO: Move wait_for_log and loadkeys_on to conf.sh.common +wait_for_log() { + msg=$1 + file=$2 + for i in 1 2 3 4 5 6 7 8 9 10; do + nextpart "$file" | grep "$msg" > /dev/null && return + sleep 1 + done + echo_i "exceeded time limit waiting for '$msg' in $file" + ret=1 +} + +dnssec_loadkeys_on() { + nsidx=$1 + zone=$2 + nextpart ns${nsidx}/named.run > /dev/null + rndccmd 10.53.0.${nsidx} loadkeys ${zone} | sed "s/^/ns${nsidx} /" | cat_i + wait_for_log "next key event" ns${nsidx}/named.run +} + # convert private-type records to readable form showprivate () { echo "-- $* --" @@ -2653,7 +2673,7 @@ my_dig() { "$DIG" +noadd +nosea +nostat +noquest +nocomm +nocmd -p "$PORT" @10.53.0.4 "$@" } -echo_i "checking dnskey query with no data still gets put in cache ($n)" +echo_i "checking DNSKEY query with no data still gets put in cache ($n)" ret=0 firstVal=$(my_dig insecure.example. dnskey| awk '$1 != ";;" { print $2 }') sleep 1 @@ -2709,7 +2729,7 @@ do fi echo_i "sleeping ...." sleep 3 -done; +done grep "ANSWER: 3," dig.out.ns2.test$n > /dev/null || ret=1 if [ "$ret" -ne 0 ]; then echo_i "nsec3 chain generation not complete"; fi dig_with_opts +noauth +nodnssec soa nsec3chain-test @10.53.0.2 > dig.out.ns2.test$n || ret=1 @@ -3856,5 +3876,198 @@ n=$((n+1)) test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) +### +### Additional checks for when the KSK is offline. +### + +# Save some useful information +zone="updatecheck-kskonly.secure" +KSK=`cat ns2/${zone}.ksk.key` +ZSK=`cat ns2/${zone}.zsk.key` +KSK_ID=`cat ns2/${zone}.ksk.id` +ZSK_ID=`cat ns2/${zone}.zsk.id` +SECTIONS="+answer +noauthority +noadditional" +echo_i "testing zone $zone KSK=$KSK_ID ZSK=$ZSK_ID" + +# Basic checks to make sure everything is fine before the KSK is made offline. +for qtype in "DNSKEY" "CDNSKEY" "CDS" +do + echo_i "checking $qtype RRset is signed with KSK only (update-check-ksk, dnssec-ksk-only) ($n)" + ret=0 + dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone > dig.out.test$n + lines=$(awk -v qt="$qtype" '$4 == "RRSIG" && $5 == qt {print}' dig.out.test$n | wc -l) + test "$lines" -eq 1 || ret=1 + grep $KSK_ID dig.out.test$n > /dev/null || ret=1 + grep $ZSK_ID dig.out.test$n > /dev/null && ret=1 + n=$((n+1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +done + +echo_i "checking SOA RRset is signed with ZSK only (update-check-ksk and dnssec-ksk-only) ($n)" +ret=0 +dig_with_opts $SECTIONS @10.53.0.2 soa $zone > dig.out.test$n +lines=$(awk '$4 == "RRSIG" && $5 == "SOA" {print}' dig.out.test$n | wc -l) +grep $KSK_ID dig.out.test$n > /dev/null && ret=1 +grep $ZSK_ID dig.out.test$n > /dev/null || ret=1 +test "$lines" -eq 1 || ret=1 +n=$((n+1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +# Roll the ZSK. +sleep 1 +zsk2=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -K ns2 -n zone "$zone") +echo_i "new ZSK $zsk2 created for zone $zone" +echo "$zsk2" | sed -e 's/.*[+]//' -e 's/^0*//' > ns2/$zone.zsk.id2 +ZSK_ID2=`cat ns2/$zone.zsk.id2` +dnssec_loadkeys_on 2 $zone + +# Wait until new ZSK becomes active. +sleep 1 +echo_i "make ZSK $ZSK inactive and make new ZSK $zsk2 active for zone $zone" +$SETTIME -I now -K ns2 $ZSK > /dev/null +$SETTIME -A now -K ns2 $zsk2 > /dev/null +dnssec_loadkeys_on 2 $zone + +# Remove the KSK from disk. +sleep 1 +echo_i "remove the KSK $KSK for zone $zone from disk" +mv ns2/$KSK.key ns2/$KSK.key.bak +mv ns2/$KSK.private ns2/$KSK.private.bak + +# Update the zone that requires a resign of the SOA RRset. +sleep 1 +echo_i "update the zone with $zone IN TXT nsupdate added me" +( +echo zone $zone +echo server 10.53.0.2 "$PORT" +echo update add $zone. 300 in txt "nsupdate added me" +echo send +) | $NSUPDATE + +# Redo the tests now that the zone is updated and the KSK is offline. +for qtype in "DNSKEY" "CDNSKEY" "CDS" +do + echo_i "checking $qtype RRset is signed with KSK only, KSK offline (update-check-ksk, dnssec-ksk-only) ($n)" + ret=0 + dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone > dig.out.test$n + lines=$(awk -v qt="$qtype" '$4 == "RRSIG" && $5 == qt {print}' dig.out.test$n | wc -l) + test "$lines" -eq 1 || ret=1 + grep $KSK_ID dig.out.test$n > /dev/null || ret=1 + grep $ZSK_ID dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID2 dig.out.test$n > /dev/null && ret=1 + n=$((n+1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +done + +for qtype in "SOA" "TXT" +do + echo_i "checking $qtype RRset is signed with ZSK only, KSK offline (update-check-ksk and dnssec-ksk-only) ($n)" + ret=0 + dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone > dig.out.test$n + lines=$(awk -v qt="$qtype" '$4 == "RRSIG" && $5 == qt {print}' dig.out.test$n | wc -l) + grep $KSK_ID dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID2 dig.out.test$n > /dev/null || ret=1 + test "$lines" -eq 1 || ret=1 + n=$((n+1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +done + +# Put back the KSK. +sleep 1 +echo_i "put back the KSK $KSK for zone $zone from disk" +mv ns2/$KSK.key.bak ns2/$KSK.key +mv ns2/$KSK.private.bak ns2/$KSK.private + +# Roll the ZSK again. +sleep 1 +zsk3=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -K ns2 -n zone "$zone") +echo_i "new ZSK $zsk3 created for zone $zone" +echo "$zsk3" | sed -e 's/.*[+]//' -e 's/^0*//' > ns2/$zone.zsk.id3 +ZSK_ID3=`cat ns2/$zone.zsk.id3` +dnssec_loadkeys_on 2 $zone + +# Wait until new ZSK becomes active. +sleep 1 +echo_i "delete old ZSK $ZSK make ZSK $ZSK2 inactive and make new ZSK $zsk3 active for zone $zone" +$SETTIME -D now -K ns2 $ZSK > /dev/null +$SETTIME -I +5 -K ns2 $zsk2 > /dev/null +$SETTIME -A +5 -K ns2 $zsk3 > /dev/null +dnssec_loadkeys_on 2 $zone + +# Remove the KSK from disk. +sleep 1 +echo_i "remove the KSK $KSK for zone $zone from disk" +mv ns2/$KSK.key ns2/$KSK.key.bak +mv ns2/$KSK.private ns2/$KSK.private.bak + +# Update the zone that requires a resign of the SOA RRset. +sleep 1 +echo_i "update the zone with $zone IN TXT nsupdate added me again" +( +echo zone $zone +echo server 10.53.0.2 "$PORT" +echo update add $zone. 300 in txt "nsupdate added me again" +echo send +) | $NSUPDATE + +# Redo the tests now that the ZSK roll has deleted the old key. +for qtype in "DNSKEY" "CDNSKEY" "CDS" +do + echo_i "checking $qtype RRset is signed with KSK only, old ZSK deleted (update-check-ksk, dnssec-ksk-only) ($n)" + ret=0 + dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone > dig.out.test$n + lines=$(awk -v qt="$qtype" '$4 == "RRSIG" && $5 == qt {print}' dig.out.test$n | wc -l) + test "$lines" -eq 1 || ret=1 + grep $KSK_ID dig.out.test$n > /dev/null || ret=1 + grep $ZSK_ID dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID2 dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID3 dig.out.test$n > /dev/null && ret=1 + n=$((n+1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +done + +for qtype in "SOA" "TXT" +do + echo_i "checking $qtype RRset is signed with ZSK only, old ZSK deleted (update-check-ksk and dnssec-ksk-only) ($n)" + ret=0 + dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone > dig.out.test$n + lines=$(awk -v qt="$qtype" '$4 == "RRSIG" && $5 == qt {print}' dig.out.test$n | wc -l) + grep $KSK_ID dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID2 dig.out.test$n > /dev/null || ret=1 + grep $ZSK_ID3 dig.out.test$n > /dev/null && ret=1 + test "$lines" -eq 1 || ret=1 + n=$((n+1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +done + +# Wait for newest ZSK to become active. +echo_i "sleep 6 to make new ZSK $zsk3 active and ZSK $zsk2 inactive" +sleep 6 + +# Redo the tests one more time. +for qtype in "DNSKEY" "CDNSKEY" "CDS" +do + echo_i "checking $qtype RRset is signed with KSK only, new ZSK active (update-check-ksk, dnssec-ksk-only) ($n)" + ret=0 + dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone > dig.out.test$n + lines=$(awk -v qt="$qtype" '$4 == "RRSIG" && $5 == qt {print}' dig.out.test$n | wc -l) + test "$lines" -eq 1 || ret=1 + grep $KSK_ID dig.out.test$n > /dev/null || ret=1 + grep $ZSK_ID dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID2 dig.out.test$n > /dev/null && ret=1 + grep $ZSK_ID3 dig.out.test$n > /dev/null && ret=1 + n=$((n+1)) + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +done + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1