mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 10:10:06 +00:00
Update check_rollover_step to return the found keys. This can be used to test that keymgr-manual-mode messages are correctly logged. 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.
325 lines
12 KiB
Python
325 lines
12 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
|
|
|
|
import pytest
|
|
|
|
import isctest
|
|
from isctest.kasp import KeyTimingMetadata
|
|
from isctest.util import param
|
|
from rollover.common import (
|
|
pytestmark,
|
|
alg,
|
|
size,
|
|
CDSS,
|
|
ALGOROLL_CONFIG,
|
|
ALGOROLL_IPUB,
|
|
ALGOROLL_IPUBC,
|
|
ALGOROLL_IRET,
|
|
ALGOROLL_IRETKSK,
|
|
ALGOROLL_KEYTTLPROP,
|
|
ALGOROLL_OFFSETS,
|
|
ALGOROLL_OFFVAL,
|
|
DURATION,
|
|
TIMEDELTA,
|
|
)
|
|
|
|
CONFIG = ALGOROLL_CONFIG
|
|
POLICY = "csk-algoroll"
|
|
TIME_PASSED = 0 # set in reconfigure() fixture
|
|
|
|
|
|
@pytest.fixture(scope="module", autouse=True)
|
|
def reconfigure(ns6, templates):
|
|
global TIME_PASSED # pylint: disable=global-statement
|
|
|
|
isctest.kasp.wait_keymgr_done(ns6, "step1.csk-algorithm-roll.kasp")
|
|
|
|
templates.render("ns6/named.conf", {"csk_roll": True})
|
|
start_time = KeyTimingMetadata.now()
|
|
ns6.reconfigure()
|
|
|
|
# Calculate time passed to correctly check for next key events.
|
|
TIME_PASSED = KeyTimingMetadata.now().value - start_time.value
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("kasp"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_algoroll_csk_reconfig_step1(tld, ns6, alg, size):
|
|
zone = f"step1.csk-algorithm-roll.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
|
|
|
|
if tld == "manual":
|
|
# Same as initial.
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
f"csk 0 8 2048 goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{-DURATION['P7D']}",
|
|
],
|
|
"manual-mode": True,
|
|
"nextev": None,
|
|
}
|
|
keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
# Check logs.
|
|
tag = keys[0].key.tag
|
|
msg1 = f"keymgr-manual-mode: block retire DNSKEY {zone}/RSASHA256/{tag} (CSK)"
|
|
msg2 = f"keymgr-manual-mode: block new key generation for zone {zone} (policy {policy})"
|
|
ns6.log.expect(msg1)
|
|
ns6.log.expect(msg2)
|
|
|
|
# Force step.
|
|
with ns6.watch_log_from_here() as watcher:
|
|
ns6.rndc(f"dnssec -step {zone}")
|
|
watcher.wait_for_line(f"keymgr: {zone} done")
|
|
|
|
# Check state after step.
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
# The RSASHA keys are outroducing.
|
|
f"csk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFVAL}",
|
|
# The ECDSAP256SHA256 keys are introducing.
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
|
],
|
|
# Next key event is when the ecdsa256 keys have been propagated.
|
|
"nextev": ALGOROLL_IPUB,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("kasp"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_algoroll_csk_reconfig_step2(tld, ns6, alg, size):
|
|
zone = f"step2.csk-algorithm-roll.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
|
|
|
|
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
# The RSASHA keys are outroducing, but need to stay present
|
|
# until the new algorithm chain of trust has been established.
|
|
# Thus the expected key states of these keys stay the same.
|
|
f"csk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFVAL}",
|
|
# The ECDSAP256SHA256 keys are introducing. The DNSKEY RRset is
|
|
# omnipresent, but the zone signatures are not.
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:rumoured ds:hidden offset:{ALGOROLL_OFFSETS['step2']}",
|
|
],
|
|
# Next key event is when all zone signatures are signed with the
|
|
# new algorithm. This is the child publication interval, minus
|
|
# the publication interval has already passed. Also, prevent
|
|
# intermittent false positives on slow platforms by subtracting
|
|
# the time passed between key creation and invoking 'rndc reconfig'.
|
|
"nextev": ALGOROLL_IPUBC - ALGOROLL_IPUB - TIME_PASSED,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("kasp"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_algoroll_csk_reconfig_step3(tld, ns6, alg, size):
|
|
zone = f"step3.csk-algorithm-roll.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
|
|
|
|
if tld == "manual":
|
|
# Same as step 2, but the zone signatures have become OMNIPRESENT.
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
f"csk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFVAL}",
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:hidden offset:{ALGOROLL_OFFSETS['step3']}",
|
|
],
|
|
"manual-mode": True,
|
|
"nextev": None,
|
|
}
|
|
keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
# Check logs.
|
|
tag = keys[1].key.tag
|
|
msg = f"keymgr-manual-mode: block transition CSK {zone}/ECDSAP256SHA256/{tag} type DS state HIDDEN to state RUMOURED"
|
|
ns6.log.expect(msg)
|
|
|
|
# Force step.
|
|
with ns6.watch_log_from_here() as watcher:
|
|
ns6.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}/RSASHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE"
|
|
if msg in ns6.log:
|
|
# Force step.
|
|
isctest.log.debug(
|
|
f"keymgr-manual-mode blocking transition CSK {zone}/RSASHA256/{tag} type DS state OMNIPRESENT to state UNRETENTIVE, step again"
|
|
)
|
|
with ns6.watch_log_from_here() as watcher:
|
|
ns6.rndc(f"dnssec -step {zone}")
|
|
watcher.wait_for_line(f"keymgr: {zone} done")
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
# The DS can be swapped.
|
|
f"csk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:unretentive offset:{ALGOROLL_OFFVAL}",
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:rumoured offset:{ALGOROLL_OFFSETS['step3']}",
|
|
],
|
|
# Next key event is when the DS becomes OMNIPRESENT. This happens
|
|
# after the publication interval of the parent side.
|
|
"nextev": ALGOROLL_IRETKSK - TIME_PASSED,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("kasp"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_algoroll_csk_reconfig_step4(tld, ns6, alg, size):
|
|
zone = f"step4.csk-algorithm-roll.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
|
|
|
|
if tld == "manual":
|
|
# Same as step 3, but the DS has become HIDDEN/OMNIPRESENT.
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
f"csk 0 8 2048 goal:hidden dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:hidden offset:{ALGOROLL_OFFVAL}",
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFSETS['step4']}",
|
|
],
|
|
"manual-mode": True,
|
|
"nextev": None,
|
|
}
|
|
keys = isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
# Check logs.
|
|
tag = keys[0].key.tag
|
|
msg = f"keymgr-manual-mode: block transition CSK {zone}/RSASHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE"
|
|
ns6.log.expect(msg)
|
|
|
|
# Force step.
|
|
with ns6.watch_log_from_here() as watcher:
|
|
ns6.rndc(f"dnssec -step {zone}")
|
|
watcher.wait_for_line(f"keymgr: {zone} done")
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
# The old DS is HIDDEN, we can remove the old algorithm records.
|
|
f"csk 0 8 2048 goal:hidden dnskey:unretentive krrsig:unretentive zrrsig:unretentive ds:hidden offset:{ALGOROLL_OFFVAL}",
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFSETS['step4']}",
|
|
],
|
|
# Next key event is when the old DNSKEY becomes HIDDEN.
|
|
# This happens after the DNSKEY TTL plus zone propagation delay.
|
|
"nextev": ALGOROLL_KEYTTLPROP,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("kasp"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_algoroll_csk_reconfig_step5(tld, ns6, alg, size):
|
|
zone = f"step5.csk-algorithm-roll.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
|
|
|
|
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
# The DNSKEY becomes HIDDEN.
|
|
f"csk 0 8 2048 goal:hidden dnskey:hidden krrsig:hidden zrrsig:unretentive ds:hidden offset:{ALGOROLL_OFFVAL}",
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFSETS['step5']}",
|
|
],
|
|
# Next key event is when the RSASHA signatures become HIDDEN.
|
|
# This happens after the max-zone-ttl plus zone propagation delay
|
|
# minus the time already passed since the UNRETENTIVE state has
|
|
# been reached. Prevent intermittent false positives on slow
|
|
# platforms by subtracting the number of seconds which passed
|
|
# between key creation and invoking 'rndc reconfig'.
|
|
"nextev": ALGOROLL_IRET - ALGOROLL_IRETKSK - ALGOROLL_KEYTTLPROP - TIME_PASSED,
|
|
}
|
|
isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tld",
|
|
[
|
|
param("kasp"),
|
|
param("manual"),
|
|
],
|
|
)
|
|
def test_algoroll_csk_reconfig_step6(tld, ns6, alg, size):
|
|
zone = f"step6.csk-algorithm-roll.{tld}"
|
|
policy = f"{POLICY}-{tld}"
|
|
|
|
isctest.kasp.wait_keymgr_done(ns6, zone, reconfig=True)
|
|
|
|
# manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
|
|
|
|
step = {
|
|
"zone": zone,
|
|
"cdss": CDSS,
|
|
"keyprops": [
|
|
# The zone signatures are now HIDDEN.
|
|
f"csk 0 8 2048 goal:hidden dnskey:hidden krrsig:hidden zrrsig:hidden ds:hidden offset:{ALGOROLL_OFFVAL}",
|
|
f"csk 0 {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent offset:{ALGOROLL_OFFSETS['step6']}",
|
|
],
|
|
# Next key event is never since we established the policy and the
|
|
# keys have an unlimited lifetime. Fallback to the default
|
|
# loadkeys interval.
|
|
"nextev": TIMEDELTA["PT1H"],
|
|
}
|
|
isctest.kasp.check_rollover_step(ns6, CONFIG, policy, step)
|