mirror of
https://github.com/isc-projects/bind9.git
synced 2026-02-20 00:10:43 -05:00
convert dnssec signing tests to python
the shell tests that queried servers to check correct signing behavior (using dnssec-signzone, dnssec-policy and nsupdate), as well as "rndc signing", private-type records, rndc zonestatus, offline keys, etc, have been moved to tests_signing.py. the minimal update test in the dnssec_update_test.pl script was also moved here and the perl script has been removed.
This commit is contained in:
parent
950df056b3
commit
4bd0213fe7
7 changed files with 615 additions and 845 deletions
|
|
@ -1,99 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# 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 https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
#
|
||||
# DNSSEC Dynamic update test suite.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# perl update_test.pl [-s server] [-p port] zone
|
||||
#
|
||||
# The server defaults to 127.0.0.1.
|
||||
# The port defaults to 53.
|
||||
#
|
||||
# Installation notes:
|
||||
#
|
||||
# This program uses the Net::DNS::Resolver module.
|
||||
# You can install it by saying
|
||||
#
|
||||
# perl -MCPAN -e "install Net::DNS"
|
||||
#
|
||||
|
||||
use Getopt::Std;
|
||||
use Net::DNS;
|
||||
use Net::DNS::Update;
|
||||
use Net::DNS::Resolver;
|
||||
|
||||
$opt_s = "127.0.0.1";
|
||||
$opt_p = 53;
|
||||
|
||||
getopt('s:p:');
|
||||
|
||||
$res = new Net::DNS::Resolver;
|
||||
$res->nameservers($opt_s);
|
||||
$res->port($opt_p);
|
||||
$res->defnames(0); # Do not append default domain.
|
||||
|
||||
@ARGV == 1 or die
|
||||
"usage: perl update_test.pl [-s server] [-p port] zone\n";
|
||||
|
||||
$zone = shift @ARGV;
|
||||
|
||||
my $failures = 0;
|
||||
|
||||
sub assert {
|
||||
my ($cond, $explanation) = @_;
|
||||
if (!$cond) {
|
||||
print "Test Failed: $explanation ***\n";
|
||||
$failures++
|
||||
}
|
||||
}
|
||||
|
||||
sub test {
|
||||
my ($expected, @records) = @_;
|
||||
|
||||
my $update = new Net::DNS::Update("$zone");
|
||||
|
||||
foreach $rec (@records) {
|
||||
$update->push(@$rec);
|
||||
}
|
||||
|
||||
$reply = $res->send($update);
|
||||
|
||||
# Did it work?
|
||||
if (defined $reply) {
|
||||
my $rcode = $reply->header->rcode;
|
||||
assert($rcode eq $expected, "expected $expected, got $rcode");
|
||||
} else {
|
||||
print "Update failed: ", $res->errorstring, "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub section {
|
||||
my ($msg) = @_;
|
||||
print "$msg\n";
|
||||
}
|
||||
|
||||
section("Add a name");
|
||||
test("NOERROR", ["update", rr_add("a.$zone 300 A 73.80.65.49")]);
|
||||
|
||||
section("Delete the name");
|
||||
test("NOERROR", ["update", rr_del("a.$zone")]);
|
||||
|
||||
if ($failures) {
|
||||
print "$failures update tests failed.\n";
|
||||
} else {
|
||||
print "All update tests successful.\n";
|
||||
}
|
||||
|
||||
exit $failures;
|
||||
|
|
@ -185,7 +185,7 @@ zone "cdnskey-auto.secure" {
|
|||
|
||||
zone "updatecheck-kskonly.secure" {
|
||||
type primary;
|
||||
file "updatecheck-kskonly.secure.db.signed";
|
||||
file "updatecheck-kskonly.secure.db";
|
||||
dnssec-policy kskonly;
|
||||
allow-update { any; };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -297,7 +297,6 @@ $SETTIME -s -g OMNIPRESENT -k OMNIPRESENT now -r OMNIPRESENT now -d RUMOURED now
|
|||
$SETTIME -s -g OMNIPRESENT -k OMNIPRESENT now -z OMNIPRESENT now $key2 >settime.out.$zone.zsk 2>&1
|
||||
# Don't sign, let dnssec-policy maintain do it.
|
||||
cat "$infile" "$key1.key" "$key2.key" >"$zonefile"
|
||||
mv $zonefile "$zonefile.signed"
|
||||
|
||||
zone=hours-vs-days
|
||||
infile=hours-vs-days.db.in
|
||||
|
|
|
|||
|
|
@ -19,62 +19,10 @@ set -e
|
|||
status=0
|
||||
n=1
|
||||
|
||||
rm -f dig.out.*
|
||||
|
||||
dig_with_opts() {
|
||||
"$DIG" +tcp +noadd +nosea +nostat +nocmd +dnssec -p "$PORT" "$@"
|
||||
}
|
||||
|
||||
dig_with_answeropts() {
|
||||
"$DIG" +noall +answer +dnssec -p "$PORT" "$@"
|
||||
}
|
||||
|
||||
delv_with_opts() {
|
||||
"$DELV" -a ns1/trusted.conf -p "$PORT" "$@"
|
||||
}
|
||||
|
||||
rndccmd() {
|
||||
"$RNDC" -c ../_common/rndc.conf -p "$CONTROLPORT" -s "$@"
|
||||
}
|
||||
|
||||
# TODO: Move loadkeys_on to conf.sh.common
|
||||
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 20 "next key event" ns${nsidx}/named.run || return 1
|
||||
}
|
||||
|
||||
# convert private-type records to readable form
|
||||
showprivate() {
|
||||
echo "-- $* --"
|
||||
dig_with_opts +nodnssec +short "@$2" -t type65534 "$1" >dig.out.$1.test$n
|
||||
cut -f3 -d' ' <dig.out.$1.$n | while read -r record; do
|
||||
# shellcheck disable=SC2016
|
||||
$PERL -e 'my $rdata = pack("H*", @ARGV[0]);
|
||||
die "invalid record" unless length($rdata) == 5 || length($rdata) == 7;
|
||||
my ($dns, $key, $remove, $complete, $alg) = unpack("CnCCn", $rdata);
|
||||
die "invalid record" unless $dns != 0;
|
||||
my $action = "signing";
|
||||
$action = "removing" if $remove;
|
||||
my $state = " (incomplete)";
|
||||
$state = " (complete)" if $complete;
|
||||
$alg = $dns if ! defined($alg);
|
||||
print ("$action: alg: $alg, key: $key$state\n");' "$record"
|
||||
done
|
||||
}
|
||||
|
||||
# check that signing records are marked as complete
|
||||
checkprivate() {
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
showprivate "$@" | grep -q incomplete || return 0
|
||||
sleep 1
|
||||
done
|
||||
echo_d "$1 signing incomplete"
|
||||
return 1
|
||||
}
|
||||
|
||||
if [ -x "${DELV}" ]; then
|
||||
ret=0
|
||||
echo_i "checking positive validation NSEC using dns_client ($n)"
|
||||
|
|
@ -286,691 +234,5 @@ if [ -x "${DELV}" ]; then
|
|||
status=$((status + ret))
|
||||
fi
|
||||
|
||||
# Run a minimal update test if possible. This is really just
|
||||
# a regression test for RT #2399; more tests should be added.
|
||||
|
||||
if $PERL -e 'use Net::DNS;' 2>/dev/null; then
|
||||
echo_i "running DNSSEC update test"
|
||||
ret=0
|
||||
{
|
||||
output=$($PERL dnssec_update_test.pl -s 10.53.0.3 -p "$PORT" dynamic.example.)
|
||||
rc=$?
|
||||
} || true
|
||||
test "$rc" -eq 0 || ret=1
|
||||
echo "$output" | cat_i
|
||||
[ $ret -eq 1 ] && status=1
|
||||
else
|
||||
echo_i "The DNSSEC update test requires the Net::DNS library." >&2
|
||||
fi
|
||||
|
||||
echo_i "checking that the NSEC3 record for the apex is properly signed when a DNSKEY is added via UPDATE ($n)"
|
||||
ret=0
|
||||
(
|
||||
kskname=$($KEYGEN -q -3 -a $DEFAULT_ALGORITHM -fk update-nsec3.example)
|
||||
(
|
||||
echo zone update-nsec3.example
|
||||
echo server 10.53.0.3 "$PORT"
|
||||
grep DNSKEY "${kskname}.key" | sed -e 's/^/update add /' -e 's/IN/300 IN/'
|
||||
echo send
|
||||
) | $NSUPDATE
|
||||
)
|
||||
dig_with_opts +dnssec a update-nsec3.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
|
||||
grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
grep "NSEC3 1 0 0 - .*" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking that signing records have been marked as complete ($n)"
|
||||
ret=0
|
||||
checkprivate dynamic.example 10.53.0.3 || ret=1
|
||||
checkprivate auto-nsec3.example 10.53.0.3 || ret=1
|
||||
checkprivate expiring.example 10.53.0.3 || ret=1
|
||||
checkprivate auto-nsec.example 10.53.0.3 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that 'rndc signing' without arguments is handled ($n)"
|
||||
ret=0
|
||||
rndccmd 10.53.0.3 signing >/dev/null 2>&1 && ret=1
|
||||
rndccmd 10.53.0.3 status >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that 'rndc signing -list' without zone is handled ($n)"
|
||||
ret=0
|
||||
rndccmd 10.53.0.3 signing -list >/dev/null 2>&1 && ret=1
|
||||
rndccmd 10.53.0.3 status >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that 'rndc signing -clear' without additional arguments is handled ($n)"
|
||||
ret=0
|
||||
rndccmd 10.53.0.3 signing -clear >/dev/null 2>&1 && ret=1
|
||||
rndccmd 10.53.0.3 status >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that 'rndc signing -clear all' without zone is handled ($n)"
|
||||
ret=0
|
||||
rndccmd 10.53.0.3 signing -clear all >/dev/null 2>&1 && ret=1
|
||||
rndccmd 10.53.0.3 status >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check rndc signing -list output ($n)"
|
||||
ret=0
|
||||
{ rndccmd 10.53.0.3 signing -list dynamic.example >signing.out.dynamic.example; } 2>&1
|
||||
grep -q "No signing records found" signing.out.dynamic.example || {
|
||||
ret=1
|
||||
sed 's/^/ns3 /' signing.out.dynamic.example | cat_i
|
||||
}
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that a split dnssec dnssec-signzone work ($n)"
|
||||
ret=0
|
||||
dig_with_opts soa split-dnssec.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
|
||||
grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
|
||||
grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that a smart split dnssec dnssec-signzone work ($n)"
|
||||
ret=0
|
||||
dig_with_opts soa split-smart.example. @10.53.0.4 >dig.out.ns4.test$n || ret=1
|
||||
grep "NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 2," dig.out.ns4.test$n >/dev/null || ret=1
|
||||
grep "flags:.* ad[ ;]" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "testing soon-to-expire RRSIGs without a replacement private key ($n)"
|
||||
ret=0
|
||||
dig_with_answeropts +nottlid expiring.example ns @10.53.0.3 | grep RRSIG >dig.out.ns3.test$n 2>&1
|
||||
# there must be a signature here
|
||||
[ -s dig.out.ns3.test$n ] || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that named doesn't loop when all private keys are not available ($n)"
|
||||
ret=0
|
||||
lines=$(grep -c "reading private key file expiring.example" ns3/named.run || true)
|
||||
test "${lines:-1000}" -lt 15 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check the correct resigning time is reported in zonestatus ($n)"
|
||||
ret=0
|
||||
rndccmd 10.53.0.3 \
|
||||
zonestatus secure.example >rndc.out.ns3.test$n
|
||||
# next resign node: secure.example/DNSKEY
|
||||
qname=$(awk '/next resign node:/ { print $4 }' rndc.out.ns3.test$n | sed 's,/.*,,')
|
||||
qtype=$(awk '/next resign node:/ { print $4 }' rndc.out.ns3.test$n | sed 's,.*/,,')
|
||||
# next resign time: Thu, 24 Apr 2014 10:38:16 GMT
|
||||
time=$(awk 'BEGIN { m["Jan"] = "01"; m["Feb"] = "02"; m["Mar"] = "03";
|
||||
m["Apr"] = "04"; m["May"] = "05"; m["Jun"] = "06";
|
||||
m["Jul"] = "07"; m["Aug"] = "08"; m["Sep"] = "09";
|
||||
m["Oct"] = "10"; m["Nov"] = "11"; m["Dec"] = "12";}
|
||||
/next resign time:/ { printf "%d%s%02d%s\n", $7, m[$6], $5, $8 }' rndc.out.ns3.test$n | sed 's/://g')
|
||||
dig_with_opts +noall +answer "$qname" "$qtype" @10.53.0.3 >dig.out.test$n
|
||||
expire=$(awk '$4 == "RRSIG" { print $9 }' dig.out.test$n)
|
||||
inception=$(awk '$4 == "RRSIG" { print $10 }' dig.out.test$n)
|
||||
$PERL -e 'exit(0) if ("'"$time"'" lt "'"$expire"'" && "'"$time"'" gt "'"$inception"'"); exit(1);' || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDS records are signed using KSK by dnssec-signzone ($n)"
|
||||
ret=0
|
||||
dig_with_opts +noall +answer @10.53.0.2 cds cds.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 2 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDS records are not signed using ZSK by dnssec-signzone -x ($n)"
|
||||
ret=0
|
||||
dig_with_opts +noall +answer @10.53.0.2 cds cds-x.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 2 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDS records are signed using KSK by with dnssec-policy ($n)"
|
||||
ret=0
|
||||
dig_with_opts +noall +answer @10.53.0.2 cds cds-auto.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that a CDS deletion record is accepted ($n)"
|
||||
ret=0
|
||||
(
|
||||
echo zone cds-update.secure
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update delete cds-update.secure CDS
|
||||
echo update add cds-update.secure 0 CDS 0 0 0 00
|
||||
echo send
|
||||
) | $NSUPDATE >nsupdate.out.test$n 2>&1
|
||||
dig_with_opts +noall +answer @10.53.0.2 cds cds-update.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "${lines:-10}" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDS" && $5 == "0" && $6 == "0" && $7 == "0" && $8 == "00" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDS records are signed only using KSK when added by nsupdate ($n)"
|
||||
ret=0
|
||||
keyid=$(cat ns2/cds-update.secure.id)
|
||||
(
|
||||
echo zone cds-update.secure
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update delete cds-update.secure CDS
|
||||
echo send
|
||||
dig_with_opts +noall +answer @10.53.0.2 dnskey cds-update.secure \
|
||||
| grep "DNSKEY.257" \
|
||||
| $DSFROMKEY -12 -C -f - -T 1 cds-update.secure \
|
||||
| sed "s/^/update add /"
|
||||
echo send
|
||||
) | $NSUPDATE
|
||||
dig_with_opts +noall +answer @10.53.0.2 cds cds-update.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk -v id="${keyid}" '$4 == "RRSIG" && $5 == "CDS" && $11 == id {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 2 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDS deletion records are signed only using KSK when added by nsupdate ($n)"
|
||||
ret=0
|
||||
keyid=$(cat ns2/cds-update.secure.id)
|
||||
(
|
||||
echo zone cds-update.secure
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update delete cds-update.secure CDS
|
||||
echo update add cds-update.secure 0 CDS 0 0 0 00
|
||||
echo send
|
||||
) | $NSUPDATE
|
||||
dig_with_opts +noall +answer @10.53.0.2 cds cds-update.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk -v id="${keyid}" '$4 == "RRSIG" && $5 == "CDS" && $11 == id {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDS" && $5 == "0" && $6 == "0" && $7 == "0" && $8 == "00" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that a non matching CDS record is accepted with a matching CDS record ($n)"
|
||||
ret=0
|
||||
(
|
||||
echo zone cds-update.secure
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update delete cds-update.secure CDS
|
||||
echo send
|
||||
dig_with_opts +noall +answer @10.53.0.2 dnskey cds-update.secure \
|
||||
| grep "DNSKEY.257" \
|
||||
| $DSFROMKEY -12 -C -f - -T 1 cds-update.secure \
|
||||
| sed "s/^/update add /"
|
||||
dig_with_opts +noall +answer @10.53.0.2 dnskey cds-update.secure \
|
||||
| grep "DNSKEY.257" | sed 's/DNSKEY.257/DNSKEY 258/' \
|
||||
| $DSFROMKEY -12 -C -A -f - -T 1 cds-update.secure \
|
||||
| sed "s/^/update add /"
|
||||
echo send
|
||||
) | $NSUPDATE
|
||||
dig_with_opts +noall +answer @10.53.0.2 cds cds-update.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDS" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 4 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDNSKEY records are signed using KSK by dnssec-signzone ($n)"
|
||||
ret=0
|
||||
dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 2 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDNSKEY records are not signed using ZSK by dnssec-signzone -x ($n)"
|
||||
ret=0
|
||||
dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey-x.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 2 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDNSKEY records are signed using KSK by with dnssec-auto ($n)"
|
||||
ret=0
|
||||
dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey-auto.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# TODO: test case for GL #1689.
|
||||
# If we allow the dnssec tools to use deprecated algorithms (such as RSAMD5)
|
||||
# we could write a test that signs a zone with supported and unsupported
|
||||
# algorithm, apply a fixed rrset order such that the unsupported algorithm
|
||||
# precedes the supported one in the DNSKEY RRset, and verify the result still
|
||||
# validates succesfully.
|
||||
|
||||
echo_i "check that a CDNSKEY deletion record is accepted ($n)"
|
||||
ret=0
|
||||
(
|
||||
echo zone cdnskey-update.secure
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update delete cdnskey-update.secure CDNSKEY
|
||||
echo update add cdnskey-update.secure 0 CDNSKEY 0 3 0 AA==
|
||||
echo send
|
||||
) | $NSUPDATE >nsupdate.out.test$n 2>&1
|
||||
dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey-update.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "${lines:-10}" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDNSKEY" && $5 == "0" && $6 == "3" && $7 == "0" && $8 == "AA==" {print}' dig.out.test$n | wc -l)
|
||||
test "${lines:-10}" -eq 1 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that CDNSKEY records are signed using KSK only when added by nsupdate ($n)"
|
||||
ret=0
|
||||
keyid=$(cat ns2/cdnskey-update.secure.id)
|
||||
(
|
||||
echo zone cdnskey-update.secure
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update delete cdnskey-update.secure CDNSKEY
|
||||
dig_with_opts +noall +answer @10.53.0.2 dnskey cdnskey-update.secure \
|
||||
| sed -n -e "s/^/update add /" -e 's/DNSKEY.257/CDNSKEY 257/p'
|
||||
echo send
|
||||
) | $NSUPDATE
|
||||
dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey-update.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk -v id="${keyid}" '$4 == "RRSIG" && $5 == "CDNSKEY" && $11 == id {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that a non matching CDNSKEY record is accepted with a matching CDNSKEY record ($n)"
|
||||
ret=0
|
||||
(
|
||||
echo zone cdnskey-update.secure
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update delete cdnskey-update.secure CDNSKEY
|
||||
dig_with_opts +noall +answer @10.53.0.2 dnskey cdnskey-update.secure \
|
||||
| sed -n -e "s/^/update add /" -e 's/DNSKEY.257/CDNSKEY 257/p'
|
||||
dig_with_opts +noall +answer @10.53.0.2 dnskey cdnskey-update.secure \
|
||||
| sed -n -e "s/^/update add /" -e 's/DNSKEY.257/CDNSKEY 258/p'
|
||||
echo send
|
||||
) | $NSUPDATE
|
||||
dig_with_opts +noall +answer @10.53.0.2 cdnskey cdnskey-update.secure >dig.out.test$n
|
||||
lines=$(awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
lines=$(awk '$4 == "CDNSKEY" {print}' dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 2 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that DNAME at apex with NSEC3 is correctly signed (dnssec-signzone) ($n)"
|
||||
ret=0
|
||||
dig_with_opts txt dname-at-apex-nsec3.example @10.53.0.3 >dig.out.ns3.test$n || ret=1
|
||||
grep "RRSIG.NSEC3 $DEFAULT_ALGORITHM_NUMBER 3 600" dig.out.ns3.test$n >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that DNSKEY and other occluded data are excluded from the delegating bitmap ($n)"
|
||||
ret=0
|
||||
dig_with_opts axfr occluded.example @10.53.0.3 >dig.out.ns3.test$n || ret=1
|
||||
grep "^delegation.occluded.example..*NSEC.*NS KEY DS RRSIG NSEC$" dig.out.ns3.test$n >/dev/null || ret=1
|
||||
grep "^delegation.occluded.example..*DNSKEY.*" dig.out.ns3.test$n >/dev/null || ret=1
|
||||
grep "^delegation.occluded.example..*AAAA.*" dig.out.ns3.test$n >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "checking DNSSEC records are occluded from ANY in an insecure zone ($n)"
|
||||
ret=0
|
||||
dig_with_opts any x.insecure.example. @10.53.0.3 >dig.out.ns3.1.test$n || ret=1
|
||||
grep "status: NOERROR" dig.out.ns3.1.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 0," dig.out.ns3.1.test$n >/dev/null || ret=1
|
||||
dig_with_opts any z.secure.example. @10.53.0.3 >dig.out.ns3.2.test$n || ret=1
|
||||
grep "status: NOERROR" dig.out.ns3.2.test$n >/dev/null || ret=1
|
||||
# A+RRSIG, NSEC+RRSIG
|
||||
grep "ANSWER: 4," dig.out.ns3.2.test$n >/dev/null || ret=1
|
||||
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"
|
||||
|
||||
# Set key state for KSK. The ZSK rollovers below assume that there is a chain
|
||||
# of trust established, so we tell named that the DS is in omnipresent state.
|
||||
$SETTIME -s -d OMNIPRESENT now -K ns2 $KSK >/dev/null
|
||||
|
||||
# Print IDs of keys used for generating RRSIG records for RRsets of type $1
|
||||
# found in dig output file $2.
|
||||
get_keys_which_signed() {
|
||||
qtype=$1
|
||||
output=$2
|
||||
# The key ID is the 11th column of the RRSIG record line.
|
||||
awk -v qt="$qtype" '$4 == "RRSIG" && $5 == qt {print $11}' <"$output"
|
||||
}
|
||||
|
||||
# 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 ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed $qtype dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$KSK_ID$" >/dev/null || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID$" >/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 ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 soa $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed "SOA" dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed "SOA" dig.out.test$n | grep "^$KSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed "SOA" dig.out.test$n | grep "^$ZSK_ID$" >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Roll the ZSK.
|
||||
zsk2=$("$KEYGEN" -q -P none -A none -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -K ns2 "$zone")
|
||||
keyfile_to_key_id "$zsk2" >ns2/$zone.zsk.id2
|
||||
ZSK_ID2=$(cat ns2/$zone.zsk.id2)
|
||||
ret=0
|
||||
echo_i "prepublish new ZSK $ZSK_ID2 for $zone ($n)"
|
||||
rndccmd 10.53.0.2 dnssec -rollover -key $ZSK_ID $zone 2>&1 | sed 's/^/ns2 /' | cat_i
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
zsk_count_equals() {
|
||||
expectedzsks=$1
|
||||
dig_with_opts @10.53.0.2 DNSKEY $zone >dig.out.test$n
|
||||
lines=$(cat dig.out.test$n | grep "DNSKEY.*256 3 13" | wc -l)
|
||||
test "$lines" -eq $expectedzsks || return 1
|
||||
}
|
||||
echo_i "check DNSKEY RRset has successor ZSK $ZSK_ID2 ($n)"
|
||||
ret=0
|
||||
# The expected number of ZSKs is 2.
|
||||
retry_quiet 5 zsk_count_equals 2 || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Make new ZSK active.
|
||||
echo_i "make ZSK $ZSK_ID inactive and make new ZSK $ZSK_ID2 active for zone $zone ($n)"
|
||||
ret=0
|
||||
$SETTIME -s -I now -K ns2 $ZSK >/dev/null
|
||||
$SETTIME -s -k OMNIPRESENT now -A now -K ns2 $zsk2 >/dev/null
|
||||
dnssec_loadkeys_on 2 $zone || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Wait for newest ZSK to become active.
|
||||
echo_i "wait until new ZSK $ZSK_ID2 active and ZSK $ZSK_ID inactive"
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
ret=0
|
||||
grep "DNSKEY $zone/$DEFAULT_ALGORITHM/$ZSK_ID2 (ZSK) is now active" ns2/named.run >/dev/null || ret=1
|
||||
grep "DNSKEY $zone/$DEFAULT_ALGORITHM/$ZSK_ID (ZSK) is now inactive" ns2/named.run >/dev/null || ret=1
|
||||
[ "$ret" -eq 0 ] && break
|
||||
sleep 1
|
||||
done
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Remove the KSK from disk.
|
||||
echo_i "remove the KSK $KSK_ID 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.
|
||||
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 ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed $qtype dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$KSK_ID$" >/dev/null || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID2$" >/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 new ZSK $ZSK_ID2 only, KSK offline ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed $qtype dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$KSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID2$" >/dev/null || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
done
|
||||
|
||||
# Put back the KSK.
|
||||
echo_i "put back the KSK $KSK_ID 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.
|
||||
zsk3=$("$KEYGEN" -q -P none -A none -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -K ns2 "$zone")
|
||||
ret=0
|
||||
keyfile_to_key_id "$zsk3" >ns2/$zone.zsk.id3
|
||||
ZSK_ID3=$(cat ns2/$zone.zsk.id3)
|
||||
echo_i "delete old ZSK $ZSK_ID, schedule ZSK $ZSK_ID2 inactive, and pre-publish ZSK $ZSK_ID3 for zone $zone ($n)"
|
||||
$SETTIME -s -k HIDDEN now -z HIDDEN now -D now -K ns2 $ZSK >/dev/null
|
||||
$SETTIME -s -k OMNIPRESENT now -z OMNIPRESENT now -K ns2 $zsk2 >/dev/null
|
||||
dnssec_loadkeys_on 2 $zone || ret=1
|
||||
rndccmd 10.53.0.2 dnssec -rollover -key $ZSK_ID2 $zone 2>&1 | sed 's/^/ns2 /' | cat_i
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Wait for newest ZSK to become published.
|
||||
echo_i "wait until new ZSK $ZSK_ID3 published"
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
ret=0
|
||||
grep "DNSKEY $zone/$DEFAULT_ALGORITHM/$ZSK_ID3 (ZSK) is now published" ns2/named.run >/dev/null || ret=1
|
||||
[ "$ret" -eq 0 ] && break
|
||||
sleep 1
|
||||
done
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Remove the KSK from disk.
|
||||
echo_i "remove the KSK $KSK_ID 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.
|
||||
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 ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed $qtype dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$KSK_ID$" >/dev/null || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID2$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID3$" >/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 $ZSK_ID2 only, old ZSK deleted ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed $qtype dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$KSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID2$" >/dev/null || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID3$" >/dev/null && ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
done
|
||||
|
||||
# Put back the KSK.
|
||||
echo_i "put back the KSK $KSK_ID for zone $zone from disk"
|
||||
mv ns2/$KSK.key.bak ns2/$KSK.key
|
||||
mv ns2/$KSK.private.bak ns2/$KSK.private
|
||||
|
||||
# Make the new ZSK (ZSK3) active.
|
||||
echo_i "make new ZSK $ZSK_ID3 active for zone $zone ($n)"
|
||||
ret=0
|
||||
$SETTIME -s -I now -K ns2 $zsk2 >/dev/null
|
||||
$SETTIME -s -k OMNIPRESENT now -A now -K ns2 $zsk3 >/dev/null
|
||||
dnssec_loadkeys_on 2 $zone || ret=1
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Wait for newest ZSK to become active.
|
||||
echo_i "wait until new ZSK $ZSK_ID3 active and ZSK $ZSK_ID2 inactive"
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
ret=0
|
||||
grep "DNSKEY $zone/$DEFAULT_ALGORITHM/$ZSK_ID3 (ZSK) is now active" ns2/named.run >/dev/null || ret=1
|
||||
grep "DNSKEY $zone/$DEFAULT_ALGORITHM/$ZSK_ID2 (ZSK) is now inactive" ns2/named.run >/dev/null || ret=1
|
||||
[ "$ret" -eq 0 ] && break
|
||||
sleep 1
|
||||
done
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Remove the KSK from disk.
|
||||
echo_i "remove the KSK $KSK_ID 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.
|
||||
echo_i "update the zone with $zone IN TXT nsupdate added me one more time"
|
||||
(
|
||||
echo zone $zone
|
||||
echo server 10.53.0.2 "$PORT"
|
||||
echo update add $zone. 300 in txt "nsupdate added me one more time"
|
||||
echo send
|
||||
) | $NSUPDATE
|
||||
n=$((n + 1))
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# 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 ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed $qtype dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$KSK_ID$" >/dev/null || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID2$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID3$" >/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 new ZSK $ZSK_ID3 only, new ZSK active ($n)"
|
||||
ret=0
|
||||
dig_with_opts $SECTIONS @10.53.0.2 $qtype $zone >dig.out.test$n
|
||||
lines=$(get_keys_which_signed $qtype dig.out.test$n | wc -l)
|
||||
test "$lines" -eq 1 || ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$KSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID2$" >/dev/null && ret=1
|
||||
get_keys_which_signed $qtype dig.out.test$n | grep "^$ZSK_ID3$" >/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
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ pytestmark = pytest.mark.extra_artifacts(
|
|||
"ns2/too-many-iterations.db",
|
||||
"ns2/inconsistent.db",
|
||||
"ns2/trusted.db",
|
||||
"ns2/updatecheck-kskonly.secure.db",
|
||||
"ns2/updatecheck-kskonly.secure.ksk.id",
|
||||
"ns2/updatecheck-kskonly.secure.ksk.key",
|
||||
"ns2/updatecheck-kskonly.secure.zsk.id",
|
||||
|
|
|
|||
613
bin/tests/system/dnssec/tests_signing.py
Normal file
613
bin/tests/system/dnssec/tests_signing.py
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# 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 https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
from collections import namedtuple
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import time
|
||||
|
||||
from dns import dnssec, name, rdataclass, rdatatype, update
|
||||
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("dns", minversion="2.0.0")
|
||||
import isctest
|
||||
|
||||
|
||||
# helper functions
|
||||
def grep_c(regex, filename):
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
blob = f.read().splitlines()
|
||||
results = [x for x in blob if re.search(regex, x)]
|
||||
return len(results)
|
||||
|
||||
|
||||
# run dnssec-keygen
|
||||
def keygen(*args):
|
||||
keygen_cmd = [os.environ.get("KEYGEN")]
|
||||
keygen_cmd.extend(args)
|
||||
return isctest.run.cmd(keygen_cmd, log_stdout=True).stdout.decode("utf-8").strip()
|
||||
|
||||
|
||||
# run dnssec-settime
|
||||
def settime(*args):
|
||||
settime_cmd = [os.environ.get("SETTIME")]
|
||||
settime_cmd.extend(args)
|
||||
return isctest.run.cmd(settime_cmd, log_stdout=True).stdout.decode("utf-8").strip()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"domain",
|
||||
[
|
||||
"auto-nsec.example",
|
||||
"auto-nsec3.example",
|
||||
],
|
||||
)
|
||||
def test_signing_complete(domain):
|
||||
PrivateType = namedtuple("PrivateType", ["alg", "key", "rem", "complete"])
|
||||
|
||||
def convert_private(rdata) -> PrivateType:
|
||||
length = len(rdata.to_wire())
|
||||
assert length in (5, 7)
|
||||
if length == 7:
|
||||
_, key, rem, complete, alg = struct.unpack(">BHBBH", rdata.to_wire())
|
||||
else:
|
||||
alg, key, rem, complete = struct.unpack(">BHBB", rdata.to_wire())
|
||||
return PrivateType(alg, key, rem, complete)
|
||||
|
||||
# query for a private type record, make sure it shows "complete"
|
||||
def check_complete():
|
||||
msg = isctest.query.create(domain, 65534)
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
assert res.answer
|
||||
for rdata in res.answer[0]:
|
||||
record = convert_private(rdata)
|
||||
assert record.complete
|
||||
return True
|
||||
|
||||
isctest.run.retry_with_timeout(check_complete, 10)
|
||||
|
||||
|
||||
def test_split_dnssec():
|
||||
# check that split-dnssec signing worked (dnssec-signzone -D)
|
||||
msg = isctest.query.create("split-dnssec.example.", "SOA")
|
||||
res1 = isctest.query.tcp(msg, "10.53.0.3")
|
||||
res2 = isctest.query.tcp(msg, "10.53.0.4")
|
||||
isctest.check.same_answer(res1, res2)
|
||||
isctest.check.noerror(res2)
|
||||
isctest.check.rr_count_eq(res2.answer, 2)
|
||||
isctest.check.adflag(res2)
|
||||
|
||||
# check that smart split-dnssec signing worked (dnssec-signzone -DS)
|
||||
msg = isctest.query.create("split-smart.example.", "SOA")
|
||||
res1 = isctest.query.tcp(msg, "10.53.0.3")
|
||||
res2 = isctest.query.tcp(msg, "10.53.0.4")
|
||||
isctest.check.same_answer(res1, res2)
|
||||
isctest.check.noerror(res2)
|
||||
isctest.check.rr_count_eq(res2.answer, 2)
|
||||
isctest.check.adflag(res2)
|
||||
|
||||
|
||||
def test_expiring_rrsig():
|
||||
# check soon-to-expire RRSIGs without a replacement private
|
||||
# key aren't deleted. this response has to have an RRSIG:
|
||||
msg = isctest.query.create("expiring.example.", "NS")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
_, sigs = res.answer
|
||||
assert sigs
|
||||
|
||||
# check that named doesn't loop when private keys are not available
|
||||
n = grep_c("reading private key file expiring.example", "ns3/named.run")
|
||||
assert n < 15
|
||||
|
||||
# check expired signatures stay place when updates are disabled
|
||||
msg = isctest.query.create("expired.example", "SOA")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
_, sigs = res.answer
|
||||
assert sigs
|
||||
|
||||
|
||||
def test_apex_signing():
|
||||
# check that DNAME at apex with NSEC3 is correctly signed
|
||||
msg = isctest.query.create("dname-at-apex-nsec3.example.", "TXT")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
sigs = [str(a) for a in res.authority if a.rdtype == rdatatype.RRSIG]
|
||||
alg = os.environ.get("DEFAULT_ALGORITHM_NUMBER")
|
||||
assert any(f"NSEC3 {alg} 3 600" in a for a in sigs)
|
||||
|
||||
|
||||
def test_occluded_data():
|
||||
# check that DNSKEY and other occluded data are excluded from
|
||||
# a delegating bitmap
|
||||
msg = isctest.query.create("occluded.example.", "AXFR")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
|
||||
n = "delegation.occluded.example."
|
||||
delegation = [r for r in res.answer if str(r.name) == n]
|
||||
assert [r for r in delegation if r.rdtype == rdatatype.DNSKEY], str(delegation)
|
||||
assert [r for r in delegation if r.rdtype == rdatatype.AAAA], str(delegation)
|
||||
nsec = [r for r in delegation if r.rdtype == rdatatype.NSEC]
|
||||
assert nsec, str(delegation)
|
||||
assert "DNSKEY" not in str(nsec[0]), str(res)
|
||||
assert "AAAA" not in str(nsec[0]), str(res)
|
||||
|
||||
# check that DNSSEC records are occluded from ANY in an insecure zone
|
||||
msg = isctest.query.create("x.extrakey.example.", "ANY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
isctest.check.noerror(res)
|
||||
isctest.check.empty_answer(res)
|
||||
msg = isctest.query.create("z.secure.example.", "ANY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
isctest.check.noerror(res)
|
||||
isctest.check.rr_count_eq(res.answer, 4) # A+RRSIG, NSEC+RRSIG
|
||||
|
||||
|
||||
def test_update_signing():
|
||||
# minimal update test: add and delete a single record
|
||||
up = update.UpdateMessage("dynamic.example.")
|
||||
up.add("a.dynamic.example.", 300, "A", "73.80.65.49")
|
||||
res = isctest.query.tcp(up, "10.53.0.3")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
up = update.UpdateMessage("dynamic.example.")
|
||||
up.delete("a.dynamic.example.")
|
||||
res = isctest.query.tcp(up, "10.53.0.3")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("a.dynamic.example", "A")
|
||||
res = isctest.query.tcp(msg, "10.53.0.4")
|
||||
isctest.check.nxdomain(res)
|
||||
isctest.check.adflag(res)
|
||||
|
||||
# check that the NSEC3 record for the apex is properly signed
|
||||
# when a DNSKEY is added via UPDATE
|
||||
key = keygen(
|
||||
"-q3fk", "-a", os.environ["DEFAULT_ALGORITHM"], "update-nsec3.example."
|
||||
)
|
||||
|
||||
with open(f"{key}.key", "r", encoding="utf-8") as f:
|
||||
dnskey = f.read().splitlines()[-1]
|
||||
dnskey = " ".join(dnskey.split()[3:])
|
||||
|
||||
up = update.UpdateMessage("update-nsec3.example.")
|
||||
up.add("update-nsec3.example.", 300, "DNSKEY", dnskey)
|
||||
res = isctest.query.tcp(up, "10.53.0.3")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("update-nsec3.example", "A")
|
||||
res = isctest.query.tcp(msg, "10.53.0.4")
|
||||
isctest.check.noerror(res)
|
||||
isctest.check.adflag(res)
|
||||
nsec3 = [str(a) for a in res.authority if a.rdtype == rdatatype.NSEC3]
|
||||
assert any("1 0 0 -" in a for a in nsec3)
|
||||
|
||||
|
||||
def test_cds_signing():
|
||||
# check that CDS records are signed using KSK+ZSK by dnssec-signzone
|
||||
msg = isctest.query.create("cds.secure.", "CDS")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cds, sigs = res.answer
|
||||
assert len(sigs) == 2
|
||||
|
||||
# check that CDS records are not signed using ZSK by dnssec-signzone -x
|
||||
msg = isctest.query.create("cds-x.secure.", "CDS")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cds, sigs = res.answer
|
||||
assert len(sigs) == 2 # there are two KSKs here
|
||||
|
||||
# check that CDS records are signed using KSK by dnssec-policy
|
||||
msg = isctest.query.create("cds-auto.secure.", "CDS")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cds, sigs = res.answer
|
||||
assert len(sigs) == 1
|
||||
|
||||
# check that CDS records are signed only using KSK when added by nsupdate
|
||||
with open("ns2/cds-update.secure.id", encoding="utf-8") as f:
|
||||
keyid = int(f.read().splitlines()[0])
|
||||
up = update.UpdateMessage("cds-update.secure.")
|
||||
up.delete("cds-update.secure.", "CDS")
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cds-update.secure.", "DNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
dnskeys, sigs = res.answer
|
||||
ksk = [a for a in dnskeys if a.flags == 257][0]
|
||||
ds = dnssec.make_ds("cds-update.secure.", ksk, 2)
|
||||
up.add("cds-update.secure.", 1, "CDS", str(ds))
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cds-update.secure.", "CDS")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cds, sig = res.answer
|
||||
assert len(cds) == 1
|
||||
assert len(sig) == 1
|
||||
assert sig[0].key_tag == keyid
|
||||
|
||||
# check that CDS deletion records are signed only using KSK when
|
||||
# added by nsupdate
|
||||
up = update.UpdateMessage("cds-update.secure.")
|
||||
up.delete("cds-update.secure.", "CDS")
|
||||
up.add("cds-update.secure.", 0, "CDS", "0 0 0 00")
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cds-update.secure.", "CDS")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cds, sig = res.answer
|
||||
assert len(cds) == 1
|
||||
assert "0 0 0 00" in str(cds[0])
|
||||
assert len(sig) == 1
|
||||
assert sig[0].key_tag == keyid
|
||||
|
||||
# check that a non-matching CDS record is accepted with a
|
||||
# matching CDS record. first, generate a DNSKEY with different flags:
|
||||
badksk = type(ksk)(
|
||||
ksk.rdclass, ksk.rdtype, ksk.flags + 1, ksk.protocol, ksk.algorithm, ksk.key
|
||||
)
|
||||
up = update.UpdateMessage("cds-update.secure.")
|
||||
badds = dnssec.make_ds("cds-update.secure.", badksk, 2)
|
||||
up.delete("cds-update.secure.", "CDS")
|
||||
up.add("cds-update.secure.", 1, "CDS", str(ds))
|
||||
up.add("cds-update.secure.", 1, "CDS", str(badds))
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cds-update.secure.", "CDS")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cds, sig = res.answer
|
||||
assert len(cds) == 2
|
||||
assert len(sig) == 1
|
||||
|
||||
|
||||
def test_cdnskey_signing():
|
||||
# check that CDNSKEY records are signed using KSK+ZSK by dnssec-signzone
|
||||
msg = isctest.query.create("cdnskey.secure.", "CDNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cdnskey, sigs = res.answer
|
||||
assert len(sigs) == 2
|
||||
|
||||
# check that CDNSKEY records are not signed using ZSK by dnssec-signzone -x
|
||||
msg = isctest.query.create("cdnskey-x.secure.", "CDNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cdnskey, sigs = res.answer
|
||||
assert len(sigs) == 2 # two KSKs here
|
||||
|
||||
# check that CDNSKEY records are signed using KSK by dnssec-policy
|
||||
msg = isctest.query.create("cdnskey-auto.secure.", "CDNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cdnskey, sigs = res.answer
|
||||
assert len(sigs) == 1
|
||||
|
||||
# check that CDNSKEY records are signed only using KSK
|
||||
# when added by nsupdate
|
||||
with open("ns2/cdnskey-update.secure.id", encoding="utf-8") as f:
|
||||
keyid = int(f.read().splitlines()[0])
|
||||
up = update.UpdateMessage("cdnskey-update.secure.")
|
||||
up.delete("cdnskey-update.secure.", "CDNSKEY")
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cdnskey-update.secure.", "DNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
dnskeys, sigs = res.answer
|
||||
ksk = [a for a in dnskeys if a.flags == 257][0]
|
||||
up.add("cdnskey-update.secure.", 1, "CDNSKEY", str(ksk))
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cdnskey-update.secure.", "CDNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cdnskey, sig = res.answer
|
||||
assert len(cdnskey) == 1
|
||||
assert len(sig) == 1
|
||||
assert sig[0].key_tag == keyid
|
||||
|
||||
# check that CDNSKEY deletion records are signed only using KSK when
|
||||
# added by nsupdate
|
||||
up = update.UpdateMessage("cdnskey-update.secure.")
|
||||
up.delete("cdnskey-update.secure.", "CDNSKEY")
|
||||
up.add("cdnskey-update.secure.", 0, "CDNSKEY", "0 3 0 AA==")
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cdnskey-update.secure.", "CDNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cdnskey, sig = res.answer
|
||||
assert len(cdnskey) == 1
|
||||
assert "0 3 0 AA==" in str(cdnskey[0])
|
||||
assert len(sig) == 1
|
||||
assert sig[0].key_tag == keyid
|
||||
|
||||
# check that a non-matching CDNSKEY record is accepted with a
|
||||
# matching CDNSKEY record. first, generate a DNSKEY with different flags:
|
||||
badksk = type(ksk)(
|
||||
ksk.rdclass, ksk.rdtype, ksk.flags + 1, ksk.protocol, ksk.algorithm, ksk.key
|
||||
)
|
||||
up = update.UpdateMessage("cdnskey-update.secure.")
|
||||
up.delete("cdnskey-update.secure.", "CDNSKEY")
|
||||
up.add("cdnskey-update.secure.", 1, "CDNSKEY", str(ksk))
|
||||
up.add("cdnskey-update.secure.", 1, "CDNSKEY", str(badksk))
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
msg = isctest.query.create("cdnskey-update.secure.", "CDNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
cdnskey, sig = res.answer
|
||||
assert len(cdnskey) == 2
|
||||
assert len(sig) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cmd",
|
||||
[
|
||||
"signing", # without arguments
|
||||
"signing -list", # without zone
|
||||
"signing -clear", # without zone
|
||||
"signing -clear all", # without zone
|
||||
],
|
||||
)
|
||||
def test_rndc_signing_except(cmd, servers):
|
||||
ns3 = servers["ns3"]
|
||||
|
||||
# check that 'rndc signing' errors are handled
|
||||
with pytest.raises(isctest.rndc.RNDCException):
|
||||
ns3.rndc(cmd, log=False)
|
||||
ns3.rndc("status", log=False)
|
||||
|
||||
|
||||
def test_rndc_signing_output(servers):
|
||||
ns3 = servers["ns3"]
|
||||
|
||||
response = ns3.rndc("signing -list dynamic.example", log=False)
|
||||
assert "No signing records found" in response
|
||||
|
||||
|
||||
def test_zonestatus_signing(servers):
|
||||
ns3 = servers["ns3"]
|
||||
# check that the correct resigning time is reported in zonestatus.
|
||||
# zonestatus reports a name/type and expecting resigning time;
|
||||
# we convert the time to seconds since epoch, look up the RRSIG
|
||||
# for the name and type, and check that the resigning time is
|
||||
# after the inception and before the expiration.
|
||||
|
||||
response = ns3.rndc("zonestatus secure.example", log=False)
|
||||
|
||||
# next resign node: secure.example/DNSKEY
|
||||
nrn = [r for r in response.splitlines() if "next resign node" in r][0]
|
||||
rdname, rdtype = nrn.split()[3].split("/")
|
||||
|
||||
# next resign time: Thu, 24 Apr 2014 10:38:16 GMT
|
||||
nrt = [r for r in response.splitlines() if "next resign time" in r][0]
|
||||
rtime = " ".join(nrt.split()[3:])
|
||||
rt = time.strptime(rtime, "%a, %d %b %Y %H:%M:%S %Z")
|
||||
when = int(time.strftime("%s", rt))
|
||||
|
||||
msg = isctest.query.create(rdname, rdtype)
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
_, sigs = res.answer
|
||||
assert sigs[0].inception < when
|
||||
assert when < sigs[0].expiration
|
||||
|
||||
|
||||
def test_offline_ksk_signing(servers):
|
||||
def getfrom(file):
|
||||
with open(file, encoding="utf-8") as f:
|
||||
return f.read().strip()
|
||||
|
||||
def getkeyid(key: str):
|
||||
m = re.match(r"K.*\+\d*\+(\d*)", key)
|
||||
return int(m.group(1))
|
||||
|
||||
def check_signing_keys(types: list[str], expect: list[str], prohibit: list[str]):
|
||||
for qtype in types:
|
||||
isctest.log.debug(f"checking signing keys for {qtype}")
|
||||
msg = isctest.query.create(zone, qtype)
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
assert res.answer, str(res)
|
||||
rrset = res.get_rrset(
|
||||
res.answer,
|
||||
name.from_text(f"{zone}."),
|
||||
rdataclass.IN,
|
||||
rdatatype.RRSIG,
|
||||
rdatatype.RdataType.make(qtype),
|
||||
)
|
||||
assert rrset, f"expected RRSIG({qtype}) missing from ANSWER" + str(res)
|
||||
keys = {rr.key_tag for rr in rrset}
|
||||
assert len(keys) == 1, str(res)
|
||||
for exp in expect:
|
||||
assert exp in keys
|
||||
for proh in prohibit:
|
||||
assert proh not in keys
|
||||
return True
|
||||
|
||||
def check_zskcount():
|
||||
msg = isctest.query.create(zone, "DNSKEY")
|
||||
res = isctest.query.tcp(msg, "10.53.0.2")
|
||||
dnskeys, _ = res.answer
|
||||
zskcount = len([rr for rr in dnskeys if rr.flags == 256])
|
||||
assert zskcount == 2, str(res)
|
||||
return True
|
||||
|
||||
def ksk_remove():
|
||||
isctest.log.info("remove the KSK from disk")
|
||||
os.rename(f"ns2/{KSK}.key", f"ns2/{KSK}.key.bak")
|
||||
os.rename(f"ns2/{KSK}.private", f"ns2/{KSK}.private.bak")
|
||||
|
||||
def ksk_recover():
|
||||
isctest.log.info("put back the KSK")
|
||||
os.rename(f"ns2/{KSK}.key.bak", f"ns2/{KSK}.key")
|
||||
os.rename(f"ns2/{KSK}.private.bak", f"ns2/{KSK}.private")
|
||||
|
||||
def loadkeys():
|
||||
pattern = re.compile(f"{zone}/IN.*next key event")
|
||||
with ns2.watch_log_from_here() as watcher:
|
||||
ns2.rndc(f"loadkeys {zone}", log=False)
|
||||
watcher.wait_for_line(pattern)
|
||||
|
||||
ksk_only_types = ["DNSKEY", "CDNSKEY", "CDS"]
|
||||
|
||||
ns2 = servers["ns2"]
|
||||
zone = "updatecheck-kskonly.secure"
|
||||
KSK = getfrom(f"ns2/{zone}.ksk.key")
|
||||
ZSK = getfrom(f"ns2/{zone}.zsk.key")
|
||||
KSKID = int(getfrom(f"ns2/{zone}.ksk.id"))
|
||||
ZSKID = int(getfrom(f"ns2/{zone}.zsk.id"))
|
||||
|
||||
# set key state for KSK. the ZSK rollovers below assume that there is a
|
||||
# chain of trust established, so we tell named that the DS is in
|
||||
# omnipresent state.
|
||||
settime("-s", "-d", "OMNIPRESENT", "now", "-Kns2", KSK)
|
||||
|
||||
isctest.log.info("check state before KSK is made offline")
|
||||
isctest.log.info("make sure certain types are signed with KSK only")
|
||||
check_signing_keys(ksk_only_types, expect=[KSKID], prohibit=[ZSKID])
|
||||
|
||||
isctest.log.info("check SOA is signed with ZSK only")
|
||||
check_signing_keys(["SOA"], expect=[ZSKID], prohibit=[KSKID])
|
||||
|
||||
isctest.log.info("roll the ZSK")
|
||||
ZSK2 = keygen(
|
||||
"-qKns2",
|
||||
"-Pnone",
|
||||
"-Anone",
|
||||
"-a",
|
||||
os.environ["DEFAULT_ALGORITHM"],
|
||||
"-b",
|
||||
os.environ["DEFAULT_BITS"],
|
||||
zone,
|
||||
)
|
||||
ZSKID2 = getkeyid(ZSK2)
|
||||
|
||||
isctest.log.info("prepublish new ZSK")
|
||||
ns2.rndc(f"dnssec -rollover -key {ZSKID} {zone}", log=False)
|
||||
isctest.run.retry_with_timeout(check_zskcount, 5)
|
||||
|
||||
isctest.log.info("make the new ZSK active")
|
||||
settime("-sKns2", "-Inow", ZSK)
|
||||
settime("-sKns2", "-Anow", "-k", "OMNIPRESENT", "now", ZSK2)
|
||||
loadkeys()
|
||||
|
||||
with ns2.watch_log_from_start() as watcher:
|
||||
watcher.wait_for_line(
|
||||
[f"{ZSKID2} (ZSK) is now active", f"{ZSKID} (ZSK) is now inactive"]
|
||||
)
|
||||
|
||||
ksk_remove()
|
||||
|
||||
isctest.log.info("update the zone, requiring a resign of the SOA RRset")
|
||||
up = update.UpdateMessage(f"{zone}.")
|
||||
up.add(f"{zone}.", 300, "TXT", "added by UPDATE")
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
isctest.log.info(
|
||||
"redo the tests now that the zone is updated and the KSK is offline"
|
||||
)
|
||||
isctest.log.info("make sure certain types are signed with KSK only")
|
||||
check_signing_keys(ksk_only_types, expect=[KSKID], prohibit=[ZSKID, ZSKID2])
|
||||
|
||||
isctest.log.info("check TXT, SOA are signed with ZSK2 only")
|
||||
|
||||
def check_txt_soa_zsk2():
|
||||
return check_signing_keys(
|
||||
["TXT", "SOA"], expect=[ZSKID2], prohibit=[KSKID, ZSKID]
|
||||
)
|
||||
|
||||
isctest.run.retry_with_timeout(check_txt_soa_zsk2, 5)
|
||||
|
||||
ksk_recover()
|
||||
|
||||
isctest.log.info("roll the ZSK again")
|
||||
ZSK3 = keygen(
|
||||
"-qKns2",
|
||||
"-Pnone",
|
||||
"-Anone",
|
||||
"-a",
|
||||
os.environ["DEFAULT_ALGORITHM"],
|
||||
"-b",
|
||||
os.environ["DEFAULT_BITS"],
|
||||
zone,
|
||||
)
|
||||
ZSKID3 = getkeyid(ZSK3)
|
||||
|
||||
isctest.log.info("delete old ZSK, schedule ZSK2 inactive, pre-publish ZSK3")
|
||||
settime("-sKns2", "-k", "HIDDEN", "now", "-z", "HIDDEN", "now", "-Dnow", ZSK)
|
||||
settime("-sKns2", "-k", "OMNIPRESENT", "now", "-z", "OMNIPRESENT", "now", ZSK2)
|
||||
loadkeys()
|
||||
ns2.rndc(f"dnssec -rollover -key {ZSKID2} {zone}", log=False)
|
||||
|
||||
with ns2.watch_log_from_start() as watcher:
|
||||
watcher.wait_for_line(f"{ZSKID3} (ZSK) is now published")
|
||||
|
||||
ksk_remove()
|
||||
|
||||
isctest.log.info("update the zone again, requiring a resign of the SOA RRset")
|
||||
up = update.UpdateMessage(f"{zone}.")
|
||||
up.add(f"{zone}.", 300, "TXT", "added by UPDATE again")
|
||||
up.add(f"{zone}.", 300, "A", "1.2.3.4")
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
isctest.log.info("redo the tests now that the ZSK roll has deleted the old key")
|
||||
|
||||
isctest.log.info("make sure certain types are signed with KSK only")
|
||||
check_signing_keys(ksk_only_types, expect=[KSKID], prohibit=[ZSKID, ZSKID2, ZSKID3])
|
||||
|
||||
isctest.log.info("check A, TXT, SOA are signed with ZSK2 only")
|
||||
|
||||
def check_a_txt_soa_zsk2():
|
||||
return check_signing_keys(
|
||||
["A", "TXT", "SOA"], expect=[ZSKID2], prohibit=[KSKID, ZSKID, ZSKID3]
|
||||
)
|
||||
|
||||
isctest.run.retry_with_timeout(check_a_txt_soa_zsk2, 5)
|
||||
|
||||
ksk_recover()
|
||||
|
||||
isctest.log.info("make ZSK3 active")
|
||||
settime("-sKns2", "-Inow", ZSK2)
|
||||
settime("-sKns2", "-k", "OMNIPRESENT", "now", "-Anow", ZSK3)
|
||||
loadkeys()
|
||||
|
||||
with ns2.watch_log_from_start() as watcher:
|
||||
watcher.wait_for_line(
|
||||
[f"{ZSKID3} (ZSK) is now active", f"{ZSKID2} (ZSK) is now inactive"]
|
||||
)
|
||||
|
||||
ksk_remove()
|
||||
|
||||
isctest.log.info("update the zone again, requiring a resign of the SOA RRset")
|
||||
up = update.UpdateMessage(f"{zone}.")
|
||||
up.add(f"{zone}.", 300, "TXT", "added by UPDATE one more time")
|
||||
up.add(f"{zone}.", 300, "A", "4.3.2.1")
|
||||
up.add(f"{zone}.", 300, "AAAA", "dead::beef")
|
||||
res = isctest.query.tcp(up, "10.53.0.2")
|
||||
isctest.check.noerror(res)
|
||||
|
||||
isctest.log.info("redo the tests one last time")
|
||||
isctest.log.info("make sure certain types are signed with KSK only")
|
||||
check_signing_keys(ksk_only_types, expect=[KSKID], prohibit=[ZSKID, ZSKID2, ZSKID3])
|
||||
|
||||
isctest.log.info("check A, TXT, SOA are signed with ZSK2 only")
|
||||
|
||||
def check_aaaa_a_txt_soa_zsk3():
|
||||
return check_signing_keys(
|
||||
["AAAA", "A", "TXT", "SOA"],
|
||||
expect=[ZSKID3],
|
||||
prohibit=[KSKID, ZSKID, ZSKID2],
|
||||
)
|
||||
|
||||
isctest.run.retry_with_timeout(check_aaaa_a_txt_soa_zsk3, 5)
|
||||
|
|
@ -1084,12 +1084,6 @@ def test_validating_forwarder(servers):
|
|||
|
||||
|
||||
def test_expired_signatures(servers):
|
||||
# check expired signatures are still in place when updates are disabled
|
||||
msg = isctest.query.create("expired.example", "SOA")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
soa, sigs = res.answer
|
||||
assert sigs
|
||||
|
||||
# check expired signatures do not validate
|
||||
msg = isctest.query.create("expired.example", "SOA")
|
||||
res = isctest.query.tcp(msg, "10.53.0.3")
|
||||
|
|
|
|||
Loading…
Reference in a new issue