--- /dev/null
+../rollover/common.py
\ No newline at end of file
--- /dev/null
+../../rollover-ksk-doubleksk/ns3/kasp.conf.j2
\ No newline at end of file
--- /dev/null
+../../rollover/ns3/named.common.conf.j2
\ No newline at end of file
--- /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.
+ */
+
+include "kasp.conf";
+include "named.common.conf";
+
+zone "three-is-a-crowd.kasp" {
+ type primary;
+ file "three-is-a-crowd.kasp.db";
+ inline-signing yes;
+ /* Use same policy as KSK rollover test zones. */
+ dnssec-policy "ksk-doubleksk";
+};
--- /dev/null
+../../rollover/ns3/template.db.in
\ No newline at end of file
--- /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
+
+cd "ns3"
+
+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"
+
+# Test #2375, the "three is a crowd" bug, where a new key is introduced but the
+# previous rollover has not finished yet. In other words, we have a key KEY2
+# that is the successor of key KEY1, and we introduce a new key KEY3 that is
+# the successor of key KEY2:
+#
+# KEY1 < KEY2 < KEY3.
+#
+# The expected behavior is that all three keys remain in the zone, and not
+# the bug behavior where KEY2 is removed and immediately replaced with KEY3.
+#
+# Set up a zone that has a KSK (KEY1) and have the successor key (KEY2)
+# published as well.
+setup three-is-a-crowd.kasp
+# These times are the same as step3.ksk-doubleksk.autosign.
+TpubN="now-60d"
+TactN="now-1413h"
+TretN="now"
+TremN="now+50h"
+TpubN1="now-27h"
+TsbmN1="now"
+TactN1="${TretN}"
+TretN1="now+60d"
+TremN1="now+1490h"
+ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}"
+newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TsbmN1} -I ${TretN1} -D ${TremN1}"
+zsktimes="-P ${TpubN} -A ${TpubN}"
+KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
+KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2)
+ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3)
+$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1
+$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.2 2>&1
+$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1
+# Set key rollover relationship.
+key_successor $KSK1 $KSK2
+# Sign zone.
+cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile"
+private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile"
+private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile"
+private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
+cp $infile $zonefile
+$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -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.
+
+# pylint: disable=redefined-outer-name,unused-import
+
+from datetime import timedelta
+
+import isctest
+from isctest.kasp import KeyTimingMetadata
+from common import (
+ pytestmark,
+ alg,
+ size,
+ KSK_CONFIG,
+ KSK_LIFETIME_POLICY,
+ KSK_IPUB,
+ KSK_IRET,
+)
+
+
+CDSS = ["CDS (SHA-256)"]
+POLICY = "ksk-doubleksk"
+OFFSET1 = -int(timedelta(days=60).total_seconds())
+OFFSET2 = -int(timedelta(hours=27).total_seconds())
+TTL = int(KSK_CONFIG["dnskey-ttl"].total_seconds())
+
+
+def test_rollover_ksk_three_is_a_crowd(alg, size, servers):
+ """Test #2375: Scheduled rollovers are happening faster than they can finish."""
+ server = servers["ns3"]
+ zone = "three-is-a-crowd.kasp"
+
+ step = {
+ "zone": zone,
+ "cdss": CDSS,
+ "keyprops": [
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{OFFSET1}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{OFFSET2}",
+ f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSET1}",
+ ],
+ "keyrelationships": [0, 1],
+ }
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
+
+ # Rollover successor KSK (with DS in rumoured state).
+ expected = isctest.kasp.policy_to_properties(TTL, step["keyprops"])
+ keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
+ isctest.kasp.check_keys(zone, keys, expected)
+ key = expected[1].key
+ now = KeyTimingMetadata.now()
+ with server.watch_log_from_here() as watcher:
+ server.rndc(f"dnssec -rollover -key {key.tag} -when {now} {zone}")
+ watcher.wait_for_line(f"keymgr: {zone} done")
+
+ # We now expect four keys (3x KSK, 1x ZSK).
+ step = {
+ "zone": zone,
+ "cdss": CDSS,
+ "keyprops": [
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{OFFSET1}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{OFFSET2}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden offset:0",
+ f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSET1}",
+ ],
+ "check-keytimes": False, # checked manually with modified values
+ }
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
+
+ expected = isctest.kasp.policy_to_properties(TTL, step["keyprops"])
+ keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
+ isctest.kasp.check_keys(zone, keys, expected)
+
+ expected[0].metadata["Successor"] = expected[1].key.tag
+ expected[1].metadata["Predecessor"] = expected[0].key.tag
+ # Three is a crowd scenario.
+ expected[1].metadata["Successor"] = expected[2].key.tag
+ expected[2].metadata["Predecessor"] = expected[1].key.tag
+ isctest.kasp.check_keyrelationships(keys, expected)
+ for kp in expected:
+ kp.set_expected_keytimes(KSK_CONFIG)
+
+ # The first successor KSK is already being retired.
+ expected[1].timing["Retired"] = now + KSK_IPUB
+ expected[1].timing["Removed"] = now + KSK_IPUB + KSK_IRET
+
+ isctest.kasp.check_keytimes(keys, expected)
pytestmark,
alg,
size,
- DEFAULT_CONFIG,
+ KSK_CONFIG,
+ KSK_LIFETIME,
+ KSK_LIFETIME_POLICY,
+ KSK_IPUB,
+ KSK_IPUBC,
+ KSK_IRET,
+ KSK_KEYTTLPROP,
TIMEDELTA,
)
CDSS = ["CDS (SHA-256)"]
-CONFIG = {
- "dnskey-ttl": TIMEDELTA["PT2H"],
- "ds-ttl": TIMEDELTA["PT1H"],
- "max-zone-ttl": TIMEDELTA["P1D"],
- "parent-propagation-delay": TIMEDELTA["PT1H"],
- "publish-safety": TIMEDELTA["P1D"],
- "purge-keys": TIMEDELTA["PT1H"],
- "retire-safety": TIMEDELTA["P2D"],
- "signatures-refresh": TIMEDELTA["P7D"],
- "signatures-validity": TIMEDELTA["P14D"],
- "zone-propagation-delay": TIMEDELTA["PT1H"],
-}
POLICY = "ksk-doubleksk"
-KSK_LIFETIME = TIMEDELTA["P60D"]
-LIFETIME_POLICY = int(KSK_LIFETIME.total_seconds())
-IPUB = Ipub(CONFIG)
-IPUBC = IpubC(CONFIG)
-IRET = Iret(CONFIG, zsk=False, ksk=True)
-KEYTTLPROP = CONFIG["dnskey-ttl"] + CONFIG["zone-propagation-delay"]
OFFSETS = {}
OFFSETS["step1-p"] = -int(TIMEDELTA["P7D"].total_seconds())
-OFFSETS["step2-p"] = -int(KSK_LIFETIME.total_seconds() - IPUBC.total_seconds())
+OFFSETS["step2-p"] = -int(KSK_LIFETIME.total_seconds() - KSK_IPUBC.total_seconds())
OFFSETS["step2-s"] = 0
OFFSETS["step3-p"] = -int(KSK_LIFETIME.total_seconds())
-OFFSETS["step3-s"] = -int(IPUBC.total_seconds())
-OFFSETS["step4-p"] = OFFSETS["step3-p"] - int(IRET.total_seconds())
-OFFSETS["step4-s"] = OFFSETS["step3-s"] - int(IRET.total_seconds())
-OFFSETS["step5-p"] = OFFSETS["step4-p"] - int(KEYTTLPROP.total_seconds())
-OFFSETS["step5-s"] = OFFSETS["step4-s"] - int(KEYTTLPROP.total_seconds())
-OFFSETS["step6-p"] = OFFSETS["step5-p"] - int(CONFIG["purge-keys"].total_seconds())
-OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(CONFIG["purge-keys"].total_seconds())
+OFFSETS["step3-s"] = -int(KSK_IPUBC.total_seconds())
+OFFSETS["step4-p"] = OFFSETS["step3-p"] - int(KSK_IRET.total_seconds())
+OFFSETS["step4-s"] = OFFSETS["step3-s"] - int(KSK_IRET.total_seconds())
+OFFSETS["step5-p"] = OFFSETS["step4-p"] - int(KSK_KEYTTLPROP.total_seconds())
+OFFSETS["step5-s"] = OFFSETS["step4-s"] - int(KSK_KEYTTLPROP.total_seconds())
+OFFSETS["step6-p"] = OFFSETS["step5-p"] - int(KSK_CONFIG["purge-keys"].total_seconds())
+OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(KSK_CONFIG["purge-keys"].total_seconds())
def test_ksk_doubleksk_step1(alg, size, servers):
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step1-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step1-p']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step1-p']}",
],
# Next key event is when the successor KSK needs to be published.
# That is the KSK lifetime - prepublication time (minus time
# already passed).
- "nextev": KSK_LIFETIME - IPUB - timedelta(days=7),
+ "nextev": KSK_LIFETIME - KSK_IPUB - timedelta(days=7),
}
- isctest.kasp.check_rollover_step(servers["ns3"], CONFIG, POLICY, step)
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
def test_ksk_doubleksk_step2(alg, size, servers):
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step2-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden offset:{OFFSETS['step2-s']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden offset:{OFFSETS['step2-s']}",
],
"keyrelationships": [1, 2],
# Next key event is when the successor KSK becomes OMNIPRESENT.
- "nextev": IPUB,
+ "nextev": KSK_IPUB,
}
- isctest.kasp.check_rollover_step(servers["ns3"], CONFIG, POLICY, step)
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
def test_ksk_doubleksk_step3(alg, size, servers):
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step3-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{OFFSETS['step3-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{OFFSETS['step3-s']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{OFFSETS['step3-p']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{OFFSETS['step3-s']}",
],
"keyrelationships": [1, 2],
# Next key event is when the predecessor DS has been replaced with
# the successor DS and enough time has passed such that the all
# validators that have this DS RRset cached only know about the
# successor DS. This is the the retire interval.
- "nextev": IRET,
+ "nextev": KSK_IRET,
}
- isctest.kasp.check_rollover_step(servers["ns3"], CONFIG, POLICY, step)
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
def test_ksk_doubleksk_step4(alg, size, servers):
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step4-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:unretentive krrsig:unretentive ds:hidden offset:{OFFSETS['step4-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step4-s']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:unretentive krrsig:unretentive ds:hidden offset:{OFFSETS['step4-p']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step4-s']}",
],
"keyrelationships": [1, 2],
# Next key event is when the DNSKEY enters the HIDDEN state.
# This is the DNSKEY TTL plus zone propagation delay.
- "nextev": KEYTTLPROP,
+ "nextev": KSK_KEYTTLPROP,
}
- isctest.kasp.check_rollover_step(servers["ns3"], CONFIG, POLICY, step)
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
def test_ksk_doubleksk_step5(alg, size, servers):
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step5-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:hidden krrsig:hidden ds:hidden offset:{OFFSETS['step5-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step5-s']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:hidden krrsig:hidden ds:hidden offset:{OFFSETS['step5-p']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step5-s']}",
],
"keyrelationships": [1, 2],
# Next key event is when the new successor needs to be published.
# This is the KSK lifetime minus Ipub minus Iret minus time elapsed.
- "nextev": KSK_LIFETIME - IPUB - IRET - KEYTTLPROP,
+ "nextev": KSK_LIFETIME - KSK_IPUB - KSK_IRET - KSK_KEYTTLPROP,
}
- isctest.kasp.check_rollover_step(servers["ns3"], CONFIG, POLICY, step)
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
def test_ksk_doubleksk_step6(alg, size, servers):
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step6-p']}",
- f"ksk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step6-s']}",
+ f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step6-s']}",
],
"nextev": None,
}
- isctest.kasp.check_rollover_step(servers["ns3"], CONFIG, POLICY, step)
+ isctest.kasp.check_rollover_step(servers["ns3"], KSK_CONFIG, POLICY, step)
ALGOROLL_IRET.total_seconds()
)
ALGOROLL_OFFVAL = -DURATION["P7D"]
+KSK_CONFIG = {
+ "dnskey-ttl": TIMEDELTA["PT2H"],
+ "ds-ttl": TIMEDELTA["PT1H"],
+ "max-zone-ttl": TIMEDELTA["P1D"],
+ "parent-propagation-delay": TIMEDELTA["PT1H"],
+ "publish-safety": TIMEDELTA["P1D"],
+ "purge-keys": TIMEDELTA["PT1H"],
+ "retire-safety": TIMEDELTA["P2D"],
+ "signatures-refresh": TIMEDELTA["P7D"],
+ "signatures-validity": TIMEDELTA["P14D"],
+ "zone-propagation-delay": TIMEDELTA["PT1H"],
+}
+KSK_LIFETIME = TIMEDELTA["P60D"]
+KSK_LIFETIME_POLICY = int(KSK_LIFETIME.total_seconds())
+KSK_IPUB = Ipub(KSK_CONFIG)
+KSK_IPUBC = IpubC(KSK_CONFIG)
+KSK_IRET = Iret(KSK_CONFIG, zsk=False, ksk=True)
+KSK_KEYTTLPROP = KSK_CONFIG["dnskey-ttl"] + KSK_CONFIG["zone-propagation-delay"]
@pytest.fixture
zone-propagation-delay PT1H;
max-zone-ttl 1d;
};
-
-dnssec-policy "ksk-doubleksk" {
- signatures-refresh P1W;
- signatures-validity P2W;
- signatures-validity-dnskey P2W;
-
- dnskey-ttl 2h;
- publish-safety P1D;
- retire-safety P2D;
- purge-keys PT1H;
-
- cdnskey no;
- keys {
- ksk key-directory lifetime P60D algorithm @DEFAULT_ALGORITHM@;
- zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
- };
-
- zone-propagation-delay PT1H;
- max-zone-ttl 1d;
-
- parent-ds-ttl 3600;
- parent-propagation-delay PT1H;
-};
file "step6.zsk-prepub.autosign.db";
dnssec-policy "zsk-prepub";
};
-
-/*
- * Zone for testing GL #2375: Three is a crowd.
- */
-zone "three-is-a-crowd.kasp" {
- type primary;
- file "three-is-a-crowd.kasp.db";
- inline-signing yes;
- /* Use same policy as KSK rollover test zones. */
- dnssec-policy "ksk-doubleksk";
-};
private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK2" >>"$infile"
cp $infile $zonefile
$SIGNER -S -x -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
-
-# Test #2375, the "three is a crowd" bug, where a new key is introduced but the
-# previous rollover has not finished yet. In other words, we have a key KEY2
-# that is the successor of key KEY1, and we introduce a new key KEY3 that is
-# the successor of key KEY2:
-#
-# KEY1 < KEY2 < KEY3.
-#
-# The expected behavior is that all three keys remain in the zone, and not
-# the bug behavior where KEY2 is removed and immediately replaced with KEY3.
-#
-# Set up a zone that has a KSK (KEY1) and have the successor key (KEY2)
-# published as well.
-setup three-is-a-crowd.kasp
-# These times are the same as step3.ksk-doubleksk.autosign.
-TpubN="now-60d"
-TactN="now-1413h"
-TretN="now"
-TremN="now+50h"
-TpubN1="now-27h"
-TsbmN1="now"
-TactN1="${TretN}"
-TretN1="now+60d"
-TremN1="now+1490h"
-ksktimes="-P ${TpubN} -A ${TpubN} -P sync ${TactN} -I ${TretN} -D ${TremN} -D sync ${TactN1}"
-newtimes="-P ${TpubN1} -A ${TactN1} -P sync ${TsbmN1} -I ${TretN1} -D ${TremN1}"
-zsktimes="-P ${TpubN} -A ${TpubN}"
-KSK1=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
-KSK2=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 -f KSK $newtimes $zone 2>keygen.out.$zone.2)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 7200 $zsktimes $zone 2>keygen.out.$zone.3)
-$SETTIME -s -g $H -k $O $TactN -r $O $TactN -d $O $TactN "$KSK1" >settime.out.$zone.1 2>&1
-$SETTIME -s -g $O -k $R $TpubN1 -r $R $TpubN1 -d $H $TpubN1 "$KSK2" >settime.out.$zone.2 2>&1
-$SETTIME -s -g $O -k $O $TactN -z $O $TactN "$ZSK" >settime.out.$zone.3 2>&1
-# Set key rollover relationship.
-key_successor $KSK1 $KSK2
-# Sign zone.
-cat template.db.in "${KSK1}.key" "${KSK2}.key" "${ZSK}.key" >"$infile"
-private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK1" >>"$infile"
-private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$KSK2" >>"$infile"
-private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$ZSK" >>"$infile"
-cp $infile $zonefile
-$SIGNER -S -x -G "cds:sha-256" -s now-1h -e now+2w -o $zone -O raw -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
for step in steps:
isctest.kasp.check_rollover_step(server, config, policy, step)
-
-
-def test_rollover_ksk_doubleksk(servers):
- server = servers["ns3"]
- policy = "ksk-doubleksk"
- cdss = ["CDS (SHA-256)"]
- config = {
- "dnskey-ttl": timedelta(hours=2),
- "ds-ttl": timedelta(seconds=3600),
- "max-zone-ttl": timedelta(days=1),
- "parent-propagation-delay": timedelta(hours=1),
- "publish-safety": timedelta(days=1),
- "purge-keys": timedelta(hours=1),
- "retire-safety": timedelta(days=2),
- "signatures-refresh": timedelta(days=7),
- "signatures-validity": timedelta(days=14),
- "zone-propagation-delay": timedelta(hours=1),
- }
- ttl = int(config["dnskey-ttl"].total_seconds())
- alg = os.environ["DEFAULT_ALGORITHM_NUMBER"]
- size = os.environ["DEFAULT_BITS"]
- ksk_lifetime = timedelta(days=60)
- lifetime_policy = int(ksk_lifetime.total_seconds())
-
- ipub = Ipub(config)
- iret = Iret(config, zsk=False, ksk=True)
-
- # Test #2375: Scheduled rollovers are happening faster than they can finish.
- isctest.log.info(
- "check that fast rollovers do not remove dependent keys from zone (#2375)"
- )
- offset1 = -int(timedelta(days=60).total_seconds())
- offset2 = -int(timedelta(hours=27).total_seconds())
- zone = "three-is-a-crowd.kasp"
- step = {
- "zone": zone,
- "cdss": cdss,
- "keyprops": [
- f"ksk {lifetime_policy} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{offset1}",
- f"ksk {lifetime_policy} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{offset2}",
- f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{offset1}",
- ],
- "keyrelationships": [0, 1],
- }
- isctest.kasp.check_rollover_step(servers["ns3"], config, policy, step)
-
- # Rollover successor KSK (with DS in rumoured state).
- expected = isctest.kasp.policy_to_properties(ttl, step["keyprops"])
- keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
- isctest.kasp.check_keys(zone, keys, expected)
- key = expected[1].key
- now = KeyTimingMetadata.now()
- with server.watch_log_from_here() as watcher:
- server.rndc(f"dnssec -rollover -key {key.tag} -when {now} {zone}")
- watcher.wait_for_line(f"keymgr: {zone} done")
-
- # We now expect four keys (3x KSK, 1x ZSK).
- step = {
- "zone": zone,
- "cdss": cdss,
- "keyprops": [
- f"ksk {lifetime_policy} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{offset1}",
- f"ksk {lifetime_policy} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:rumoured offset:{offset2}",
- f"ksk {lifetime_policy} {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden offset:0",
- f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{offset1}",
- ],
- "check-keytimes": False, # checked manually with modified values
- }
- isctest.kasp.check_rollover_step(servers["ns3"], config, policy, step)
-
- expected = isctest.kasp.policy_to_properties(ttl, step["keyprops"])
- keys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
- isctest.kasp.check_keys(zone, keys, expected)
-
- expected[0].metadata["Successor"] = expected[1].key.tag
- expected[1].metadata["Predecessor"] = expected[0].key.tag
- # Three is a crowd scenario.
- expected[1].metadata["Successor"] = expected[2].key.tag
- expected[2].metadata["Predecessor"] = expected[1].key.tag
- isctest.kasp.check_keyrelationships(keys, expected)
- for kp in expected:
- kp.set_expected_keytimes(config, offset=None)
-
- # The first successor KSK is already being retired.
- expected[1].timing["Retired"] = now + ipub
- expected[1].timing["Removed"] = now + ipub + iret
-
- isctest.kasp.check_keytimes(keys, expected)