redirect \
resolver \
rndc \
+ rollover \
rootkeysentinel \
rpzextra \
rrchecker \
dnssec-policy "unlimited";
};
-/* Manual rollover. */
-zone "manual-rollover.kasp" {
- type primary;
- file "manual-rollover.kasp.db";
- dnssec-policy "manual-rollover";
-};
-
/* A zone that inherits dnssec-policy. */
zone "inherit.kasp" {
type primary;
inline-signing no;
};
-dnssec-policy "manual-rollover" {
- dnskey-ttl 3600;
-
- keys {
- ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
- zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
- };
-};
-
dnssec-policy "multisigner-model2" {
dnskey-ttl 3600;
inline-signing no;
rumoured rsasha256 rsasha512 ecdsa256 ecdsa384 \
dynamic dynamic-inline-signing inline-signing \
checkds-ksk checkds-doubleksk checkds-csk inherit unlimited \
- manual-rollover multisigner-model2 keystore; do
+ multisigner-model2 keystore; do
setup "${zn}.kasp"
cp template.db.in "$zonefile"
done
# Set up zones that are already signed.
#
-# Zone to test manual rollover.
-setup manual-rollover.kasp
-T="now-1d"
-ksktimes="-P $T -A $T -P sync $T"
-zsktimes="-P $T -A $T"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2>keygen.out.$zone.2)
-$SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" >settime.out.$zone.1 2>&1
-$SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" >settime.out.$zone.2 2>&1
-cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
-private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile"
-private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
-cp $infile $zonefile
-$SIGNER -PS -x -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
# We are signing the raw version of the zone here. This is unusual and not
# common operation, but want to make sure that in such a case BIND 9 does not
# schedule a resigning operation on the raw version. Add expired signatures so
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
-#
-# Testing manual rollover.
-#
-set_zone "manual-rollover.kasp"
-set_policy "manual-rollover" "2" "3600"
-set_server "ns3" "10.53.0.3"
-key_clear "KEY1"
-key_clear "KEY2"
-key_clear "KEY3"
-key_clear "KEY4"
-# Key properties.
-set_keyrole "KEY1" "ksk"
-set_keylifetime "KEY1" "0"
-set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
-set_keysigning "KEY1" "yes"
-set_zonesigning "KEY1" "no"
-
-set_keyrole "KEY2" "zsk"
-set_keylifetime "KEY2" "0"
-set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
-set_keysigning "KEY2" "no"
-set_zonesigning "KEY2" "yes"
-# During set up everything was set to OMNIPRESENT.
-set_keystate "KEY1" "GOAL" "omnipresent"
-set_keystate "KEY1" "STATE_DNSKEY" "omnipresent"
-set_keystate "KEY1" "STATE_KRRSIG" "omnipresent"
-set_keystate "KEY1" "STATE_DS" "omnipresent"
-
-set_keystate "KEY2" "GOAL" "omnipresent"
-set_keystate "KEY2" "STATE_DNSKEY" "omnipresent"
-set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-
-# The first keys were published and activated a day ago.
-created=$(key_get KEY1 CREATED)
-set_addkeytime "KEY1" "PUBLISHED" "${created}" -86400
-set_addkeytime "KEY1" "SYNCPUBLISH" "${created}" -86400
-set_addkeytime "KEY1" "ACTIVE" "${created}" -86400
-created=$(key_get KEY2 CREATED)
-set_addkeytime "KEY2" "PUBLISHED" "${created}" -86400
-set_addkeytime "KEY2" "ACTIVE" "${created}" -86400
-# Key lifetimes are unlimited, so not setting RETIRED and REMOVED.
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-
-# Schedule KSK rollover in six months (15552000 seconds).
-active=$(key_get KEY1 ACTIVE)
-set_addkeytime "KEY1" "RETIRED" "${active}" 15552000
-retired=$(key_get KEY1 RETIRED)
-rndc_rollover "$SERVER" "$DIR" $(key_get KEY1 ID) "${retired}" "$ZONE"
-set_addkeytime "KEY1" "RETIRED" "${active}" 15559500
-retired=$(key_get KEY1 RETIRED)
-# Retire interval of this policy is 26h (93600 seconds).
-set_addkeytime "KEY1" "REMOVED" "${retired}" 93600
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-check_keytimes
-check_apex
-check_subdomain
-dnssec_verify
-
-# Schedule KSK rollover now.
-set_policy "manual-rollover" "3" "3600"
-set_keystate "KEY1" "GOAL" "hidden"
-created=$(key_get KEY1 CREATED)
-set_keytime "KEY1" "RETIRED" "${created}"
-rndc_rollover "$SERVER" "$DIR" $(key_get KEY1 ID) "${created}" "$ZONE"
-# New key is introduced.
-set_keyrole "KEY3" "ksk"
-set_keylifetime "KEY3" "0"
-set_keyalgorithm "KEY3" "13" "ECDSAP256SHA256" "256"
-set_keysigning "KEY3" "yes"
-set_zonesigning "KEY3" "no"
-
-set_keystate "KEY3" "GOAL" "omnipresent"
-set_keystate "KEY3" "STATE_DNSKEY" "rumoured"
-set_keystate "KEY3" "STATE_KRRSIG" "rumoured"
-set_keystate "KEY3" "STATE_DS" "hidden"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-check_apex
-check_subdomain
-dnssec_verify
-
-# Schedule ZSK rollover now.
-set_policy "manual-rollover" "4" "3600"
-set_keystate "KEY2" "GOAL" "hidden"
-created=$(key_get KEY2 CREATED)
-set_keytime "KEY2" "RETIRED" "${created}"
-rndc_rollover "$SERVER" "$DIR" $(key_get KEY2 ID) "${created}" "$ZONE"
-# New key is introduced.
-set_keyrole "KEY4" "zsk"
-set_keylifetime "KEY4" "0"
-set_keyalgorithm "KEY4" "13" "ECDSAP256SHA256" "256"
-set_keysigning "KEY4" "no"
-set_zonesigning "KEY4" "no" # not yet, first prepublish DNSKEY.
-
-set_keystate "KEY4" "GOAL" "omnipresent"
-set_keystate "KEY4" "STATE_DNSKEY" "rumoured"
-set_keystate "KEY4" "STATE_ZRRSIG" "hidden"
-
-check_keys
-check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
-check_apex
-check_subdomain
-dnssec_verify
-
-# Try to schedule a ZSK rollover for an inactive key (should fail).
-n=$((n + 1))
-echo_i "check that rndc dnssec -rollover fails if key is inactive ($n)"
-ret=0
-rndccmd "$SERVER" dnssec -rollover -key $(key_get KEY4 ID) "$ZONE" >rndc.dnssec.rollover.out.$ZONE.$n || ret=1
-grep "key is not actively signing" rndc.dnssec.rollover.out.$ZONE.$n >/dev/null || log_error "bad error message"
-test "$ret" -eq 0 || echo_i "failed"
-status=$((status + ret))
-
#
# Testing DNSSEC introduction.
#
--- /dev/null
+/*
+ * 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-policy "manual-rollover" {
+ dnskey-ttl 3600;
+
+ keys {
+ ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
+ zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
+ };
+};
--- /dev/null
+/*
+ * 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.
+ */
+
+// NS3
+
+include "kasp.conf";
+
+options {
+ query-source address 10.53.0.3;
+ notify-source 10.53.0.3;
+ transfer-source 10.53.0.3;
+ port @PORT@;
+ pid-file "named.pid";
+ listen-on { 10.53.0.3; };
+ listen-on-v6 { none; };
+ allow-transfer { any; };
+ recursion no;
+ dnssec-validation no;
+};
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+ inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+ type hint;
+ file "../../_common/root.hint.blackhole";
+};
+
+/* Manual rollover. */
+zone "manual-rollover.kasp" {
+ type primary;
+ file "manual-rollover.kasp.db";
+ dnssec-policy "manual-rollover";
+};
--- /dev/null
+#!/bin/sh -e
+
+# 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.
+
+# shellcheck source=conf.sh
+. ../../conf.sh
+
+echo_i "ns3/setup.sh"
+
+setup() {
+ zone="$1"
+ echo_i "setting up zone: $zone"
+ zonefile="${zone}.db"
+ infile="${zone}.db.infile"
+ echo "$zone" >>zones
+}
+
+# Set in the key state files the Predecessor/Successor fields.
+# Key $1 is the predecessor of key $2.
+key_successor() {
+ id1=$(keyfile_to_key_id "$1")
+ id2=$(keyfile_to_key_id "$2")
+ echo "Predecessor: ${id1}" >>"${2}.state"
+ echo "Successor: ${id2}" >>"${1}.state"
+}
+
+# Make lines shorter by storing key states in environment variables.
+H="HIDDEN"
+R="RUMOURED"
+O="OMNIPRESENT"
+U="UNRETENTIVE"
+
+# Zone to test manual rollover.
+setup manual-rollover.kasp
+T="now-7d"
+keytimes="-P $T -A $T"
+KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -f KSK $keytimes $zone 2>keygen.out.$zone.1)
+ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $keytimes $zone 2>keygen.out.$zone.2)
+$SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" >settime.out.$zone.1 2>&1
+$SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" >settime.out.$zone.2 2>&1
+cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
+private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK" >>"$infile"
+private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
+cp $infile $zonefile
+$SIGNER -PS -x -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
--- /dev/null
+; 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 300
+@ IN SOA mname1. . (
+ 1 ; serial
+ 20 ; refresh (20 seconds)
+ 20 ; retry (20 seconds)
+ 1814400 ; expire (3 weeks)
+ 3600 ; minimum (1 hour)
+ )
+
+ NS ns3
+ns3 A 10.53.0.3
+
+a A 10.0.0.1
+b A 10.0.0.2
+c A 10.0.0.3
+
--- /dev/null
+#!/bin/sh -e
+
+# 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.
+
+# shellcheck source=conf.sh
+. ../conf.sh
+
+set -e
+
+(
+ cd ns3
+ $SHELL setup.sh
+)
--- /dev/null
+# 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.
+
+import os
+
+from datetime import timedelta
+
+import pytest
+
+pytest.importorskip("dns", minversion="2.0.0")
+import dns.update
+
+import isctest
+from isctest.kasp import KeyTimingMetadata
+
+pytestmark = pytest.mark.extra_artifacts(
+ [
+ "*.axfr*",
+ "dig.out*",
+ "ns*/*.db",
+ "ns*/*.db.infile",
+ "ns*/*.db.jbk",
+ "ns*/*.db.signed",
+ "ns*/*.db.signed.jnl",
+ "ns*/*.conf",
+ "ns*/dsset-*",
+ "ns*/K*.key",
+ "ns*/K*.private",
+ "ns*/K*.state",
+ "ns*/keygen.out.*",
+ "ns*/settime.out.*",
+ "ns*/signer.out.*",
+ "ns*/zones",
+ ]
+)
+
+
+def Ipub(config):
+ return (
+ config["dnskey-ttl"]
+ + config["zone-propagation-delay"]
+ + config["publish-safety"]
+ )
+
+
+def IpubC(config, rollover=True):
+ if rollover:
+ ttl = config["dnskey-ttl"]
+ safety_interval = config["publish-safety"]
+ else:
+ ttl = config["max-zone-ttl"]
+ safety_interval = timedelta(0)
+
+ return ttl + config["zone-propagation-delay"] + safety_interval
+
+
+def Iret(config, zsk=True, ksk=False, rollover=True):
+ sign_delay = timedelta(0)
+ safety_interval = timedelta(0)
+ if rollover:
+ sign_delay = config["signatures-validity"] - config["signatures-refresh"]
+ safety_interval = config["retire-safety"]
+
+ iretKSK = timedelta(0)
+ if ksk:
+ # KSK: Double-KSK Method: Iret = DprpP + TTLds
+ iretKSK = (
+ config["parent-propagation-delay"] + config["ds-ttl"] + safety_interval
+ )
+
+ iretZSK = timedelta(0)
+ if zsk:
+ # ZSK: Pre-Publication Method: Iret = Dsgn + Dprp + TTLsig
+ iretZSK = (
+ sign_delay
+ + config["zone-propagation-delay"]
+ + config["max-zone-ttl"]
+ + safety_interval
+ )
+
+ return max(iretKSK, iretZSK)
+
+
+def test_rollover_manual(servers):
+ server = servers["ns3"]
+ policy = "manual-rollover"
+ config = {
+ "dnskey-ttl": timedelta(hours=1),
+ "ds-ttl": timedelta(days=1),
+ "max-zone-ttl": timedelta(days=1),
+ "parent-propagation-delay": timedelta(hours=1),
+ "publish-safety": timedelta(hours=1),
+ "retire-safety": timedelta(hours=1),
+ "signatures-refresh": timedelta(days=7),
+ "signatures-validity": timedelta(days=14),
+ "zone-propagation-delay": timedelta(minutes=5),
+ }
+ ttl = int(config["dnskey-ttl"].total_seconds())
+ alg = os.environ["DEFAULT_ALGORITHM_NUMBER"]
+ size = os.environ["DEFAULT_BITS"]
+
+ zone = "manual-rollover.kasp"
+ key_properties = [
+ f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent",
+ f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent",
+ ]
+ expected = isctest.kasp.policy_to_properties(ttl, key_properties)
+ keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
+ ksks = [k for k in keys if k.is_ksk()]
+ zsks = [k for k in keys if not k.is_ksk()]
+
+ isctest.kasp.check_zone_is_signed(server, zone)
+ isctest.kasp.check_keys(zone, keys, expected)
+
+ offset = -timedelta(days=7)
+ for kp in expected:
+ kp.set_expected_keytimes(config, offset=offset)
+
+ isctest.kasp.check_keytimes(keys, expected)
+ isctest.kasp.check_dnssecstatus(server, zone, keys, policy=policy)
+ isctest.kasp.check_apex(server, zone, ksks, zsks)
+ isctest.kasp.check_subdomain(server, zone, ksks, zsks)
+ isctest.kasp.check_dnssec_verify(server, zone)
+
+ # Schedule KSK rollover in six months.
+ assert len(ksks) == 1
+ ksk = ksks[0]
+ startroll = expected[0].timing["Active"] + timedelta(days=30 * 6)
+ expected[0].timing["Retired"] = startroll + Ipub(config)
+ expected[0].timing["Removed"] = expected[0].timing["Retired"] + Iret(
+ config, zsk=False, ksk=True
+ )
+
+ with server.watch_log_from_here() as watcher:
+ server.rndc(f"dnssec -rollover -key {ksk.tag} -when {startroll} {zone}")
+ watcher.wait_for_line(f"keymgr: {zone} done")
+
+ isctest.kasp.check_keys(zone, keys, expected)
+ isctest.kasp.check_keytimes(keys, expected)
+ isctest.kasp.check_dnssecstatus(server, zone, keys, policy=policy)
+ isctest.kasp.check_apex(server, zone, ksks, zsks)
+ isctest.kasp.check_subdomain(server, zone, ksks, zsks)
+ isctest.kasp.check_dnssec_verify(server, zone)
+
+ # Schedule KSK rollover now.
+ now = KeyTimingMetadata.now()
+ with server.watch_log_from_here() as watcher:
+ server.rndc(f"dnssec -rollover -key {ksk.tag} -when {now} {zone}")
+ watcher.wait_for_line(f"keymgr: {zone} done")
+
+ key_properties = [
+ f"ksk unlimited {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent",
+ f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden",
+ f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent",
+ ]
+ expected = isctest.kasp.policy_to_properties(ttl, key_properties)
+ keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
+ ksks = [k for k in keys if k.is_ksk()]
+ zsks = [k for k in keys if not k.is_ksk()]
+
+ isctest.kasp.check_keys(zone, keys, expected)
+
+ expected[0].metadata["Successor"] = expected[1].key.tag
+ expected[1].metadata["Predecessor"] = expected[0].key.tag
+ isctest.kasp.check_keyrelationships(keys, expected)
+
+ for kp in expected:
+ off = offset
+ if "Predecessor" in kp.metadata:
+ off = 0
+ kp.set_expected_keytimes(config, offset=off)
+
+ expected[0].timing["Retired"] = now + Ipub(config)
+ expected[0].timing["Removed"] = expected[0].timing["Retired"] + Iret(
+ config, zsk=False, ksk=True
+ )
+
+ isctest.kasp.check_keytimes(keys, expected)
+ isctest.kasp.check_dnssecstatus(server, zone, keys, policy=policy)
+ isctest.kasp.check_apex(server, zone, ksks, zsks)
+ isctest.kasp.check_subdomain(server, zone, ksks, zsks)
+ isctest.kasp.check_dnssec_verify(server, zone)
+
+ # Schedule ZSK rollover now.
+ assert len(zsks) == 1
+ zsk = zsks[0]
+ now = KeyTimingMetadata.now()
+ with server.watch_log_from_here() as watcher:
+ server.rndc(f"dnssec -rollover -key {zsk.tag} -when {now} {zone}")
+ watcher.wait_for_line(f"keymgr: {zone} done")
+
+ key_properties = [
+ f"ksk unlimited {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent",
+ f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden",
+ f"zsk unlimited {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent",
+ f"zsk unlimited {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:hidden",
+ ]
+ expected = isctest.kasp.policy_to_properties(ttl, key_properties)
+ keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
+ ksks = [k for k in keys if k.is_ksk()]
+ zsks = [k for k in keys if not k.is_ksk()]
+
+ isctest.kasp.check_keys(zone, keys, expected)
+
+ expected[0].metadata["Successor"] = expected[1].key.tag
+ expected[1].metadata["Predecessor"] = expected[0].key.tag
+ expected[2].metadata["Successor"] = expected[3].key.tag
+ expected[3].metadata["Predecessor"] = expected[2].key.tag
+ isctest.kasp.check_keyrelationships(keys, expected)
+
+ # Try to schedule a ZSK rollover for an inactive key (should fail).
+ zsk = expected[3].key
+ response = server.rndc(f"dnssec -rollover -key {zsk.tag} {zone}")
+ assert "key is not actively signing" in response