--- /dev/null
+../rollover/common.py
\ 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.
+ */
+
+dnssec-policy "multisigner-model2" {
+ dnskey-ttl 3600;
+ inline-signing no;
+
+ keys {
+ ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@ tag-range 32768 65535;
+ zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@ tag-range 32768 65535;
+ };
+};
--- /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";
+
+/* RFC 8901 Multi-signer Model 2. */
+zone "multisigner-model2.kasp" {
+ type primary;
+ file "multisigner-model2.kasp.db";
+ dnssec-policy "multisigner-model2";
+ allow-update { any; };
+};
+
+/*
+ * A zone that starts with keys that have tags that are
+ * outside of the desired multi-signer key tag range.
+ */
+zone "single-to-multisigner.kasp" {
+ type primary;
+ file "single-to-multisigner.kasp.db";
+ dnssec-policy "multisigner-model2";
+ allow-update { any; };
+};
--- /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"
+
+# Multi-signer zones.
+setup "multisigner-model2.kasp"
+cp template.db.in "$zonefile"
+KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 -M 32768:65535 $zone 2>keygen.out.$zone.1)
+ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -M 32768:65535 $zone 2>keygen.out.$zone.2)
+cat "${KSK}.key" | grep -v ";.*" >>"${zone}.db"
+cat "${ZSK}.key" | grep -v ";.*" >>"${zone}.db"
+# Import a ZSK of another provider into the DNSKEY RRset.
+ZSK1=$($KEYGEN -K ../ -a $DEFAULT_ALGORITHM -L 3600 -M 0:32767 $zone 2>keygen.out.$zone.3)
+cat "../${ZSK1}.key" | grep -v ";.*" >>"${zone}.db"
+
+# We are changing an existing single-signed zone to multi-signed
+# zone where the key tags do not match the dnssec-policy key tag range
+setup single-to-multisigner.kasp
+T="now-7d"
+S="now-8635mi" # T - 1d5m
+keytimes="-P $T -A $T"
+cdstimes="-P sync $S"
+KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -M 0:32767 -L 3600 -f KSK $keytimes $cdstimes $zone 2>keygen.out.$zone.1)
+ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -M 0:32767 -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"
+$SIGNER -PS -z -x -s now-2w -e now-1mi -o $zone -f "${zonefile}" $infile >signer.out.$zone.1 2>&1
+echo "Lifetime: 0" >>"${KSK}".state
+echo "Lifetime: 0" >>"${ZSK}".state
--- /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 os
+
+import pytest
+
+pytest.importorskip("dns", minversion="2.0.0")
+import dns.update
+
+import isctest
+from isctest.kasp import Iret
+from common import (
+ pytestmark,
+ alg,
+ size,
+)
+
+
+def test_rollover_multisigner(servers, alg, size):
+ server = servers["ns3"]
+ policy = "multisigner-model2"
+ 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=5),
+ "signatures-validity": timedelta(days=14),
+ "zone-propagation-delay": timedelta(minutes=5),
+ }
+ ttl = int(config["dnskey-ttl"].total_seconds())
+
+ offset = -timedelta(days=7)
+ offval = int(offset.total_seconds())
+
+ def keygen(zone):
+ keygen_command = [
+ os.environ.get("KEYGEN"),
+ "-a",
+ alg,
+ "-L",
+ "3600",
+ "-M",
+ "0:32767",
+ zone,
+ ]
+
+ return isctest.run.cmd(keygen_command).stdout.decode("utf-8")
+
+ zone = "multisigner-model2.kasp"
+
+ isctest.kasp.check_dnssec_verify(server, zone)
+
+ key_properties = [
+ f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden tag-range:32768-65535",
+ f"zsk unlimited {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:rumoured tag-range:32768-65535",
+ ]
+ expected = isctest.kasp.policy_to_properties(ttl, key_properties)
+
+ newprops = [f"zsk unlimited {alg} {size} tag-range:0-32767"]
+ expected2 = isctest.kasp.policy_to_properties(ttl, newprops)
+ expected2[0].properties["private"] = False
+ expected2[0].properties["legacy"] = True
+ expected = expected + expected2
+
+ ownkeys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
+ extkeys = isctest.kasp.keydir_to_keylist(zone)
+ keys = ownkeys + extkeys
+ ksks = [k for k in ownkeys if k.is_ksk()]
+ zsks = [k for k in ownkeys if not k.is_ksk()]
+ zsks = zsks + extkeys
+
+ isctest.kasp.check_keys(zone, keys, expected)
+ for kp in expected:
+ kp.set_expected_keytimes(config)
+ 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)
+
+ # Update zone with ZSK from another provider for zone.
+ out = keygen(zone)
+ newkeys = isctest.kasp.keystr_to_keylist(out)
+ newprops = [f"zsk unlimited {alg} {size} tag-range:0-32767"]
+ expected2 = isctest.kasp.policy_to_properties(ttl, newprops)
+ expected2[0].properties["private"] = False
+ expected2[0].properties["legacy"] = True
+ expected = expected + expected2
+
+ dnskey = newkeys[0].dnskey().split()
+ rdata = " ".join(dnskey[4:])
+
+ update_msg = dns.update.UpdateMessage(zone)
+ update_msg.add(f"{dnskey[0]}", 3600, "DNSKEY", rdata)
+ server.nsupdate(update_msg)
+
+ isctest.kasp.check_dnssec_verify(server, zone)
+
+ keys = keys + newkeys
+ zsks = zsks + newkeys
+ isctest.kasp.check_keys(zone, keys, expected)
+ isctest.kasp.check_apex(server, zone, ksks, zsks)
+ isctest.kasp.check_subdomain(server, zone, ksks, zsks)
+
+ # Remove ZSKs from the other providers for zone.
+ dnskey2 = extkeys[0].dnskey().split()
+ rdata2 = " ".join(dnskey2[4:])
+ update_msg = dns.update.UpdateMessage(zone)
+ update_msg.delete(f"{dnskey[0]}", "DNSKEY", rdata)
+ update_msg.delete(f"{dnskey2[0]}", "DNSKEY", rdata2)
+ server.nsupdate(update_msg)
+
+ isctest.kasp.check_dnssec_verify(server, zone)
+
+ expected = isctest.kasp.policy_to_properties(ttl, key_properties)
+ keys = ownkeys
+ ksks = [k for k in ownkeys if k.is_ksk()]
+ zsks = [k for k in ownkeys if not k.is_ksk()]
+ isctest.kasp.check_keys(zone, keys, expected)
+ isctest.kasp.check_apex(server, zone, ksks, zsks)
+ isctest.kasp.check_subdomain(server, zone, ksks, zsks)
+
+ # A zone transitioning from single-signed to multi-signed. We should have
+ # the old omnipresent keys outside of the desired key range and the new
+ # keys in the desired key range.
+ zone = "single-to-multisigner.kasp"
+
+ isctest.kasp.check_dnssec_verify(server, zone)
+
+ key_properties = [
+ f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden tag-range:32768-65535",
+ f"zsk unlimited {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:hidden tag-range:32768-65535",
+ f"ksk unlimited {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent tag-range:0-32767 offset:{offval}",
+ f"zsk unlimited {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent tag-range:0-32767 offset:{offval}",
+ ]
+ 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)
+
+ for kp in expected:
+ kp.set_expected_keytimes(config)
+
+ start = expected[0].key.get_timing("Created")
+ expected[2].timing["Retired"] = start
+ expected[2].timing["Removed"] = expected[2].timing["Retired"] + Iret(
+ config, zsk=False, ksk=True
+ )
+ expected[3].timing["Retired"] = start
+ expected[3].timing["Removed"] = expected[3].timing["Retired"] + Iret(
+ config, zsk=True, ksk=False
+ )
+
+ 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)
zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
};
};
-
-dnssec-policy "multisigner-model2" {
- dnskey-ttl 3600;
- inline-signing no;
-
- keys {
- ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@ tag-range 32768 65535;
- zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@ tag-range 32768 65535;
- };
-};
* information regarding copyright ownership.
*/
-// NS3
-
include "kasp.conf";
-
include "named.common.conf";
-/* Manual rollover. */
zone "manual-rollover.kasp" {
type primary;
file "manual-rollover.kasp.db";
dnssec-policy "manual-rollover";
};
-
-/* RFC 8901 Multi-signer Model 2. */
-zone "multisigner-model2.kasp" {
- type primary;
- file "multisigner-model2.kasp.db";
- dnssec-policy "multisigner-model2";
- allow-update { any; };
-};
-
-/*
- * A zone that starts with keys that have tags that are
- * outside of the desired multi-signer key tag range.
- */
-zone "single-to-multisigner.kasp" {
- type primary;
- file "single-to-multisigner.kasp.db";
- dnssec-policy "multisigner-model2";
- allow-update { any; };
-};
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
-
-# Multi-signer zones.
-setup "multisigner-model2.kasp"
-cp template.db.in "$zonefile"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 -M 32768:65535 $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 -M 32768:65535 $zone 2>keygen.out.$zone.2)
-cat "${KSK}.key" | grep -v ";.*" >>"${zone}.db"
-cat "${ZSK}.key" | grep -v ";.*" >>"${zone}.db"
-# Import a ZSK of another provider into the DNSKEY RRset.
-ZSK1=$($KEYGEN -K ../ -a $DEFAULT_ALGORITHM -L 3600 -M 0:32767 $zone 2>keygen.out.$zone.3)
-cat "../${ZSK1}.key" | grep -v ";.*" >>"${zone}.db"
-
-# We are changing an existing single-signed zone to multi-signed
-# zone where the key tags do not match the dnssec-policy key tag range
-setup single-to-multisigner.kasp
-T="now-7d"
-S="now-8635mi" # T - 1d5m
-keytimes="-P $T -A $T"
-cdstimes="-P sync $S"
-KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -M 0:32767 -L 3600 -f KSK $keytimes $cdstimes $zone 2>keygen.out.$zone.1)
-ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -M 0:32767 -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"
-$SIGNER -PS -z -x -s now-2w -e now-1mi -o $zone -f "${zonefile}" $infile >signer.out.$zone.1 2>&1
-echo "Lifetime: 0" >>"${KSK}".state
-echo "Lifetime: 0" >>"${ZSK}".state
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
-import os
-
from datetime import timedelta
+import os
import pytest
-pytest.importorskip("dns", minversion="2.0.0")
-import dns.update
-
import isctest
-from isctest.kasp import KeyTimingMetadata, Ipub, IpubC, Iret
+from isctest.kasp import KeyTimingMetadata, Ipub, Iret
from common import pytestmark
zsk = expected[3].key
response = server.rndc(f"dnssec -rollover -key {zsk.tag} {zone}")
assert "key is not actively signing" in response
-
-
-def test_rollover_multisigner(servers):
- server = servers["ns3"]
- policy = "multisigner-model2"
- 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=5),
- "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"]
-
- offset = -timedelta(days=7)
- offval = int(offset.total_seconds())
-
- def keygen(zone):
- keygen_command = [
- os.environ.get("KEYGEN"),
- "-a",
- alg,
- "-L",
- "3600",
- "-M",
- "0:32767",
- zone,
- ]
-
- return isctest.run.cmd(keygen_command).stdout.decode("utf-8")
-
- zone = "multisigner-model2.kasp"
-
- isctest.kasp.check_dnssec_verify(server, zone)
-
- key_properties = [
- f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden tag-range:32768-65535",
- f"zsk unlimited {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:rumoured tag-range:32768-65535",
- ]
- expected = isctest.kasp.policy_to_properties(ttl, key_properties)
-
- newprops = [f"zsk unlimited {alg} {size} tag-range:0-32767"]
- expected2 = isctest.kasp.policy_to_properties(ttl, newprops)
- expected2[0].properties["private"] = False
- expected2[0].properties["legacy"] = True
- expected = expected + expected2
-
- ownkeys = isctest.kasp.keydir_to_keylist(zone, server.identifier)
- extkeys = isctest.kasp.keydir_to_keylist(zone)
- keys = ownkeys + extkeys
- ksks = [k for k in ownkeys if k.is_ksk()]
- zsks = [k for k in ownkeys if not k.is_ksk()]
- zsks = zsks + extkeys
-
- isctest.kasp.check_keys(zone, keys, expected)
- for kp in expected:
- kp.set_expected_keytimes(config)
- 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)
-
- # Update zone with ZSK from another provider for zone.
- out = keygen(zone)
- newkeys = isctest.kasp.keystr_to_keylist(out)
- newprops = [f"zsk unlimited {alg} {size} tag-range:0-32767"]
- expected2 = isctest.kasp.policy_to_properties(ttl, newprops)
- expected2[0].properties["private"] = False
- expected2[0].properties["legacy"] = True
- expected = expected + expected2
-
- dnskey = newkeys[0].dnskey().split()
- rdata = " ".join(dnskey[4:])
-
- update_msg = dns.update.UpdateMessage(zone)
- update_msg.add(f"{dnskey[0]}", 3600, "DNSKEY", rdata)
- server.nsupdate(update_msg)
-
- isctest.kasp.check_dnssec_verify(server, zone)
-
- keys = keys + newkeys
- zsks = zsks + newkeys
- isctest.kasp.check_keys(zone, keys, expected)
- isctest.kasp.check_apex(server, zone, ksks, zsks)
- isctest.kasp.check_subdomain(server, zone, ksks, zsks)
-
- # Remove ZSKs from the other providers for zone.
- dnskey2 = extkeys[0].dnskey().split()
- rdata2 = " ".join(dnskey2[4:])
- update_msg = dns.update.UpdateMessage(zone)
- update_msg.delete(f"{dnskey[0]}", "DNSKEY", rdata)
- update_msg.delete(f"{dnskey2[0]}", "DNSKEY", rdata2)
- server.nsupdate(update_msg)
-
- isctest.kasp.check_dnssec_verify(server, zone)
-
- expected = isctest.kasp.policy_to_properties(ttl, key_properties)
- keys = ownkeys
- ksks = [k for k in ownkeys if k.is_ksk()]
- zsks = [k for k in ownkeys if not k.is_ksk()]
- isctest.kasp.check_keys(zone, keys, expected)
- isctest.kasp.check_apex(server, zone, ksks, zsks)
- isctest.kasp.check_subdomain(server, zone, ksks, zsks)
-
- # A zone transitioning from single-signed to multi-signed. We should have
- # the old omnipresent keys outside of the desired key range and the new
- # keys in the desired key range.
- zone = "single-to-multisigner.kasp"
-
- isctest.kasp.check_dnssec_verify(server, zone)
-
- key_properties = [
- f"ksk unlimited {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden tag-range:32768-65535",
- f"zsk unlimited {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:hidden tag-range:32768-65535",
- f"ksk unlimited {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent tag-range:0-32767 offset:{offval}",
- f"zsk unlimited {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent tag-range:0-32767 offset:{offval}",
- ]
- 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)
-
- for kp in expected:
- kp.set_expected_keytimes(config)
-
- start = expected[0].key.get_timing("Created")
- expected[2].timing["Retired"] = start
- expected[2].timing["Removed"] = expected[2].timing["Retired"] + Iret(
- config, zsk=False, ksk=True
- )
- expected[3].timing["Retired"] = start
- expected[3].timing["Removed"] = expected[3].timing["Retired"] + Iret(
- config, zsk=True, ksk=False
- )
-
- 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)