2025-02-28 15:52:20 +01:00
|
|
|
# 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.
|
|
|
|
|
|
|
|
from datetime import timedelta
|
2025-06-11 16:33:53 +02:00
|
|
|
import os
|
2025-02-28 15:52:20 +01:00
|
|
|
|
|
|
|
import isctest
|
2025-06-11 16:35:03 +02:00
|
|
|
from isctest.kasp import KeyTimingMetadata, Ipub, Iret
|
2025-02-28 15:52:20 +01:00
|
|
|
|
2025-06-11 16:35:03 +02:00
|
|
|
from common import pytestmark # pylint: disable=unused-import
|
2025-02-28 15:52:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
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"
|
2025-03-19 10:10:13 +01:00
|
|
|
|
2025-06-17 14:32:49 +10:00
|
|
|
with server.watch_log_from_start() as watcher:
|
|
|
|
watcher.wait_for_line(f"keymgr: {zone} done")
|
|
|
|
|
2025-03-19 10:10:13 +01:00
|
|
|
isctest.kasp.check_dnssec_verify(server, zone)
|
|
|
|
|
2025-02-28 15:52:20 +01:00
|
|
|
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_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)
|
|
|
|
|
|
|
|
# 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")
|
|
|
|
|
2025-03-19 10:10:13 +01:00
|
|
|
isctest.kasp.check_dnssec_verify(server, zone)
|
2025-02-28 15:52:20 +01:00
|
|
|
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)
|
|
|
|
|
|
|
|
# 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")
|
|
|
|
|
2025-03-19 10:10:13 +01:00
|
|
|
isctest.kasp.check_dnssec_verify(server, zone)
|
|
|
|
|
2025-02-28 15:52:20 +01:00
|
|
|
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)
|
|
|
|
|
|
|
|
# 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")
|
|
|
|
|
2025-03-19 10:10:13 +01:00
|
|
|
isctest.kasp.check_dnssec_verify(server, zone)
|
|
|
|
|
2025-02-28 15:52:20 +01:00
|
|
|
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
|