summaryrefslogtreecommitdiffstats
path: root/bin/python/isc/keyseries.py.in
diff options
context:
space:
mode:
Diffstat (limited to 'bin/python/isc/keyseries.py.in')
-rw-r--r--bin/python/isc/keyseries.py.in232
1 files changed, 232 insertions, 0 deletions
diff --git a/bin/python/isc/keyseries.py.in b/bin/python/isc/keyseries.py.in
new file mode 100644
index 0000000..e75f82b
--- /dev/null
+++ b/bin/python/isc/keyseries.py.in
@@ -0,0 +1,232 @@
+# 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.
+
+from collections import defaultdict
+from .dnskey import *
+from .keydict import *
+from .keyevent import *
+from .policy import *
+import time
+
+
+class keyseries:
+ _K = defaultdict(lambda: defaultdict(list))
+ _Z = defaultdict(lambda: defaultdict(list))
+ _zones = set()
+ _kdict = None
+ _context = None
+
+ def __init__(self, kdict, now=time.time(), context=None):
+ self._kdict = kdict
+ self._context = context
+ self._zones = set(kdict.missing())
+
+ for zone in kdict.zones():
+ self._zones.add(zone)
+ for alg, keys in kdict[zone].items():
+ for k in keys.values():
+ if k.sep:
+ if not (k.delete() and k.delete() < now):
+ self._K[zone][alg].append(k)
+ else:
+ if not (k.delete() and k.delete() < now):
+ self._Z[zone][alg].append(k)
+
+ self._K[zone][alg].sort()
+ self._Z[zone][alg].sort()
+
+ def __iter__(self):
+ for zone in self._zones:
+ for collection in [self._K, self._Z]:
+ if zone not in collection:
+ continue
+ for alg, keys in collection[zone].items():
+ for key in keys:
+ yield key
+
+ def dump(self):
+ for k in self:
+ print("%s" % repr(k))
+
+ def fixseries(self, keys, policy, now, **kwargs):
+ force = kwargs.get("force", False)
+ if not keys:
+ return
+
+ # handle the first key
+ key = keys[0]
+ if key.sep:
+ rp = policy.ksk_rollperiod
+ prepub = policy.ksk_prepublish or (30 * 86400)
+ postpub = policy.ksk_postpublish or (30 * 86400)
+ else:
+ rp = policy.zsk_rollperiod
+ prepub = policy.zsk_prepublish or (30 * 86400)
+ postpub = policy.zsk_postpublish or (30 * 86400)
+
+ # the first key should be published and active
+ p = key.publish()
+ a = key.activate()
+ if not p or p > now:
+ key.setpublish(now)
+ p = now
+ if not a or a > now:
+ key.setactivate(now)
+ a = now
+
+ i = key.inactive()
+ fudge = 300
+ if not rp:
+ key.setinactive(None, **kwargs)
+ key.setdelete(None, **kwargs)
+ elif not i or a + rp != i:
+ if not i and a + rp > now + prepub + fudge:
+ key.setinactive(a + rp, **kwargs)
+ key.setdelete(a + rp + postpub, **kwargs)
+ elif not i:
+ key.setinactive(now + prepub + fudge, **kwargs)
+ key.setdelete(now + prepub + postpub + fudge, **kwargs)
+ elif i < now:
+ pass
+ elif a + rp > i:
+ key.setinactive(a + rp, **kwargs)
+ key.setdelete(a + rp + postpub, **kwargs)
+ elif a + rp > now + prepub + fudge:
+ key.setinactive(a + rp, **kwargs)
+ key.setdelete(a + rp + postpub, **kwargs)
+ else:
+ key.setinactive(now + prepub + fudge, **kwargs)
+ key.setdelete(now + prepub + postpub + fudge, **kwargs)
+ else:
+ d = key.delete()
+ if not d or i + postpub > now + fudge:
+ key.setdelete(i + postpub, **kwargs)
+ elif not d:
+ key.setdelete(now + postpub + fudge, **kwargs)
+ elif d < now + fudge:
+ pass
+ elif d < i + postpub:
+ key.setdelete(i + postpub, **kwargs)
+
+ if policy.keyttl != key.ttl:
+ key.setttl(policy.keyttl)
+
+ # handle all the subsequent keys
+ prev = key
+ for key in keys[1:]:
+ # if no rollperiod, then all keys after the first in
+ # the series kept inactive.
+ # (XXX: we need to change this to allow standby keys)
+ if not rp:
+ key.setpublish(None, **kwargs)
+ key.setactivate(None, **kwargs)
+ key.setinactive(None, **kwargs)
+ key.setdelete(None, **kwargs)
+ if policy.keyttl != key.ttl:
+ key.setttl(policy.keyttl)
+ continue
+
+ # otherwise, ensure all dates are set correctly based on
+ # the initial key
+ a = prev.inactive()
+ p = a - prepub
+ key.setactivate(a, **kwargs)
+ key.setpublish(p, **kwargs)
+ key.setinactive(a + rp, **kwargs)
+ key.setdelete(a + rp + postpub, **kwargs)
+ prev.setdelete(a + postpub, **kwargs)
+ if policy.keyttl != key.ttl:
+ key.setttl(policy.keyttl)
+ prev = key
+
+ # if we haven't got sufficient coverage, create successor key(s)
+ while rp and prev.inactive() and prev.inactive() < now + policy.coverage:
+ # commit changes to predecessor: a successor can only be
+ # generated if Inactive has been set in the predecessor key
+ prev.commit(self._context["settime_path"], **kwargs)
+ key = prev.generate_successor(
+ self._context["keygen_path"],
+ self._context["randomdev"],
+ prepub,
+ **kwargs
+ )
+
+ key.setinactive(key.activate() + rp, **kwargs)
+ key.setdelete(key.inactive() + postpub, **kwargs)
+ keys.append(key)
+ prev = key
+
+ # last key? we already know we have sufficient coverage now, so
+ # disable the inactivation of the final key (if it was set),
+ # ensuring that if dnssec-keymgr isn't run again, the last key
+ # in the series will at least remain usable.
+ prev.setinactive(None, **kwargs)
+ prev.setdelete(None, **kwargs)
+
+ # commit changes
+ for key in keys:
+ key.commit(self._context["settime_path"], **kwargs)
+
+ def enforce_policy(self, policies, now=time.time(), **kwargs):
+ # If zones is provided as a parameter, use that list.
+ # If not, use what we have in this object
+ zones = kwargs.get("zones", self._zones)
+ keys_dir = kwargs.get("dir", self._context.get("keys_path", None))
+ force = kwargs.get("force", False)
+
+ for zone in zones:
+ collections = []
+ policy = policies.policy(zone)
+ keys_dir = keys_dir or policy.directory or "."
+ alg = policy.algorithm
+ algnum = dnskey.algnum(alg)
+ if "ksk" not in kwargs or not kwargs["ksk"]:
+ if len(self._Z[zone][algnum]) == 0:
+ k = dnskey.generate(
+ self._context["keygen_path"],
+ self._context["randomdev"],
+ keys_dir,
+ zone,
+ alg,
+ policy.zsk_keysize,
+ False,
+ policy.keyttl or 3600,
+ **kwargs
+ )
+ self._Z[zone][algnum].append(k)
+ collections.append(self._Z[zone])
+
+ if "zsk" not in kwargs or not kwargs["zsk"]:
+ if len(self._K[zone][algnum]) == 0:
+ k = dnskey.generate(
+ self._context["keygen_path"],
+ self._context["randomdev"],
+ keys_dir,
+ zone,
+ alg,
+ policy.ksk_keysize,
+ True,
+ policy.keyttl or 3600,
+ **kwargs
+ )
+ self._K[zone][algnum].append(k)
+ collections.append(self._K[zone])
+
+ for collection in collections:
+ for algorithm, keys in collection.items():
+ if algorithm != algnum:
+ continue
+ try:
+ self.fixseries(keys, policy, now, **kwargs)
+ except Exception as e:
+ raise Exception(
+ "%s/%s: %s" % (zone, dnskey.algstr(algnum), str(e))
+ )