mirror of
https://github.com/isc-projects/bind9.git
synced 2026-07-16 02:12:50 -04:00
Merge kasp.Key functionality into zone.FileZoneKey
Make zone.FileZoneKey the single representation of a file-backed key (typically generated by dnssec-keygen). Move the common key-related functionality into zone.FileZoneKey, and extend that functionality in kasp.Key to also add state and timing related operations on top. Remove duplicate into_ta() function. Note that is_ksk() is implemented differently for kasp.Key: with the metadata file available, the KSK status is loaded from that file, as it indicates the authoritative policy decision which makes the key a KSK. In zone.FileZoneKey which doesn't work with the metadata file, the KSK status if inferred from the DNSKEY SEP flag - the best information available for that class. Assisted-by: Claude:claude-opus-4-8
This commit is contained in:
parent
9dc039f735
commit
35fc5ce8b4
2 changed files with 29 additions and 40 deletions
|
|
@ -20,22 +20,19 @@ import os
|
|||
import re
|
||||
import time
|
||||
|
||||
import dns.dnssec
|
||||
import dns.exception
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rcode
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.rrset
|
||||
import dns.tsig
|
||||
import dns.zone
|
||||
import dns.zonefile
|
||||
|
||||
from isctest.instance import NamedInstance
|
||||
from isctest.run import EnvCmd
|
||||
from isctest.template import TrustAnchor
|
||||
from isctest.vars.algorithms import ALL_ALGORITHMS_BY_NUM, Algorithm
|
||||
from isctest.zone import FileZoneKey
|
||||
|
||||
import isctest.log
|
||||
import isctest.query
|
||||
|
|
@ -380,26 +377,18 @@ class SettimeOptions:
|
|||
|
||||
|
||||
@total_ordering
|
||||
class Key:
|
||||
class Key(FileZoneKey):
|
||||
"""
|
||||
Represent a key from a keyfile.
|
||||
A FileZoneKey specialized with KASP timing and state-file operations.
|
||||
|
||||
This object keeps track of its origin (keydir + name), can be used to
|
||||
retrieve metadata from the underlying files and supports convenience
|
||||
operations for KASP tests.
|
||||
Inherits the key-material accessors (dnskey, into_ta, ...) from FileZoneKey
|
||||
and adds the metadata reads, signing-state derivation, and timing
|
||||
convenience operations used by the KASP/rollover tests.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, keydir: str | Path | None = None):
|
||||
self.name = name
|
||||
if keydir is None:
|
||||
self.keydir = Path()
|
||||
else:
|
||||
self.keydir = Path(keydir)
|
||||
self.path = str(self.keydir / name)
|
||||
self.privatefile = f"{self.path}.private"
|
||||
self.keyfile = f"{self.path}.key"
|
||||
super().__init__(name, keydir)
|
||||
self.statefile = f"{self.path}.state"
|
||||
self.tag = int(self.name[-5:])
|
||||
self.external = False
|
||||
|
||||
def get_timing(
|
||||
|
|
@ -522,20 +511,9 @@ class Key:
|
|||
), f"DNSKEY not found in {self.keyfile}"
|
||||
return dnskey_rr
|
||||
|
||||
def into_ta(self, ta_type: str, dsdigest=dns.dnssec.DSDigest.SHA256) -> TrustAnchor:
|
||||
dnskey = self.dnskey
|
||||
if ta_type in ["static-ds", "initial-ds"]:
|
||||
ds = dns.dnssec.make_ds(dnskey.name, dnskey[0], dsdigest)
|
||||
parts = str(ds).split()
|
||||
contents = " ".join(parts[:3]) + f' "{parts[3]}"'
|
||||
elif ta_type in ["static-key", "initial-key"]:
|
||||
parts = str(dnskey).split()
|
||||
contents = " ".join(parts[4:7]) + f' "{"".join(parts[7:])}"'
|
||||
else:
|
||||
raise ValueError(f"invalid trust anchor type: {ta_type}")
|
||||
return TrustAnchor(str(dnskey.name), ta_type, contents)
|
||||
|
||||
def is_ksk(self) -> bool:
|
||||
# KASP role follows the .state KSK metadata, not the DNSKEY SEP flag:
|
||||
# a CSK may be configured without SEP (see the csk-nosep test).
|
||||
return self.get_metadata("KSK") == "yes"
|
||||
|
||||
def is_zsk(self) -> bool:
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import dns.name
|
|||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.rrset
|
||||
import dns.zonefile
|
||||
|
||||
from .kasp import Key
|
||||
from .log import debug
|
||||
from .run import EnvCmd
|
||||
from .template import NS1, Nameserver, TemplateEngine, TrustAnchor
|
||||
|
|
@ -50,7 +50,7 @@ class ZoneKey(ABC):
|
|||
Abstract base for a DNSSEC key attached to a Zone.
|
||||
|
||||
Two concrete implementations exist:
|
||||
FileZoneKey — wraps a dnssec-keygen-managed key file pair (kasp.Key).
|
||||
FileZoneKey — reads a dnssec-keygen-managed key file pair.
|
||||
PythonZoneKey — holds a Python-native (private_key, dnskey_rdata) pair
|
||||
required for dnspython-based operations and signing.
|
||||
|
||||
|
|
@ -107,18 +107,28 @@ class FileZoneKey(ZoneKey):
|
|||
"""
|
||||
A ZoneKey backed by dnssec-keygen-managed key files.
|
||||
|
||||
Constructed by FileZoneKey.generate(); callers normally do not
|
||||
instantiate this directly. The underlying kasp.Key is accessible via
|
||||
.key for working with timing metadata, state files, etc.
|
||||
Reads key material directly from the .key/.private file pair. Normally
|
||||
constructed via FileZoneKey.generate() (or Zone.add_keys());
|
||||
isctest.kasp.Key subclasses it to add KASP timing/state operations.
|
||||
"""
|
||||
|
||||
def __init__(self, key: Key, zone: Zone) -> None:
|
||||
self.key = key
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
keydir: str | Path | None = None,
|
||||
zone: Zone | None = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.keydir = Path() if keydir is None else Path(keydir)
|
||||
self.path = str(self.keydir / name)
|
||||
self.privatefile = f"{self.path}.private"
|
||||
self.keyfile = f"{self.path}.key"
|
||||
self.tag = int(self.name[-5:])
|
||||
self.zone = zone
|
||||
|
||||
@property
|
||||
def dnskey(self) -> dns.rrset.RRset:
|
||||
return self.key.dnskey
|
||||
raise NotImplementedError # TODO will be re-implemented in the followup commit
|
||||
|
||||
def write_dsset(
|
||||
self,
|
||||
|
|
@ -134,6 +144,7 @@ class FileZoneKey(ZoneKey):
|
|||
PythonZoneKey KSKs (PythonZoneKey appends to the same file);
|
||||
Zone.copy_dssets enforces this.
|
||||
"""
|
||||
assert self.zone is not None, "write_dsset requires a zone-attached key"
|
||||
src = Path(self.zone.ns.name) / f"dsset-{self.zone.name}."
|
||||
shutil.copy(src, Path(target_dir))
|
||||
debug(f"{self.zone.name}: dsset copied to {target_dir}")
|
||||
|
|
@ -160,7 +171,7 @@ class FileZoneKey(ZoneKey):
|
|||
"KEYGEN", f"-q -a {alg.number} -b {alg.bits} -K {KEYDIR} -L {DNSKEY_TTL}"
|
||||
)
|
||||
key_name = keygen(f"{params} {zone.name}", cwd=zone.ns.name).out.strip()
|
||||
return FileZoneKey(Key(key_name, keydir=keydir), zone=zone)
|
||||
return FileZoneKey(key_name, keydir=keydir, zone=zone)
|
||||
|
||||
|
||||
class PythonZoneKey(ZoneKey):
|
||||
|
|
|
|||
Loading…
Reference in a new issue