2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00
bind/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py

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

401 lines
15 KiB
Python
Raw Normal View History

2025-06-09 16:33:13 +02: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.
# pylint: disable=redefined-outer-name,unused-import
from datetime import timedelta
import pytest
2025-06-09 16:33:13 +02:00
import isctest
from isctest.kasp import Ipub, Iret
from isctest.util import param
from rollover.common import (
2025-06-09 16:33:13 +02:00
pytestmark,
alg,
size,
TIMEDELTA,
)
CDSS = ["CDNSKEY", "CDS (SHA-256)", "CDS (SHA-384)"]
CONFIG = {
"dnskey-ttl": TIMEDELTA["PT1H"],
"ds-ttl": TIMEDELTA["PT1H"],
"max-zone-ttl": TIMEDELTA["P1D"],
"parent-propagation-delay": TIMEDELTA["P7D"],
"publish-safety": TIMEDELTA["PT1H"],
"purge-keys": TIMEDELTA[0],
"retire-safety": TIMEDELTA["PT1H"],
"signatures-refresh": TIMEDELTA["PT12H"],
"signatures-validity": TIMEDELTA["P1D"],
"zone-propagation-delay": TIMEDELTA["PT1H"],
}
POLICY = "csk-roll2"
CSK_LIFETIME = timedelta(days=31 * 6)
LIFETIME_POLICY = int(CSK_LIFETIME.total_seconds())
IPUB = Ipub(CONFIG)
IRET = Iret(CONFIG, zsk=True, ksk=True)
IRETZSK = Iret(CONFIG)
IRETKSK = Iret(CONFIG, ksk=True)
KEYTTLPROP = CONFIG["dnskey-ttl"] + CONFIG["zone-propagation-delay"]
OFFSETS = {}
OFFSETS["step1-p"] = -int(timedelta(days=7).total_seconds())
OFFSETS["step2-p"] = -int(CSK_LIFETIME.total_seconds() - IPUB.total_seconds())
OFFSETS["step2-s"] = 0
OFFSETS["step3-p"] = -int(CSK_LIFETIME.total_seconds())
OFFSETS["step3-s"] = -int(IPUB.total_seconds())
OFFSETS["step4-p"] = OFFSETS["step3-p"] - int(IRETZSK.total_seconds())
OFFSETS["step4-s"] = OFFSETS["step3-s"] - int(IRETZSK.total_seconds())
OFFSETS["step5-p"] = OFFSETS["step4-p"] - int(
IRETKSK.total_seconds() - IRETZSK.total_seconds()
)
OFFSETS["step5-s"] = OFFSETS["step4-s"] - int(
IRETKSK.total_seconds() - IRETZSK.total_seconds()
)
OFFSETS["step6-p"] = OFFSETS["step5-p"] - int(KEYTTLPROP.total_seconds())
OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(KEYTTLPROP.total_seconds())
OFFSETS["step7-p"] = OFFSETS["step6-p"] - int(timedelta(days=90).total_seconds())
OFFSETS["step7-s"] = OFFSETS["step6-s"] - int(timedelta(days=90).total_seconds())
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_csk_roll2_step1(tld, alg, size, ns3):
zone = f"step1.csk-roll2.{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.
2025-06-09 16:33:13 +02:00
step = {
# Introduce the first key. This will immediately be active.
"zone": zone,
2025-06-09 16:33:13 +02:00
"cdss": CDSS,
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step1-p']}",
],
# Next key event is when the successor CSK needs to be published
# minus time already elapsed. This is Lcsk - Ipub + Dreg (we ignore
# registration delay).
"nextev": CSK_LIFETIME - IPUB - TIMEDELTA["P7D"],
}
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
2025-06-09 16:33:13 +02:00
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_csk_roll2_step2(tld, alg, size, ns3):
zone = f"step2.csk-roll2.{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"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}",
],
"manual-mode": True,
"nextev": None,
}
keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
# Check logs.
tag = keys[0].key.tag
msg = f"keymgr-manual-mode: block CSK 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")
2025-06-09 16:33:13 +02:00
step = {
# Successor CSK is prepublished (signs DNSKEY RRset, but not yet
# other RRsets).
# CSK1 goal: omnipresent -> hidden
# CSK2 goal: hidden -> omnipresent
# CSK2 dnskey: hidden -> rumoured
# CSK2 krrsig: hidden -> rumoured
"zone": zone,
2025-06-09 16:33:13 +02:00
"cdss": CDSS,
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:hidden ds:hidden offset:{OFFSETS['step2-s']}",
],
"keyrelationships": [0, 1],
# Next key event is when the successor CSK becomes OMNIPRESENT.
"nextev": IPUB,
}
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
2025-06-09 16:33:13 +02:00
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_csk_roll2_step3(tld, alg, size, ns3):
zone = f"step3.csk-roll2.{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"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:hidden ds:hidden offset:{OFFSETS['step3-s']}",
],
"keyrelationships": [0, 1],
"manual-mode": True,
"nextev": None,
}
keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
# Check logs.
tag = keys[1].key.tag
msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG 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[0].key.tag
msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE"
if msg in ns3.log:
# Force step.
isctest.log.debug(
f"keymgr-manual-mode blocking transition CSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG 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")
2025-06-09 16:33:13 +02:00
step = {
# Successor CSK becomes omnipresent, meaning we can start signing
# the remainder of the zone with the successor CSK, and we can
# submit the DS.
"zone": zone,
2025-06-09 16:33:13 +02:00
"cdss": CDSS,
# Predecessor CSK will be removed, so moving to UNRETENTIVE.
# CSK1 zrrsig: omnipresent -> unretentive
# Successor CSK DNSKEY is OMNIPRESENT, so moving ZRRSIG to RUMOURED.
# CSK2 dnskey: rumoured -> omnipresent
# CSK2 krrsig: rumoured -> omnipresent
# CSK2 zrrsig: hidden -> rumoured
# The predecessor DS can be withdrawn and the successor DS can be
# introduced.
# CSK1 ds: omnipresent -> unretentive
# CSK2 ds: hidden -> rumoured
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:unretentive ds:unretentive offset:{OFFSETS['step3-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:rumoured ds:rumoured offset:{OFFSETS['step3-s']}",
],
"keyrelationships": [0, 1],
# 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": IRETZSK,
# Set 'smooth' to true so expected signatures of subdomain are
# from the predecessor ZSK.
"smooth": True,
}
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
2025-06-09 16:33:13 +02:00
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_csk_roll2_step4(tld, alg, size, ns3):
zone = f"step4.csk-roll2.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
2025-06-09 16:33:13 +02:00
step = {
"zone": zone,
2025-06-09 16:33:13 +02:00
"cdss": CDSS,
# The predecessor ZRRSIG is HIDDEN. The successor ZRRSIG is
# OMNIPRESENT.
# CSK1 zrrsig: unretentive -> hidden
# CSK2 zrrsig: rumoured -> omnipresent
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:hidden ds:unretentive offset:{OFFSETS['step4-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:rumoured offset:{OFFSETS['step4-s']}",
],
"keyrelationships": [0, 1],
# 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 retire interval of the KSK part (minus)
# time already elapsed).
"nextev": IRET - IRETZSK,
# We already swapped the DS in the previous step, so disable ds-swap.
"ds-swap": False,
}
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
2025-06-09 16:33:13 +02:00
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_csk_roll2_step5(tld, alg, size, ns3):
zone = f"step5.csk-roll2.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
if tld == "manual":
# Same as step 4, but DS has become HIDDEN/OMNIPRESENT.
step = {
"zone": zone,
"cdss": CDSS,
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:hidden ds:hidden offset:{OFFSETS['step5-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step5-s']}",
],
"keyrelationships": [0, 1],
"manual-mode": True,
"nextev": None,
}
keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
# Check logs.
tag = keys[0].key.tag
msg1 = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE"
msg2 = f"keymgr-manual-mode: block transition CSK {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")
2025-06-09 16:33:13 +02:00
step = {
"zone": zone,
2025-06-09 16:33:13 +02:00
"cdss": CDSS,
# The predecessor DNSKEY can be removed.
# CSK1 dnskey: omnipresent -> unretentive
# CSK1 krrsig: omnipresent -> unretentive
# CSK1 ds: unretentive -> hidden
# The successor key is now fully OMNIPRESENT.
# CSK2 ds: rumoured -> omnipresent
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:unretentive krrsig:unretentive zrrsig:hidden ds:hidden offset:{OFFSETS['step5-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step5-s']}",
],
"keyrelationships": [0, 1],
# Next key event is when the DNSKEY enters the HIDDEN state.
# This is the DNSKEY TTL plus zone propagation delay.
"nextev": KEYTTLPROP,
}
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
2025-06-09 16:33:13 +02:00
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_csk_roll2_step6(tld, alg, size, ns3):
zone = f"step6.csk-roll2.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
2025-06-09 16:33:13 +02:00
step = {
"zone": zone,
2025-06-09 16:33:13 +02:00
"cdss": CDSS,
# The predecessor CSK is now completely HIDDEN.
# CSK1 dnskey: unretentive -> hidden
# CSK1 krrsig: unretentive -> hidden
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:hidden krrsig:hidden zrrsig:hidden ds:hidden offset:{OFFSETS['step6-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step6-s']}",
],
"keyrelationships": [0, 1],
# Next key event is when the new successor needs to be published.
# This is the Lcsk, minus time passed since the key was published.
"nextev": CSK_LIFETIME - IRET - IPUB - KEYTTLPROP,
}
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
2025-06-09 16:33:13 +02:00
@pytest.mark.parametrize(
"tld",
[
param("autosign"),
param("manual"),
],
)
def test_csk_roll2_step7(tld, alg, size, ns3):
zone = f"step7.csk-roll2.{tld}"
policy = f"{POLICY}-{tld}"
isctest.kasp.wait_keymgr_done(ns3, zone)
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
2025-06-09 16:33:13 +02:00
step = {
"zone": zone,
2025-06-09 16:33:13 +02:00
"cdss": CDSS,
# The predecessor CSK is now completely HIDDEN.
"keyprops": [
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:hidden krrsig:hidden zrrsig:hidden ds:hidden offset:{OFFSETS['step7-p']}",
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step7-s']}",
],
"keyrelationships": [0, 1],
"nextev": None,
}
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)