Home | History | Annotate | Line # | Download | only in rollover-zsk-prepub
      1 # Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      2 #
      3 # SPDX-License-Identifier: MPL-2.0
      4 #
      5 # This Source Code Form is subject to the terms of the Mozilla Public
      6 # License, v. 2.0.  If a copy of the MPL was not distributed with this
      7 # file, you can obtain one at https://mozilla.org/MPL/2.0/.
      8 #
      9 # See the COPYRIGHT file distributed with this work for additional
     10 # information regarding copyright ownership.
     11 
     12 # pylint: disable=redefined-outer-name,unused-import
     13 
     14 from datetime import timedelta
     15 
     16 import pytest
     17 
     18 import isctest
     19 from isctest.kasp import Ipub, Iret
     20 from isctest.util import param
     21 from rollover.common import (
     22     pytestmark,
     23     alg,
     24     size,
     25     TIMEDELTA,
     26 )
     27 from rollover.setup import (
     28     configure_root,
     29     configure_tld,
     30     configure_zsk_prepub,
     31 )
     32 
     33 CONFIG = {
     34     "dnskey-ttl": TIMEDELTA["PT1H"],
     35     "ds-ttl": TIMEDELTA["P1D"],
     36     "max-zone-ttl": TIMEDELTA["P1D"],
     37     "parent-propagation-delay": TIMEDELTA["PT1H"],
     38     "publish-safety": TIMEDELTA["P1D"],
     39     "purge-keys": TIMEDELTA["PT1H"],
     40     "retire-safety": TIMEDELTA["P2D"],
     41     "signatures-refresh": TIMEDELTA["P7D"],
     42     "signatures-validity": TIMEDELTA["P14D"],
     43     "zone-propagation-delay": TIMEDELTA["PT1H"],
     44 }
     45 POLICY = "zsk-prepub"
     46 ZSK_LIFETIME = TIMEDELTA["P30D"]
     47 LIFETIME_POLICY = int(ZSK_LIFETIME.total_seconds())
     48 IPUB = Ipub(CONFIG)
     49 IRET = Iret(CONFIG)
     50 KEYTTLPROP = CONFIG["dnskey-ttl"] + CONFIG["zone-propagation-delay"]
     51 OFFSETS = {}
     52 OFFSETS["step1-p"] = -int(TIMEDELTA["P7D"].total_seconds())
     53 OFFSETS["step2-p"] = -int(ZSK_LIFETIME.total_seconds() - IPUB.total_seconds())
     54 OFFSETS["step2-s"] = 0
     55 OFFSETS["step3-p"] = -int(ZSK_LIFETIME.total_seconds())
     56 OFFSETS["step3-s"] = -int(IPUB.total_seconds())
     57 OFFSETS["step4-p"] = OFFSETS["step3-p"] - int(IRET.total_seconds())
     58 OFFSETS["step4-s"] = OFFSETS["step3-s"] - int(IRET.total_seconds())
     59 OFFSETS["step5-p"] = OFFSETS["step4-p"] - int(KEYTTLPROP.total_seconds())
     60 OFFSETS["step5-s"] = OFFSETS["step4-s"] - int(KEYTTLPROP.total_seconds())
     61 OFFSETS["step6-p"] = OFFSETS["step5-p"] - int(CONFIG["purge-keys"].total_seconds())
     62 OFFSETS["step6-s"] = OFFSETS["step5-s"] - int(CONFIG["purge-keys"].total_seconds())
     63 
     64 
     65 def bootstrap():
     66     data = {
     67         "tlds": [],
     68         "trust_anchors": [],
     69     }
     70 
     71     tlds = []
     72     for tld_name in [
     73         "autosign",
     74         "manual",
     75     ]:
     76         delegations = configure_zsk_prepub(tld_name)
     77 
     78         tld = configure_tld(tld_name, delegations)
     79         tlds.append(tld)
     80 
     81         data["tlds"].append(tld_name)
     82 
     83     ta = configure_root(tlds)
     84     data["trust_anchors"].append(ta)
     85 
     86     return data
     87 
     88 
     89 @pytest.mark.parametrize(
     90     "tld",
     91     [
     92         param("autosign"),
     93         param("manual"),
     94     ],
     95 )
     96 def test_zsk_prepub_step1(tld, alg, size, ns3):
     97     zone = f"step1.zsk-prepub.{tld}"
     98     policy = f"{POLICY}-{tld}"
     99 
    100     isctest.kasp.wait_keymgr_done(ns3, zone)
    101 
    102     # manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
    103     # Note that the key was already generated during setup.
    104 
    105     step = {
    106         # Introduce the first key. This will immediately be active.
    107         "zone": zone,
    108         "keyprops": [
    109             f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step1-p']}",
    110             f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step1-p']}",
    111         ],
    112         # Next key event is when the successor ZSK needs to be published.
    113         # That is the ZSK lifetime - prepublication time (minus time
    114         # already passed).
    115         "nextev": ZSK_LIFETIME - IPUB - timedelta(days=7),
    116     }
    117     isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    118 
    119 
    120 @pytest.mark.parametrize(
    121     "tld",
    122     [
    123         param("autosign"),
    124         param("manual"),
    125     ],
    126 )
    127 def test_zsk_prepub_step2(tld, alg, size, ns3):
    128     zone = f"step2.zsk-prepub.{tld}"
    129     policy = f"{POLICY}-{tld}"
    130 
    131     isctest.kasp.wait_keymgr_done(ns3, zone)
    132 
    133     if tld == "manual":
    134         # Same as step 1.
    135         step = {
    136             "zone": zone,
    137             "keyprops": [
    138                 f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}",
    139                 f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step2-p']}",
    140             ],
    141             "manual-mode": True,
    142             "nextev": None,
    143         }
    144         keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    145 
    146         # Check logs.
    147         tag = keys[1].key.tag
    148         msg = f"keymgr-manual-mode: block ZSK rollover for key {zone}/ECDSAP256SHA256/{tag} (policy {policy})"
    149         assert msg in ns3.log
    150 
    151         # Force step.
    152         with ns3.watch_log_from_here() as watcher:
    153             ns3.rndc(f"dnssec -step {zone}")
    154             watcher.wait_for_line(
    155                 f"zone {zone}/IN (signed): zone_rekey done: key {tag}/ECDSAP256SHA256"
    156             )
    157 
    158     step = {
    159         # it is time to pre-publish the successor zsk.
    160         # zsk1 goal: omnipresent -> hidden
    161         # zsk2 goal: hidden -> omnipresent
    162         # zsk2 dnskey: hidden -> rumoured
    163         "zone": zone,
    164         "keyprops": [
    165             f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step2-p']}",
    166             f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step2-p']}",
    167             f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:rumoured zrrsig:hidden offset:{OFFSETS['step2-s']}",
    168         ],
    169         "keyrelationships": [1, 2],
    170         # next key event is when the successor zsk becomes omnipresent.
    171         # that is the dnskey ttl plus the zone propagation delay
    172         "nextev": IPUB,
    173     }
    174     isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    175 
    176 
    177 @pytest.mark.parametrize(
    178     "tld",
    179     [
    180         param("autosign"),
    181         param("manual"),
    182     ],
    183 )
    184 def test_zsk_prepub_step3(tld, alg, size, ns3):
    185     zone = f"step3.zsk-prepub.{tld}"
    186     policy = f"{POLICY}-{tld}"
    187 
    188     isctest.kasp.wait_keymgr_done(ns3, zone)
    189 
    190     if tld == "manual":
    191         # Same as step 2, but DNSKEY has become OMNIPRESENT.
    192         step = {
    193             "zone": zone,
    194             "keyprops": [
    195                 f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}",
    196                 f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step3-p']}",
    197                 f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:hidden offset:{OFFSETS['step3-s']}",
    198             ],
    199             "keyrelationships": [1, 2],
    200             "manual-mode": True,
    201             "nextev": None,
    202         }
    203         keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    204 
    205         # Check logs.
    206         tag = keys[2].key.tag
    207         msg = f"keymgr-manual-mode: block transition ZSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state HIDDEN to state RUMOURED"
    208         assert msg in ns3.log
    209 
    210         # Force step.
    211         with ns3.watch_log_from_here() as watcher:
    212             ns3.rndc(f"dnssec -step {zone}")
    213             watcher.wait_for_line(
    214                 f"zone {zone}/IN (signed): zone_rekey done: key {tag}/ECDSAP256SHA256"
    215             )
    216 
    217         # Check logs.
    218         tag = keys[1].key.tag
    219         msg = f"keymgr-manual-mode: block transition ZSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE"
    220         if msg in ns3.log:
    221             # Force step.
    222             isctest.log.debug(
    223                 f"keymgr-manual-mode blocking transition ZSK {zone}/ECDSAP256SHA256/{tag} type ZRRSIG state OMNIPRESENT to state UNRETENTIVE, step again"
    224             )
    225             with ns3.watch_log_from_here() as watcher:
    226                 ns3.rndc(f"dnssec -step {zone}")
    227                 watcher.wait_for_line(
    228                     f"zone {zone}/IN (signed): zone_rekey done: key {tag}/ECDSAP256SHA256"
    229                 )
    230 
    231     step = {
    232         # predecessor zsk is no longer actively signing. successor zsk is
    233         # now actively signing.
    234         # zsk1 zrrsig: omnipresent -> unretentive
    235         # zsk2 dnskey: rumoured -> omnipresent
    236         # zsk2 zrrsig: hidden -> rumoured
    237         "zone": zone,
    238         "keyprops": [
    239             f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step3-p']}",
    240             f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:unretentive offset:{OFFSETS['step3-p']}",
    241             f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:rumoured offset:{OFFSETS['step3-s']}",
    242         ],
    243         "keyrelationships": [1, 2],
    244         # next key event is when all the rrsig records have been replaced
    245         # with signatures of the new zsk, in other words when zrrsig
    246         # becomes omnipresent.
    247         "nextev": IRET,
    248         # set 'smooth' to true so expected signatures of subdomain are
    249         # from the predecessor zsk.
    250         "smooth": True,
    251     }
    252     isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    253 
    254     # Force full resign and check all signatures have been replaced.
    255     with ns3.watch_log_from_here() as watcher:
    256         ns3.rndc(f"sign {zone}")
    257         watcher.wait_for_line(f"zone {zone}/IN (signed): sending notifies")
    258 
    259     step["smooth"] = False
    260     step["nextev"] = Iret(CONFIG, smooth=False)
    261     isctest.kasp.check_rollover_step(ns3, CONFIG, POLICY, step)
    262 
    263 
    264 @pytest.mark.parametrize(
    265     "tld",
    266     [
    267         param("autosign"),
    268         param("manual"),
    269     ],
    270 )
    271 def test_zsk_prepub_step4(tld, alg, size, ns3):
    272     zone = f"step4.zsk-prepub.{tld}"
    273     policy = f"{POLICY}-{tld}"
    274 
    275     isctest.kasp.wait_keymgr_done(ns3, zone)
    276 
    277     if tld == "manual":
    278         # Same as step 3, but zone signatures have become HIDDEN/OMNIPRESENT.
    279         step = {
    280             "zone": zone,
    281             "keyprops": [
    282                 f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step4-p']}",
    283                 f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:omnipresent zrrsig:hidden offset:{OFFSETS['step4-p']}",
    284                 f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step4-s']}",
    285             ],
    286             "keyrelationships": [1, 2],
    287             "manual-mode": True,
    288             "nextev": None,
    289         }
    290         keys = isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    291 
    292         # Check logs.
    293         tag = keys[1].key.tag
    294         msg = f"keymgr-manual-mode: block transition ZSK {zone}/ECDSAP256SHA256/{tag} type DNSKEY state OMNIPRESENT to state UNRETENTIVE"
    295         assert msg in ns3.log
    296 
    297         # Force step.
    298         tag = keys[2].key.tag
    299         with ns3.watch_log_from_here() as watcher:
    300             ns3.rndc(f"dnssec -step {zone}")
    301             watcher.wait_for_line(
    302                 f"zone {zone}/IN (signed): zone_rekey done: key {tag}/ECDSAP256SHA256"
    303             )
    304 
    305     step = {
    306         # predecessor zsk is no longer needed. all rrsets are signed with
    307         # the successor zsk.
    308         # zsk1 dnskey: omnipresent -> unretentive
    309         # zsk1 zrrsig: unretentive -> hidden
    310         # zsk2 zrrsig: rumoured -> omnipresent
    311         "zone": zone,
    312         "keyprops": [
    313             f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step4-p']}",
    314             f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:unretentive zrrsig:hidden offset:{OFFSETS['step4-p']}",
    315             f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step4-s']}",
    316         ],
    317         "keyrelationships": [1, 2],
    318         # next key event is when the dnskey enters the hidden state.
    319         # this is the dnskey ttl plus zone propagation delay.
    320         "nextev": KEYTTLPROP,
    321     }
    322     isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    323 
    324 
    325 @pytest.mark.parametrize(
    326     "tld",
    327     [
    328         param("autosign"),
    329         param("manual"),
    330     ],
    331 )
    332 def test_zsk_prepub_step5(tld, alg, size, ns3):
    333     zone = f"step5.zsk-prepub.{tld}"
    334     policy = f"{POLICY}-{tld}"
    335 
    336     isctest.kasp.wait_keymgr_done(ns3, zone)
    337 
    338     # manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
    339 
    340     step = {
    341         # predecessor zsk is now removed.
    342         # zsk1 dnskey: unretentive -> hidden
    343         "zone": zone,
    344         "keyprops": [
    345             f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step5-p']}",
    346             f"zsk {LIFETIME_POLICY} {alg} {size} goal:hidden dnskey:hidden zrrsig:hidden offset:{OFFSETS['step5-p']}",
    347             f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step5-s']}",
    348         ],
    349         "keyrelationships": [1, 2],
    350         # next key event is when the new successor needs to be published.
    351         # this is the zsk lifetime minus IRET minus IPUB minus time
    352         # elapsed.
    353         "nextev": ZSK_LIFETIME - IRET - IPUB - KEYTTLPROP,
    354     }
    355     isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    356 
    357 
    358 @pytest.mark.parametrize(
    359     "tld",
    360     [
    361         param("autosign"),
    362         param("manual"),
    363     ],
    364 )
    365 def test_zsk_prepub_step6(tld, alg, size, ns3):
    366     zone = f"step6.zsk-prepub.{tld}"
    367     policy = f"{POLICY}-{tld}"
    368 
    369     isctest.kasp.wait_keymgr_done(ns3, zone)
    370 
    371     # manual-mode: Nothing changing in the zone, no 'dnssec -step' required.
    372 
    373     step = {
    374         # predecessor zsk is now purged.
    375         "zone": zone,
    376         "keyprops": [
    377             f"ksk unlimited {alg} {size} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent offset:{OFFSETS['step6-p']}",
    378             f"zsk {LIFETIME_POLICY} {alg} {size} goal:omnipresent dnskey:omnipresent zrrsig:omnipresent offset:{OFFSETS['step6-s']}",
    379         ],
    380         "nextev": None,
    381     }
    382     isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step)
    383