diff options
Diffstat (limited to 'bin/tests/system/dupsigs')
-rw-r--r-- | bin/tests/system/dupsigs/check_journal.pl | 211 | ||||
-rw-r--r-- | bin/tests/system/dupsigs/clean.sh | 24 | ||||
-rw-r--r-- | bin/tests/system/dupsigs/ns1/named.args | 1 | ||||
-rw-r--r-- | bin/tests/system/dupsigs/ns1/named.conf.in | 33 | ||||
-rw-r--r-- | bin/tests/system/dupsigs/ns1/reset_keys.sh | 100 | ||||
-rw-r--r-- | bin/tests/system/dupsigs/ns1/signing.test.db.in | 18 | ||||
-rw-r--r-- | bin/tests/system/dupsigs/setup.sh | 24 | ||||
-rw-r--r-- | bin/tests/system/dupsigs/tests.sh | 70 |
8 files changed, 481 insertions, 0 deletions
diff --git a/bin/tests/system/dupsigs/check_journal.pl b/bin/tests/system/dupsigs/check_journal.pl new file mode 100644 index 0000000..99bf690 --- /dev/null +++ b/bin/tests/system/dupsigs/check_journal.pl @@ -0,0 +1,211 @@ +#!/usr/bin/env 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. + +use strict; +use warnings; + +sub process_changeset; + +my @changeset; + +while( my $line = <> ) { + chomp $line; + + if( $line =~ /^(?<op>add|del) (?<label>\S+)\s+(?<ttl>\d+)\s+IN\s+(?<rrtype>\S+)\s+(?<rdata>.*)/ ) { + my $change = { + op => $+{op}, + label => $+{label}, + ttl => $+{ttl}, + rrtype => $+{rrtype}, + rdata => $+{rdata}, + }; + + if( $change->{op} eq 'del' and $change->{rrtype} eq 'SOA' ) { + if( @changeset ) { + process_changeset( @changeset ); + @changeset = (); + } + } + + push @changeset, $change; + } + else { + die "error parsing journal data"; + } +} + +if( @changeset ) { + process_changeset( @changeset ); +} + +{ + my %rrsig_db; + my %keys; + my $apex; + + sub process_changeset { + my @changeset = @_; + + if( not $apex ) { + # the first record of the first changeset is guaranteed to be the apex + $apex = $changeset[0]{label}; + } + + my $newserial; + my %touched_rrsigs; + my %touched_keys; + + foreach my $change( @changeset ) { + if( $change->{rrtype} eq 'SOA' ) { + if( $change->{op} eq 'add' ) { + if( $change->{rdata} !~ /^\S+ \S+ (?<serial>\d+)/ ) { + die "unable to parse SOA"; + } + + $newserial = $+{serial}; + } + } + elsif( $change->{rrtype} eq 'NSEC' ) { + ; # do nothing + } + elsif( $change->{rrtype} eq 'DNSKEY' ) { + ; # ignore for now + } + elsif( $change->{rrtype} eq 'TYPE65534' and $change->{label} eq $apex ) { + # key status + if( $change->{rdata} !~ /^\\# (?<datasize>\d+) (?<data>[0-9A-F]+)$/ ) { + die "unable to parse key status record"; + } + + my $datasize = $+{datasize}; + my $data = $+{data}; + + if( $datasize == 5 ) { + my( $alg, $id, $flag_del, $flag_done ) = unpack 'CnCC', pack( 'H10', $data ); + + if( $change->{op} eq 'add' ) { + if( not exists $keys{$id} ) { + $touched_keys{$id} //= 1; + + $keys{$id} = { + $data => 1, + rrs => 1, + done_signing => $flag_done, + deleting => $flag_del, + }; + } + else { + if( not exists $keys{$id}{$data} ) { + my $keydata = $keys{$id}; + $touched_keys{$id} = { %$keydata }; + + $keydata->{rrs}++; + $keydata->{$data} = 1; + $keydata->{done_signing} += $flag_done; + $keydata->{deleting} += $flag_del; + } + } + } + else { + # this logic relies upon the convention that there won't + # ever be multiple records with the same flag set + if( exists $keys{$id} ) { + my $keydata = $keys{$id}; + + if( exists $keydata->{$data} ) { + $touched_keys{$id} = { %$keydata }; + + $keydata->{rrs}--; + delete $keydata->{$data}; + $keydata->{done_signing} -= $flag_done; + $keydata->{deleting} -= $flag_del; + + if( $keydata->{rrs} == 0 ) { + delete $keys{$id}; + } + } + } + } + } + else { + die "unexpected key status record content"; + } + } + elsif( $change->{rrtype} eq 'RRSIG' ) { + if( $change->{rdata} !~ /^(?<covers>\S+) \d+ \d+ \d+ (?<validity_end>\d+) (?<validity_start>\d+) (?<signing_key>\d+)/ ) { + die "unable to parse RRSIG rdata"; + } + + $change->{covers} = $+{covers}; + $change->{validity_end} = $+{validity_end}; + $change->{validity_start} = $+{validity_start}; + $change->{signing_key} = $+{signing_key}; + + my $db_key = $change->{label} . ':' . $change->{covers}; + + $rrsig_db{$db_key} //= {}; + $touched_rrsigs{$db_key} = 1; + + if( $change->{op} eq 'add' ) { + $rrsig_db{$db_key}{ $change->{signing_key} } = 1; + } + else { + # del + delete $rrsig_db{$db_key}{ $change->{signing_key} }; + } + } + } + + foreach my $key_id( sort keys %touched_keys ) { + my $old_data; + my $new_data; + + if( ref $touched_keys{$key_id} ) { + $old_data = $touched_keys{$key_id}; + } + + if( exists $keys{$key_id} ) { + $new_data = $keys{$key_id}; + } + + if( $old_data ) { + if( $new_data ) { + print "at serial $newserial key $key_id status changed from ($old_data->{deleting},$old_data->{done_signing}) to ($new_data->{deleting},$new_data->{done_signing})\n"; + } + else { + print "at serial $newserial key $key_id status removed from zone\n"; + } + } + else { + print "at serial $newserial key $key_id status added with flags ($new_data->{deleting},$new_data->{done_signing})\n"; + } + } + + foreach my $rrsig_id( sort keys %touched_rrsigs ) { + my $n_signing_keys = keys %{ $rrsig_db{$rrsig_id} }; + + if( $n_signing_keys == 0 ) { + print "at serial $newserial $rrsig_id went unsigned\n"; + } + elsif( $rrsig_id =~ /:DNSKEY$/ ) { + if( $n_signing_keys != 2 ) { + print "at serial $newserial $rrsig_id was signed $n_signing_keys time(s) when it should have been signed twice\n"; + } + } + elsif( $n_signing_keys > 1 ) { + my @signing_keys = sort { $a <=> $b } keys %{ $rrsig_db{$rrsig_id} }; + print "at serial $newserial $rrsig_id was signed too many times, keys (@signing_keys)\n"; + } + } + } +} diff --git a/bin/tests/system/dupsigs/clean.sh b/bin/tests/system/dupsigs/clean.sh new file mode 100644 index 0000000..2af75a0 --- /dev/null +++ b/bin/tests/system/dupsigs/clean.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# 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. + +rm -f dig.out* +rm -f ns1/named.conf +rm -f ns1/named.lock +rm -f ns1/named.memstats +rm -f ns1/named.run +rm -f ns1/signing.test.db +rm -f ns1/signing.test.db.jbk +rm -f ns1/signing.test.db.signed +rm -f ns1/signing.test.db.signed.jnl +rm -f ns1/keys/signing.test/K* +rm -f ns1/managed-keys.bind* diff --git a/bin/tests/system/dupsigs/ns1/named.args b/bin/tests/system/dupsigs/ns1/named.args new file mode 100644 index 0000000..231eed4 --- /dev/null +++ b/bin/tests/system/dupsigs/ns1/named.args @@ -0,0 +1 @@ +-D dupsigs-ns1 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4 -T maxcachesize=2097152 -T sigvalinsecs diff --git a/bin/tests/system/dupsigs/ns1/named.conf.in b/bin/tests/system/dupsigs/ns1/named.conf.in new file mode 100644 index 0000000..494ecfb --- /dev/null +++ b/bin/tests/system/dupsigs/ns1/named.conf.in @@ -0,0 +1,33 @@ +/* + * 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. + */ + +options { + recursion no; + max-journal-size unlimited; + port @PORT@; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + pid-file "named.pid"; +}; + +zone "signing.test" { + type primary; + masterfile-format text; + allow-update { any; }; + file "signing.test.db"; + update-check-ksk yes; + key-directory "keys/signing.test"; + inline-signing yes; + auto-dnssec maintain; + sig-validity-interval 20 5; +}; diff --git a/bin/tests/system/dupsigs/ns1/reset_keys.sh b/bin/tests/system/dupsigs/ns1/reset_keys.sh new file mode 100644 index 0000000..4faa0bb --- /dev/null +++ b/bin/tests/system/dupsigs/ns1/reset_keys.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +# 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. + +SYSTEMTESTTOP=../.. +. $SYSTEMTESTTOP/conf.sh + +zone=signing.test +rm -rf keys/signing.test +mkdir -p keys/signing.test + +timetodnssec() { + $PERL -e 'my ($S,$M,$H,$d,$m,$y,$x) = gmtime(@ARGV[0]); + printf("%04u%02u%02u%02u%02u%02u\n", $y+1900,$m+1,$d,$H,$M,$S);' ${1} +} + +KEYDIR=keys/signing.test +KSK=`$KEYGEN -a RSASHA256 -K $KEYDIR -q -f KSK $zone` + +ZSK0=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK1=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK2=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK3=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK4=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK5=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK6=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK7=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK8=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` +ZSK9=`$KEYGEN -a RSASHA256 -K $KEYDIR -q $zone` + +# clear all times on all keys +for FILEN in keys/signing.test/*.key +do + $SETTIME -P none -A none -R none -I none -D none $FILEN +done + +BASE=`date +%s` +BASET=`timetodnssec $BASE` + +# reset the publish and activation time on the KSK +$SETTIME -P $BASET -A $BASET $KEYDIR/$KSK + +# reset the publish and activation time on the first ZSK +$SETTIME -P $BASET -A $BASET $KEYDIR/$ZSK0 + +# schedule the first roll +R1=`expr $BASE + 50` +R1T=`timetodnssec $R1` + +$SETTIME -I $R1T $KEYDIR/$ZSK0 +$SETTIME -P $BASET -A $R1T $KEYDIR/$ZSK1 + +# schedule the second roll (which includes the delete of the first key) +R2=`expr $R1 + 50` +R2T=`timetodnssec $R2` +DT=$R2 +DTT=`timetodnssec $DT` + +$SETTIME -D $DTT $KEYDIR/$ZSK0 +$SETTIME -I $R2T $KEYDIR/$ZSK1 +$SETTIME -P $R1T -A $R2T $KEYDIR/$ZSK2 + +# schedule the third roll +R3=`expr $R2 + 25` +R3T=`timetodnssec $R3` + +$SETTIME -D $R3T $KEYDIR/$ZSK1 +$SETTIME -I $R3T $KEYDIR/$ZSK2 +$SETTIME -P $R2T -A $R3T $KEYDIR/$ZSK3 + +$SETTIME -P $R3T $KEYDIR/$ZSK4 + +echo KSK=$KSK +echo ZSK0=$ZSK0 +echo ZSK1=$ZSK1 +echo ZSK2=$ZSK2 +echo ZSK3=$ZSK3 +echo ZSK4=$ZSK4 + +exit + +# schedule the fourth roll +# this isn't long enough for the signing to complete and would result in +# duplicate signatures, see +# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/231#note_9597 +R4=`expr $R3 + 10` +R4T=`timetodnssec $R4` + +$SETTIME -D $R4T $KEYDIR/$ZSK2 +$SETTIME -I $R4T $KEYDIR/$ZSK3 +$SETTIME -P $R3T -A $R4T $KEYDIR/$ZSK4 diff --git a/bin/tests/system/dupsigs/ns1/signing.test.db.in b/bin/tests/system/dupsigs/ns1/signing.test.db.in new file mode 100644 index 0000000..b522b6f --- /dev/null +++ b/bin/tests/system/dupsigs/ns1/signing.test.db.in @@ -0,0 +1,18 @@ +; 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. + +$TTL 3600 +@ IN SOA ns root.ns 1996072700 3600 1800 86400 60 +@ NS ns +ns A 127.0.0.1 +ns AAAA ::1 + +$GENERATE 0-499 a${0,4,d} AAAA ::$ diff --git a/bin/tests/system/dupsigs/setup.sh b/bin/tests/system/dupsigs/setup.sh new file mode 100644 index 0000000..f687543 --- /dev/null +++ b/bin/tests/system/dupsigs/setup.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# 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. + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +$SHELL clean.sh + +test -r $RANDFILE || $GENRANDOM 800 $RANDFILE + +copy_setports ns1/named.conf.in ns1/named.conf + +cp -f ns1/signing.test.db.in ns1/signing.test.db +(cd ns1; $SHELL ./reset_keys.sh) diff --git a/bin/tests/system/dupsigs/tests.sh b/bin/tests/system/dupsigs/tests.sh new file mode 100644 index 0000000..dfc88ce --- /dev/null +++ b/bin/tests/system/dupsigs/tests.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# 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. + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +status=0 + +# Wait for the zone to be fully signed before beginning test +# +# We expect the zone to have the following: +# +# - 6 signatures for signing.test. +# - 3 signatures for ns.signing.test. +# - 2 x 500 signatures for a{0000-0499}.signing.test. +# +# for a total of 1009. +fully_signed () { + $DIG axfr signing.test -p ${PORT} @10.53.0.1 > "dig.out.ns1.axfr" + awk 'BEGIN { lines = 0 } + $4 == "RRSIG" {lines++} + END { if (lines != 1009) exit(1) }' < "dig.out.ns1.axfr" +} + +# Wait for the last NSEC record in the zone to be signed. This is a lightweight +# alternative to avoid many AXFR requests while waiting for the zone to be +# fully signed. +_wait_for_last_nsec_signed() { + $DIG +dnssec a0499.signing.test -p ${PORT} @10.53.0.1 nsec > "dig.out.ns1.wait" || return 1 + grep "signing.test\..*IN.*RRSIG.*signing.test" "dig.out.ns1.wait" > /dev/null || return 1 + return 0 +} + +echo_i "wait for the zone to be fully signed" +retry_quiet 60 _wait_for_last_nsec_signed +retry_quiet 10 fully_signed || status=1 +if [ $status != 0 ]; then echo_i "failed"; fi + +start=`date +%s` +now=$start +end=$((start + 140)) + +while [ $now -lt $end ] && [ $status -eq 0 ]; do + et=$((now - start)) + echo_i "............... $et ............" + $JOURNALPRINT ns1/signing.test.db.signed.jnl | $PERL check_journal.pl | cat_i + $DIG axfr signing.test -p ${PORT} @10.53.0.1 > dig.out.at$et + awk '$4 == "RRSIG" { print $11 }' dig.out.at$et | sort | uniq -c | cat_i + lines=`awk '$4 == "RRSIG" { print}' dig.out.at$et | wc -l` + if [ ${et} -ne 0 -a ${lines} -ne 1009 ] + then + echo_i "failed" + status=$((status + 1)) + fi + sleep 5 + now=`date +%s` +done + +echo_i "exit status: $status" +[ $status -eq 0 ] || exit 1 |