diff --git a/bin/tests/system/Makefile.am b/bin/tests/system/Makefile.am index 0a3bfc090b..d849ced799 100644 --- a/bin/tests/system/Makefile.am +++ b/bin/tests/system/Makefile.am @@ -210,7 +210,7 @@ if HAVE_PYTHON TESTS += kasp keymgr2kasp tcp pipelined if HAVE_PYMOD_DNS -TESTS += qmin cookie timeouts +TESTS += checkds qmin cookie timeouts if HAVE_PERLMOD_NET_DNS TESTS += dnssec diff --git a/bin/tests/system/checkds/README b/bin/tests/system/checkds/README new file mode 100644 index 0000000000..4c426cb45d --- /dev/null +++ b/bin/tests/system/checkds/README @@ -0,0 +1,19 @@ +Copyright (C) Internet Systems Consortium, Inc. ("ISC") + +See COPYRIGHT in the source root or https://isc.org/copyright.html for terms. + +The test setup for the checkds tests. + +These servers are parent servers: +- ns2 is a primary authoritative server that serves the parent zone for zones + configured in ns9. +- ns4 is the secondary server for ns2. +- ns5 is a primary authoritative server that serves the parent zone for zones + configured in ns9, but this one does not publish DS records (to test cases + where the DS is missing). +- ns6 is an authoritative server for a different zone, to test badly configured + parental agents. +- ns7 is the secondary server for ns5. + +Finally, ns9 is the authoritative server for the various DNSSEC enabled test +domains. diff --git a/bin/tests/system/checkds/clean.sh b/bin/tests/system/checkds/clean.sh new file mode 100644 index 0000000000..99bafb9829 --- /dev/null +++ b/bin/tests/system/checkds/clean.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +set -e + +rm -f dig.out* +rm -f ns*/named.conf ns*/named.memstats ns*/named.run* +rm -f ns*/*.jnl ns*/*.jbk +rm -f ns*/K*.private ns*/K*.key ns*/K*.state +rm -f ns*/dsset-* +rm -f ns*/*.db ns*/*.jnl ns*/*.jbk ns*/*.db.signed ns*/*.db.infile +rm -f ns*/keygen.out.* ns*/settime.out.* ns*/signer.out.* +rm -f ns*/managed-keys.bind* +rm -f ns*/*.mkeys +rm -f ns*/zones +rm -f tests-checkds.py.status +rm -f *.checkds.out diff --git a/bin/tests/system/checkds/conftest.py b/bin/tests/system/checkds/conftest.py new file mode 100644 index 0000000000..a491ff435a --- /dev/null +++ b/bin/tests/system/checkds/conftest.py @@ -0,0 +1,71 @@ +############################################################################ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. +############################################################################ + +import os +import pytest + + +def pytest_configure(config): + config.addinivalue_line( + "markers", "dnspython: mark tests that need dnspython to function" + ) + config.addinivalue_line( + "markers", "dnspython2: mark tests that need dnspython >= 2.0.0" + ) + + +def pytest_collection_modifyitems(config, items): + # pylint: disable=unused-argument,unused-import,too-many-branches + # pylint: disable=import-outside-toplevel + + # Test for dnspython module + skip_dnspython = pytest.mark.skip( + reason="need dnspython module to run") + try: + import dns.query # noqa: F401 + except ModuleNotFoundError: + for item in items: + if "dnspython" in item.keywords: + item.add_marker(skip_dnspython) + + # Test for dnspython >= 2.0.0 module + skip_dnspython2 = pytest.mark.skip( + reason="need dnspython >= 2.0.0 module to run") + try: + from dns.query import udp_with_fallback # noqa: F401 + except ImportError: + for item in items: + if "dnspython2" in item.keywords: + item.add_marker(skip_dnspython2) + + +@pytest.fixture +def named_port(request): + # pylint: disable=unused-argument + port = os.getenv("PORT") + if port is None: + port = 5301 + else: + port = int(port) + + return port + + +@pytest.fixture +def control_port(request): + # pylint: disable=unused-argument + port = os.getenv("CONTROLPORT") + if port is None: + port = 5301 + else: + port = int(port) + + return port diff --git a/bin/tests/system/checkds/ns2/named.conf.in b/bin/tests/system/checkds/ns2/named.conf.in new file mode 100644 index 0000000000..c22e27987b --- /dev/null +++ b/bin/tests/system/checkds/ns2/named.conf.in @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// NS2 + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion 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"; +}; + +zone "checkds" { + type primary; + file "checkds.db"; +}; diff --git a/bin/tests/system/checkds/ns2/setup.sh b/bin/tests/system/checkds/ns2/setup.sh new file mode 100644 index 0000000000..7fb586afee --- /dev/null +++ b/bin/tests/system/checkds/ns2/setup.sh @@ -0,0 +1,42 @@ +#!/bin/sh -e +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +echo_i "ns2/setup.sh" + +for subdomain in dspublished reference missing-dspublished bad-dspublished \ + multiple-dspublished incomplete-dspublished bad2-dspublished \ + dswithdrawn missing-dswithdrawn bad-dswithdrawn \ + multiple-dswithdrawn incomplete-dswithdrawn bad2-dswithdrawn +do + cp "../ns9/dsset-$subdomain.checkds$TP" . +done + +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + +zone="checkds" +infile="checkds.db.infile" +zonefile="checkds.db" + +CSK=$($KEYGEN -k default $zone 2> keygen.out.$zone) +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" +$SIGNER -S -g -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone 2>&1 diff --git a/bin/tests/system/checkds/ns2/template.db.in b/bin/tests/system/checkds/ns2/template.db.in new file mode 100644 index 0000000000..e9e66f20ca --- /dev/null +++ b/bin/tests/system/checkds/ns2/template.db.in @@ -0,0 +1,36 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA secondary.example. hostmaster.example. ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns2 +ns2 A 10.53.0.2 + +dspublished NS ns9.dspublished +reference NS ns9.reference +missing-dspublished NS ns9.missing-dspublished +bad-dspublished NS ns9.bad-dspublished +multiple-dspublished NS ns9.multiple-dspublished +incomplete-dspublished NS ns9.incomplete-dspublished +bad2-dspublished NS ns9.bad2-dspublished + +dswithdrawn NS ns9.dswithdrawn +missing-dswithdrawn NS ns9.missing-dswithdrawn +bad-dswithdrawn NS ns9.bad-dswithdrawn +multiple-dswithdrawn NS ns9.multiple-dswithdrawn +incomplete-dswithdrawn NS ns9.incomplete-dswithdrawn +bad2-dswithdrawn NS ns9.bad2-dswithdrawn + diff --git a/bin/tests/system/checkds/ns4/named.conf.in b/bin/tests/system/checkds/ns4/named.conf.in new file mode 100644 index 0000000000..6de6692597 --- /dev/null +++ b/bin/tests/system/checkds/ns4/named.conf.in @@ -0,0 +1,39 @@ +/* + * 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. + */ + +// NS4 + +options { + query-source address 10.53.0.4; + notify-source 10.53.0.4; + transfer-source 10.53.0.4; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.4; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "checkds" { + type secondary; + file "checkds.db"; + primaries { 10.53.0.2 port @PORT@; }; +}; diff --git a/bin/tests/system/checkds/ns5/named.conf.in b/bin/tests/system/checkds/ns5/named.conf.in new file mode 100644 index 0000000000..8b2e4b15ed --- /dev/null +++ b/bin/tests/system/checkds/ns5/named.conf.in @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// NS5 + +options { + query-source address 10.53.0.5; + notify-source 10.53.0.5; + transfer-source 10.53.0.5; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.5; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "checkds" { + type primary; + file "checkds.db"; +}; diff --git a/bin/tests/system/checkds/ns5/setup.sh b/bin/tests/system/checkds/ns5/setup.sh new file mode 100644 index 0000000000..100cd5dbec --- /dev/null +++ b/bin/tests/system/checkds/ns5/setup.sh @@ -0,0 +1,34 @@ +#!/bin/sh -e +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +echo_i "ns5/setup.sh" + +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + +zone="checkds" +infile="checkds.db.infile" +zonefile="checkds.db" + +CSK=$($KEYGEN -k default $zone 2> keygen.out.$zone) +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" +$SIGNER -S -g -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone 2>&1 diff --git a/bin/tests/system/checkds/ns5/template.db.in b/bin/tests/system/checkds/ns5/template.db.in new file mode 100644 index 0000000000..5ff796a102 --- /dev/null +++ b/bin/tests/system/checkds/ns5/template.db.in @@ -0,0 +1,36 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA secondary.example. hostmaster.example. ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns5 +ns5 A 10.53.0.5 + +dspublished NS ns9.dspublished +reference NS ns9.reference +missing-dspublished NS ns9.missing-dspublished +bad-dspublished NS ns9.bad-dspublished +multiple-dspublished NS ns9.multiple-dspublished +incomplete-dspublished NS ns9.incomplete-dspublished +bad2-dspublished NS ns9.bad2-dspublished + +dswithdrawn NS ns9.dswithdrawn +missing-dswithdrawn NS ns9.missing-dswithdrawn +bad-dswithdrawn NS ns9.bad-dswithdrawn +multiple-dswithdrawn NS ns9.multiple-dswithdrawn +incomplete-dswithdrawn NS ns9.incomplete-dswithdrawn +bad2-dswithdrawn NS ns9.bad2-dswithdrawn + diff --git a/bin/tests/system/checkds/ns6/named.conf.in b/bin/tests/system/checkds/ns6/named.conf.in new file mode 100644 index 0000000000..d9d1a1d8dd --- /dev/null +++ b/bin/tests/system/checkds/ns6/named.conf.in @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// NS2 + +options { + query-source address 10.53.0.6; + notify-source 10.53.0.6; + transfer-source 10.53.0.6; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.6; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "foo" { + type primary; + file "foo.db"; +}; diff --git a/bin/tests/system/checkds/ns7/named.conf.in b/bin/tests/system/checkds/ns7/named.conf.in new file mode 100644 index 0000000000..3bd74690cd --- /dev/null +++ b/bin/tests/system/checkds/ns7/named.conf.in @@ -0,0 +1,44 @@ +/* + * 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. + */ + +// NS7 + +options { + query-source address 10.53.0.7; + notify-source 10.53.0.7; + transfer-source 10.53.0.7; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.7; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "checkds" { + type secondary; + file "checkds.db"; + primaries { 10.53.0.5 port @PORT@; }; +}; diff --git a/bin/tests/system/checkds/ns9/named.conf.in b/bin/tests/system/checkds/ns9/named.conf.in new file mode 100644 index 0000000000..7252d466c8 --- /dev/null +++ b/bin/tests/system/checkds/ns9/named.conf.in @@ -0,0 +1,193 @@ + +// NS9 + +options { + query-source address 10.53.0.9; + notify-source 10.53.0.9; + transfer-source 10.53.0.9; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.9; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.9 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +parental-agents "ns2" port @PORT@ { + 10.53.0.2; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +/* + * Zone with parental agent configured, due for DS checking. + */ +zone "dspublished.checkds" { + type primary; + file "dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { 10.53.0.2 port @PORT@; }; +}; + +/* + * Zone with parental agent configured, due for DS checking. + * Same as above, but now with a reference to parental-agents. + */ +zone "reference.checkds" { + type primary; + file "reference.checkds.db"; + dnssec-policy "default"; + parental-agents { "ns2"; }; +}; + +/* + * Zone with parental agent configured, due for DS checking. + * The parental agent does not have the DS yet. + */ +zone "missing-dspublished.checkds" { + type primary; + file "missing-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.5 port @PORT@; // missing + }; +}; + + +/* + * Zone with parental agent configured, due for DS checking. + * This case, the server is badly configured. + */ +zone "bad-dspublished.checkds" { + type primary; + file "bad-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.6 port @PORT@; // bad + }; +}; + +/* + * Zone with multiple parental agents configured, due for DS checking. + * All need to have the DS before the rollover may continue. + */ +zone "multiple-dspublished.checkds" { + type primary; + file "multiple-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.2 port @PORT@; + 10.53.0.4 port @PORT@; + }; +}; + +/* + * Zone with multiple parental agents configured, due for DS checking. + * All need to have the DS before the rollover may continue. + * This case, one server is still missing the DS. + */ +zone "incomplete-dspublished.checkds" { + type primary; + file "incomplete-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.2 port @PORT@; + 10.53.0.4 port @PORT@; + 10.53.0.5 port @PORT@; // missing + }; +}; + + +/* + * Zone with multiple parental agents configured, due for DS checking. + * All need to have the DS before the rollover may continue. + * This case, one server is badly configured. + */ +zone "bad2-dspublished.checkds" { + type primary; + file "bad2-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.2 port @PORT@; + 10.53.0.4 port @PORT@; + 10.53.0.6 port @PORT@; // bad + }; +}; + +// TODO: Other test cases: +// - Test with bogus response +// - check with TSIG +// - check with TLS + + +/* + * Zones that are going insecure (test DS withdrawn polling). + */ +zone "dswithdrawn.checkds" { + type primary; + file "dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { 10.53.0.5 port @PORT@; }; +}; + +zone "missing-dswithdrawn.checkds" { + type primary; + file "missing-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.2 port @PORT@; // still published + }; +}; + +zone "bad-dswithdrawn.checkds" { + type primary; + file "bad-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.6 port @PORT@; // bad + }; +}; + +zone "multiple-dswithdrawn.checkds" { + type primary; + file "multiple-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.5 port @PORT@; + 10.53.0.7 port @PORT@; + }; +}; + +zone "incomplete-dswithdrawn.checkds" { + type primary; + file "incomplete-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.2 port @PORT@; // still published + 10.53.0.5 port @PORT@; + 10.53.0.7 port @PORT@; + }; +}; + +zone "bad2-dswithdrawn.checkds" { + type primary; + file "bad2-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.5 port @PORT@; + 10.53.0.7 port @PORT@; + 10.53.0.6 port @PORT@; // bad + }; +}; diff --git a/bin/tests/system/checkds/ns9/setup.sh b/bin/tests/system/checkds/ns9/setup.sh new file mode 100644 index 0000000000..efb4a1e2c2 --- /dev/null +++ b/bin/tests/system/checkds/ns9/setup.sh @@ -0,0 +1,69 @@ +#!/bin/sh -e +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +echo_i "ns9/setup.sh" + +setup() { + zone="$1" + echo_i "setting up zone: $zone" + zonefile="${zone}.db" + infile="${zone}.db.infile" + echo "$zone" >> zones +} + +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + +# Short environment variable names for key states and times. +H="HIDDEN" +R="RUMOURED" +O="OMNIPRESENT" +U="UNRETENTIVE" +T="now-30d" +Y="now-1y" + +# DS Publication. +for zn in dspublished reference missing-dspublished bad-dspublished \ + multiple-dspublished incomplete-dspublished bad2-dspublished +do + setup "${zn}.checkds" + cp template.db.in "$zonefile" + keytimes="-P $T -P sync $T -A $T" + CSK=$($KEYGEN -k default $keytimes $zone 2> keygen.out.$zone) + $SETTIME -s -g $O -k $O $T -r $O $T -z $O $T -d $R $T "$CSK" > settime.out.$zone 2>&1 + cat template.db.in "${CSK}.key" > "$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" + $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 +done + +# DS Withdrawal. +for zn in dswithdrawn missing-dswithdrawn bad-dswithdrawn multiple-dswithdrawn \ + incomplete-dswithdrawn bad2-dswithdrawn +do + setup "${zn}.checkds" + cp template.db.in "$zonefile" + keytimes="-P $Y -P sync $Y -A $Y" + CSK=$($KEYGEN -k default $keytimes $zone 2> keygen.out.$zone) + $SETTIME -s -g $H -k $O $T -r $O $T -z $O $T -d $U $T "$CSK" > settime.out.$zone 2>&1 + cat template.db.in "${CSK}.key" > "$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" + $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/checkds/ns9/template.db.in b/bin/tests/system/checkds/ns9/template.db.in new file mode 100644 index 0000000000..36404a903c --- /dev/null +++ b/bin/tests/system/checkds/ns9/template.db.in @@ -0,0 +1,25 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns9 +ns9 A 10.53.0.9 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 + diff --git a/bin/tests/system/checkds/setup.sh b/bin/tests/system/checkds/setup.sh new file mode 100644 index 0000000000..2261e15745 --- /dev/null +++ b/bin/tests/system/checkds/setup.sh @@ -0,0 +1,38 @@ +#!/bin/sh -e +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../conf.sh + +set -e + +$SHELL clean.sh + +copy_setports ns2/named.conf.in ns2/named.conf +copy_setports ns4/named.conf.in ns4/named.conf +copy_setports ns5/named.conf.in ns5/named.conf +copy_setports ns6/named.conf.in ns6/named.conf +copy_setports ns7/named.conf.in ns7/named.conf +copy_setports ns9/named.conf.in ns9/named.conf + +# Setup zones +( + cd ns9 + $SHELL setup.sh +) +( + cd ns5 + $SHELL setup.sh +) +( + cd ns2 + $SHELL setup.sh +) diff --git a/bin/tests/system/checkds/tests-checkds.py b/bin/tests/system/checkds/tests-checkds.py new file mode 100755 index 0000000000..1c60c93309 --- /dev/null +++ b/bin/tests/system/checkds/tests-checkds.py @@ -0,0 +1,376 @@ +#!/usr/bin/python3 +############################################################################ +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. +############################################################################ + +import mmap +import os +import subprocess +import sys +import time + +import dns.resolver +import pytest + + +def has_signed_apex_nsec(zone, response): + has_nsec = False + has_rrsig = False + + ttl = 300 + nextname = "a." + types = "NS SOA RRSIG NSEC DNSKEY CDS CDNSKEY" + match = "{0} {1} IN NSEC {2}{0} {3}".format(zone, ttl, nextname, types) + sig = "{0} {1} IN RRSIG NSEC 13 2 300".format(zone, ttl) + + for rr in response.answer: + if match in rr.to_text(): + has_nsec = True + if sig in rr.to_text(): + has_rrsig = True + + if not has_nsec: + print("error: missing apex NSEC record in response") + if not has_rrsig: + print("error: missing NSEC signature in response") + + return has_nsec and has_rrsig + + +def do_query(server, qname, qtype, tcp=False): + query = dns.message.make_query(qname, qtype, use_edns=True, + want_dnssec=True) + try: + if tcp: + response = dns.query.tcp(query, server.nameservers[0], timeout=3, + port=server.port) + else: + response = dns.query.udp(query, server.nameservers[0], timeout=3, + port=server.port) + except dns.exception.Timeout: + print("error: query timeout for query {} {} to {}".format( + qname, qtype, server.nameservers[0])) + return None + + return response + + +def verify_zone(zone, transfer): + verify = os.getenv("VERIFY") + assert verify is not None + + filename = "{}out".format(zone) + with open(filename, 'w') as file: + for rr in transfer.answer: + file.write(rr.to_text()) + file.write('\n') + + # dnssec-verify command with default arguments. + verify_cmd = [verify, "-z", "-o", zone, filename] + + verifier = subprocess.run(verify_cmd, capture_output=True, check=True) + + if verifier.returncode != 0: + print("error: dnssec-verify {} failed".format(zone)) + sys.stderr.buffer.write(verifier.stderr) + + return verifier.returncode == 0 + + +def read_statefile(server, zone): + addr = server.nameservers[0] + count = 0 + keyid = 0 + state = {} + + response = do_query(server, zone, "DS", tcp=True) + if not isinstance(response, dns.message.Message): + print("error: no response for {} DS from {}".format(zone, addr)) + return {} + + if response.rcode() == dns.rcode.NOERROR: + # fetch key id from response. + for rr in response.answer: + if rr.match(dns.name.from_text(zone), dns.rdataclass.IN, + dns.rdatatype.DS, dns.rdatatype.NONE): + if count == 0: + keyid = list(dict(rr.items).items())[0][0].key_tag + count += 1 + + if count != 1: + print("error: expected a single DS in response for {} from {}," + "got {}".format(zone, addr, count)) + return {} + else: + print("error: {} response for {} DNSKEY from {}".format( + dns.rcode.to_text(response.rcode()), zone, addr)) + return {} + + filename = "ns9/K{}+013+{:05d}.state".format(zone, keyid) + print("read state file {}".format(filename)) + + try: + with open(filename, 'r') as file: + for line in file: + if line.startswith(';'): + continue + key, val = line.strip().split(':', 1) + state[key.strip()] = val.strip() + + except FileNotFoundError: + # file may not be written just yet. + return {} + + return state + + +def zone_check(server, zone): + addr = server.nameservers[0] + + # wait until zone is fully signed. + signed = False + for _ in range(10): + response = do_query(server, zone, 'NSEC') + if not isinstance(response, dns.message.Message): + print("error: no response for {} NSEC from {}".format(zone, addr)) + elif response.rcode() == dns.rcode.NOERROR: + signed = has_signed_apex_nsec(zone, response) + else: + print("error: {} response for {} NSEC from {}".format( + dns.rcode.to_text(response.rcode()), zone, addr)) + + if signed: + break + + time.sleep(1) + + assert signed + + # check if zone if DNSSEC valid. + verified = False + transfer = do_query(server, zone, 'AXFR', tcp=True) + if not isinstance(transfer, dns.message.Message): + print("error: no response for {} AXFR from {}".format(zone, addr)) + elif transfer.rcode() == dns.rcode.NOERROR: + verified = verify_zone(zone, transfer) + else: + print("error: {} response for {} AXFR from {}".format( + dns.rcode.to_text(transfer.rcode()), zone, addr)) + + assert verified + + +def keystate_check(server, zone, key): + val = 0 + deny = False + + search = key + if key.startswith('!'): + deny = True + search = key[1:] + + for _ in range(10): + state = read_statefile(server, zone) + try: + val = state[search] + except KeyError: + pass + + if not deny and val != 0: + break + if deny and val == 0: + break + + time.sleep(1) + + if deny: + assert val == 0 + else: + assert val != 0 + + +def wait_for_log(filename, log): + found = False + + for _ in range(10): + print("read log file {}".format(filename)) + + try: + with open(filename, 'r') as file: + s = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) + if s.find(bytes(log, "ascii")) != -1: + found = True + except FileNotFoundError: + print("file not found {}".format(filename)) + + if found: + break + + print("sleep") + time.sleep(1) + + assert found + + +@pytest.mark.dnspython +@pytest.mark.dnspython2 +def test_checkds_dspublished(named_port): + # We create resolver instances that will be used to send queries. + server = dns.resolver.Resolver() + server.nameservers = ["10.53.0.9"] + server.port = named_port + + parent = dns.resolver.Resolver() + parent.nameservers = ["10.53.0.2"] + parent.port = named_port + + # DS correctly published in parent. + zone_check(server, "dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + keystate_check(parent, "dspublished.checkds.", "DSPublish") + + # DS correctly published in parent (reference to parental-agent). + zone_check(server, "reference.checkds.") + wait_for_log("ns9/named.run", + "zone reference.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + keystate_check(parent, "reference.checkds.", "DSPublish") + + # DS not published in parent. + zone_check(server, "missing-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone missing-dspublished.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + keystate_check(parent, "missing-dspublished.checkds.", "!DSPublish") + + # Badly configured parent. + zone_check(server, "bad-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone bad-dspublished.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad-dspublished.checkds.", "!DSPublish") + + # TBD: DS published in parent, but bogus signature. + + # DS correctly published in all parents. + zone_check(server, "multiple-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone multiple-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone multiple-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.4") + keystate_check(parent, "multiple-dspublished.checkds.", "DSPublish") + + # DS published in only one of multiple parents. + zone_check(server, "incomplete-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone incomplete-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone incomplete-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.4") + wait_for_log("ns9/named.run", + "zone incomplete-dspublished.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + keystate_check(parent, "incomplete-dspublished.checkds.", "!DSPublish") + + # One of the parents is badly configured. + wait_for_log("ns9/named.run", + "zone bad2-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone bad2-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.4") + wait_for_log("ns9/named.run", + "zone bad2-dspublished.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad2-dspublished.checkds.", "!DSPublish") + + # TBD: DS published in all parents, but one has bogus signature. + + # TBD: Check with TSIG + + # TBD: Check with TLS + + +@pytest.mark.dnspython +@pytest.mark.dnspython2 +def test_checkds_dswithdrawn(named_port): + # We create resolver instances that will be used to send queries. + server = dns.resolver.Resolver() + server.nameservers = ["10.53.0.9"] + server.port = named_port + + parent = dns.resolver.Resolver() + parent.nameservers = ["10.53.0.2"] + parent.port = named_port + + # DS correctly published in single parent. + zone_check(server, "dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + keystate_check(parent, "dswithdrawn.checkds.", "DSRemoved") + + # DS not withdrawn from parent. + zone_check(server, "missing-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone missing-dswithdrawn.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + keystate_check(parent, "missing-dswithdrawn.checkds.", "!DSRemoved") + + # Badly configured parent. + zone_check(server, "bad-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone bad-dswithdrawn.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad-dswithdrawn.checkds.", "!DSRemoved") + + # TBD: DS published in parent, but bogus signature. + + # DS correctly withdrawn from all parents. + zone_check(server, "multiple-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone multiple-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + wait_for_log("ns9/named.run", + "zone multiple-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.7") + keystate_check(parent, "multiple-dswithdrawn.checkds.", "DSRemoved") + + # DS withdrawn from only one of multiple parents. + zone_check(server, "incomplete-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone incomplete-dswithdrawn.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone incomplete-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + wait_for_log("ns9/named.run", + "zone incomplete-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.7") + keystate_check(parent, "incomplete-dswithdrawn.checkds.", "!DSRemoved") + + # One of the parents is badly configured. + wait_for_log("ns9/named.run", + "zone bad2-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + wait_for_log("ns9/named.run", + "zone bad2-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.7") + wait_for_log("ns9/named.run", + "zone bad2-dswithdrawn.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad2-dswithdrawn.checkds.", "!DSRemoved") + + # TBD: DS withdrawn from all parents, but one has bogus signature. diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 699edba607..d6773c174d 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -82,6 +82,7 @@ SEQUENTIALDIRS="$SEQUENTIAL_COMMON $SEQUENTIAL_UNIX" PARALLEL_UNIX="@DNSTAP@ chain +checkds cookie dlzexternal dnssec diff --git a/util/copyrights b/util/copyrights index fe9802ea3c..f2aae63f10 100644 --- a/util/copyrights +++ b/util/copyrights @@ -188,6 +188,14 @@ ./bin/tests/system/checkconf/dnssec.2 X 2011,2016,2018,2019,2020,2021 ./bin/tests/system/checkconf/good.zonelist X 2016,2017,2018,2019,2020,2021 ./bin/tests/system/checkconf/tests.sh SH 2005,2007,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 +./bin/tests/system/checkds/README TXT.BRIEF 2021 +./bin/tests/system/checkds/clean.sh SH 2021 +./bin/tests/system/checkds/conftest.py PYTHON 2021 +./bin/tests/system/checkds/ns2/setup.sh SH 2021 +./bin/tests/system/checkds/ns5/setup.sh SH 2021 +./bin/tests/system/checkds/ns9/setup.sh SH 2021 +./bin/tests/system/checkds/setup.sh SH 2021 +./bin/tests/system/checkds/tests-checkds.py PYTHON-BIN 2021 ./bin/tests/system/checknames/clean.sh SH 2004,2007,2012,2014,2015,2016,2018,2019,2020,2021 ./bin/tests/system/checknames/setup.sh SH 2004,2007,2012,2014,2016,2018,2019,2020,2021 ./bin/tests/system/checknames/tests.sh SH 2004,2007,2012,2013,2014,2015,2016,2018,2019,2020,2021