mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 01:59:26 +00:00
Similar to previous commits. Parametrize each test case and in case of manual-mode, execute additional checks. First a keymgr run should not change the existing key state (with exceptions of timing events such as moving from RUMOURED to OMNIPRESENT, and from UNRETENTIVE to HIDDEN). Appropriate messages must be logged. After enforcing the next step with 'rndc dnssec -step', the key state should be the same as if the step were to be taken automatically.
426 lines
16 KiB
Python
426 lines
16 KiB
Python
# 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.kasp import Ipub, Iret
|
|
from isctest.util import param
|
|
from rollover.common import (
|
|
pytestmark,
|
|
alg,
|
|
size,
|
|
TIMEDELTA,
|
|
)
|
|
|
|
|
|
CDSS = ["CDNSKEY", "CDS (SHA-384)"]
|
|
CONFIG = {
|
|
"dnskey-ttl": TIMEDELTA["PT1H"],
|
|
"ds-ttl": TIMEDELTA["PT1H"],
|
|
"max-zone-ttl": TIMEDELTA["P1D"],
|
|
"parent-propagation-delay": TIMEDELTA["PT1H"],
|
|
"publish-safety": TIMEDELTA["PT1H"],
|
|
"purge-keys": TIMEDELTA["PT1H"],
|
|
"retire-safety": TIMEDELTA["PT2H"],
|
|
"signatures-refresh": TIMEDELTA["P5D"],
|
|
"signatures-validity": TIMEDELTA["P30D"],
|
|
"zone-propagation-delay": TIMEDELTA["PT1H"],
|
|
}
|
|
POLICY = "csk-roll1"
|
|
CSK_LIFETIME = timedelta(days=31 * 6)
|
|
LIFETIME_POLICY = int(CSK_LIFETIME.total_seconds())
|
|
IPUB = Ipub(CONFIG)
|
|
IRETZSK = Iret(CONFIG)
|
|
IRETKSK = Iret(CONFIG, zsk=False, ksk=True)
|
|
KEYTTLPROP = CONFIG["dnskey-ttl"] + CONFIG["zone-propagation-delay"]
|
|
SIGNDELAY = IRETZSK - IRETKSK - KEYTTLPROP
|
|
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(IRETKSK.total_seconds())
|
|
OFFSETS["step4-s"] = OFFSETS["step3-s"] - int(IRETKSK.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(SIGNDELAY.total_seconds())
|
|
OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(SIGNDELAY.total_seconds())
|
|
OFFSETS["step7-p"] = OFFSETS["step6-p"] - int(KEYTTLPROP.total_seconds())
|
|
OFFSETS["step7-s"] = OFFSETS["step6-s"] - int(KEYTTLPROP.total_seconds())
|
|
OFFSETS["step8-p"] = OFFSETS["step7-p"] - int(CONFIG["purge-keys"].total_seconds())
|
|
OFFSETS["step8-s"] = OFFSETS["step7-s"] - int(CONFIG["purge-keys"].total_seconds())
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step1(tld, alg, size, ns3):
|
|
zone = f"step1.csk-roll1.{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"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(days=7),
|
|
}
|
|
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step2(tld, alg, size, ns3):
|
|
zone = f"step2.csk-roll1.{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")
|
|
|
|
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,
|
|
"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)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step3(tld, alg, size, ns3):
|
|
zone = f"step3.csk-roll1.{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")
|
|
|
|
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,
|
|
"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": IRETKSK,
|
|
# 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)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step4(tld, alg, size, ns3):
|
|
zone = f"step4.csk-roll1.{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"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:unretentive ds:hidden offset:{OFFSETS['step4-p']}",
|
|
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:rumoured ds:omnipresent offset:{OFFSETS['step4-s']}",
|
|
],
|
|
"keyrelationships": [0, 1],
|
|
"manual-mode": True,
|
|
"nextev": None,
|
|
# We already swapped the DS in the previous step, so disable ds-swap.
|
|
"ds-swap": False,
|
|
}
|
|
keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
|
|
|
|
# Check logs.
|
|
tag = keys[0].key.tag
|
|
msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type KRRSIG state OMNIPRESENT to state UNRETENTIVE"
|
|
|
|
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 = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
# The predecessor CSK is no longer signing the DNSKEY RRset.
|
|
# CSK1 krrsig: omnipresent -> unretentive
|
|
# The predecessor DS is hidden. The successor DS is now omnipresent.
|
|
# CSK1 ds: unretentive -> hidden
|
|
# CSK2 ds: rumoured -> omnipresent
|
|
"keyprops": [
|
|
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:unretentive zrrsig:unretentive ds:hidden offset:{OFFSETS['step4-p']}",
|
|
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:rumoured ds:omnipresent offset:{OFFSETS['step4-s']}",
|
|
],
|
|
"keyrelationships": [0, 1],
|
|
# Next key event is when the KRRSIG enters the HIDDEN state.
|
|
# This is the DNSKEY TTL plus zone propagation delay.
|
|
"nextev": KEYTTLPROP,
|
|
# 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)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step5(tld, alg, size, ns3):
|
|
zone = f"step5.csk-roll1.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns3, zone)
|
|
|
|
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
# The predecessor KRRSIG records are now all hidden.
|
|
# CSK1 krrsig: unretentive -> hidden
|
|
"keyprops": [
|
|
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent krrsig:hidden zrrsig:unretentive ds:hidden offset:{OFFSETS['step5-p']}",
|
|
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:rumoured ds:omnipresent offset:{OFFSETS['step5-s']}",
|
|
],
|
|
"keyrelationships": [0, 1],
|
|
# Next key event is when the DNSKEY can be removed. This is when
|
|
# all ZRRSIG records have been replaced with signatures of the new
|
|
# CSK.
|
|
"nextev": SIGNDELAY,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step6(tld, alg, size, ns3):
|
|
zone = f"step6.csk-roll1.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns3, zone)
|
|
|
|
if tld == "manual":
|
|
return
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
# The predecessor ZRRSIG records are now all hidden (so the DNSKEY
|
|
# can be removed).
|
|
# CSK1 dnskey: omnipresent -> unretentive
|
|
# CSK1 zrrsig: unretentive -> hidden
|
|
# CSK2 zrrsig: rumoured -> omnipresent
|
|
"keyprops": [
|
|
f"csk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:unretentive 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 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)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step7(tld, alg, size, ns3):
|
|
zone = f"step7.csk-roll1.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns3, zone)
|
|
|
|
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"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],
|
|
# Next key event is when the new successor needs to be published.
|
|
# This is the Lcsk, minus time passed since the key started signing,
|
|
# minus the prepublication time.
|
|
"nextev": CSK_LIFETIME - IRETZSK - IPUB - KEYTTLPROP,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("autosign"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_csk_roll1_step8(tld, alg, size, ns3):
|
|
zone = f"step8.csk-roll1.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns3, zone)
|
|
|
|
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
f"csk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{OFFSETS['step8-s']}",
|
|
],
|
|
"nextev": None,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
|