2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 18:19:42 +00:00
bind/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

340 lines
13 KiB
Python
Raw Normal View History

# 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 pytest
import isctest
from isctest.util import param
from rollover.common import (
pytestmark,
alg,
size,
2025-06-10 16:33:08 +02:00
KSK_CONFIG,
KSK_LIFETIME,
KSK_LIFETIME_POLICY,
KSK_IPUB,
KSK_IPUBC,
KSK_IRET,
KSK_KEYTTLPROP,
TIMEDELTA,
)
CDSS = ["CDS (SHA-256)"]
POLICY = "ksk-doubleksk"
OFFSETS = {}
OFFSETS["step1-p"] = -int(TIMEDELTA["P7D"].total_seconds())
2025-06-10 16:33:08 +02:00
OFFSETS["step2-p"] = -int(KSK_LIFETIME.total_seconds() - KSK_IPUBC.total_seconds())
OFFSETS["step2-s"] = 0
OFFSETS["step3-p"] = -int(KSK_LIFETIME.total_seconds())
2025-06-10 16:33:08 +02:00
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())
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_ksk_doubleksk_step1(tld, alg, size, ns3):
zone = f"step1.ksk-doubleksk.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
# Note that the key was already generated during setup.
step = {
# Introduce the first key. This will immediately be active.
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step1-p']}",
2025-06-10 16:33:08 +02:00
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).
2025-06-10 16:33:08 +02:00
"nextev": KSK_LIFETIME - KSK_IPUB - timedelta(days=7),
}
isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_ksk_doubleksk_step2(tld, alg, size, ns3):
zone = f"step2.ksk-doubleksk.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
if tld == "manual":
# Same as step 1.
step = {
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step2-p']}",
f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}",
],
"manual-mode": True,
"nextev": None,
}
keys = isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
# Check logs.
tag = keys[1].key.tag
msg = f"keymgr-manual-mode: block KSK rollover for key {zone}/ECDSAP256SHA256/{tag} (policy {policy})"
ns3.log.expect(msg)
# Force step.
with ns3.watch_log_from_here() as watcher:
ns3.rndc(f"dnssec -step {zone}")
watcher.wait_for_line(f"keymgr: {zone} done")
step = {
# Successor KSK is prepublished (and signs DNSKEY RRset).
# KSK1 goal: omnipresent -> hidden
# KSK2 goal: hidden -> omnipresent
# KSK2 dnskey: hidden -> rumoured
# KSK2 krrsig: hidden -> rumoured
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step2-p']}",
2025-06-10 16:33:08 +02:00
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.
2025-06-10 16:33:08 +02:00
"nextev": KSK_IPUB,
}
isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_ksk_doubleksk_step3(tld, alg, size, ns3):
zone = f"step3.ksk-doubleksk.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
if tld == "manual":
# Same as step 2, but DNSKEY has become OMNIPRESENT.
step = {
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step3-p']}",
f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}",
f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:hidden offset:{OFFSETS['step3-s']}",
],
"keyrelationships": [1, 2],
"manual-mode": True,
"nextev": None,
}
keys = isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
# Check logs.
tag = keys[2].key.tag
msg = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type DS state HIDDEN to state RUMOURED"
ns3.log.expect(msg)
# Force step.
with ns3.watch_log_from_here() as watcher:
ns3.rndc(f"dnssec -step {zone}")
watcher.wait_for_line(f"keymgr: {zone} done")
# Check logs.
tag = keys[1].key.tag
msg = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE"
if msg in ns3.log:
# Force step.
isctest.log.debug(
f"keymgr-manual-mode blocking transition KSK {zone}/ECDSAP256SHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE, step again"
)
with ns3.watch_log_from_here() as watcher:
ns3.rndc(f"dnssec -step {zone}")
watcher.wait_for_line(f"keymgr: {zone} done")
step = {
# The successor DNSKEY RRset has become omnipresent. The
# predecessor DS can be withdrawn and the successor DS can be
# introduced.
# KSK1 ds: omnipresent -> unretentive
# KSK2 dnskey: rumoured -> omnipresent
# KSK2 krrsig: rumoured -> omnipresent
# KSK2 ds: hidden -> rumoured
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step3-p']}",
2025-06-10 16:33:08 +02:00
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.
2025-06-10 16:33:08 +02:00
"nextev": KSK_IRET,
}
isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_ksk_doubleksk_step4(tld, alg, size, ns3):
zone = f"step4.ksk-doubleksk.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
if tld == "manual":
# Same as step 3, but DS has become HIDDEN/OMNIPRESENT.
step = {
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step4-p']}",
f"ksk {KSK_LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent 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],
"manual-mode": True,
"nextev": None,
}
keys = isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
# Check logs.
tag = keys[1].key.tag
msg1 = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE"
msg2 = f"keymgr-manual-mode: block transition KSK {zone}/ECDSAP256SHA256/{tag} type KRRSIG state OMNIPRESENT to state UNRETENTIVE"
ns3.log.expect(msg1)
ns3.log.expect(msg2)
# Force step.
with ns3.watch_log_from_here() as watcher:
ns3.rndc(f"dnssec -step {zone}")
watcher.wait_for_line(f"keymgr: {zone} done")
step = {
# The predecessor DNSKEY may be removed, the successor DS is
# omnipresent.
# KSK1 dnskey: omnipresent -> unretentive
# KSK1 krrsig: omnipresent -> unretentive
# KSK1 ds: unretentive -> hidden
# KSK2 ds: rumoured -> omnipresent
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step4-p']}",
2025-06-10 16:33:08 +02:00
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.
2025-06-10 16:33:08 +02:00
"nextev": KSK_KEYTTLPROP,
}
isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_ksk_doubleksk_step5(tld, alg, size, ns3):
zone = f"step5.ksk-doubleksk.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = {
# The predecessor DNSKEY is long enough removed from the zone it
# has become hidden.
# KSK1 dnskey: unretentive -> hidden
# KSK1 krrsig: unretentive -> hidden
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step5-p']}",
2025-06-10 16:33:08 +02:00
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.
2025-06-10 16:33:08 +02:00
"nextev": KSK_LIFETIME - KSK_IPUB - KSK_IRET - KSK_KEYTTLPROP,
}
isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step)
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_ksk_doubleksk_step6(tld, alg, size, ns3):
zone = f"step6.ksk-doubleksk.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
step = {
# Predecessor KSK is now purged.
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"zsk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step6-p']}",
2025-06-10 16:33:08 +02:00
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(ns3, KSK_CONFIG, policy, step)