From: Michael Tremer Date: Thu, 6 Oct 2022 18:36:16 +0000 (+0000) Subject: repos: Automatically rotate keys X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=99670487de1df95a056856860d977424bb6657e9;p=pbs.git repos: Automatically rotate keys Signed-off-by: Michael Tremer --- diff --git a/src/buildservice/keys.py b/src/buildservice/keys.py index c00536af..0bc60782 100644 --- a/src/buildservice/keys.py +++ b/src/buildservice/keys.py @@ -209,10 +209,18 @@ class Key(base.DataObject): return list(subkeys) - async def generate_subkey(self, algorithm=None): - key = await self.backend.keys.generate( - "%s - DUMMY SUBKEY <%s>" % (self.name, self.email), - ) + async def generate_subkey(self, name, algorithm=None): + """ + Generates a subkey + """ + # Append name to the parent key name + name = "%s - %s" % (self.name, name) + + # XXX This is currently creating a totally new key + key = await self.backend.keys.generate(name, self.email) + + # XXX mark this as a subkey + key._set_attribute("parent_key_id", self.id) # Append to existing list of subkeys self.subkeys.append(key) @@ -234,7 +242,7 @@ class Key(base.DataObject): return False - def revoke(self): + async def revoke(self): """ Revokes this key """ diff --git a/src/buildservice/repository.py b/src/buildservice/repository.py index aa447a52..5adff974 100644 --- a/src/buildservice/repository.py +++ b/src/buildservice/repository.py @@ -1,6 +1,7 @@ #!/usr/bin/python import asyncio +import datetime import logging import os.path @@ -16,6 +17,12 @@ from . import misc from .constants import * from .decorators import * +# How long should a key be in active use for? +KEY_LIFETIME = datetime.timedelta(days=365) + +# How long should rotating keys overlap? +KEY_ROLLOVER_TIME = datetime.timedelta(days=60) + class Repositories(base.Object): def _get_repository(self, query, *args): res = self.db.get(query, *args) @@ -90,6 +97,9 @@ class Repositories(base.Object): if key: repo.key = key + # Rotate keys for the first time + await repo.rotate_keys() + return repo def _make_slug(self, name, owner=None): @@ -150,6 +160,13 @@ class Repositories(base.Object): for repo in self: await repo.write() + async def rotate_keys(self): + """ + Rotates keys for all repositories + """ + for repo in self: + await repo.rotate_keys() + class Repository(base.DataObject): table = "repositories" @@ -332,6 +349,67 @@ class Repository(base.DataObject): def key(self): return self.pakfire.keys.get_by_id(self.data.key_id) + async def _create_subkey(self): + """ + Creates a new subkey for this repository + """ + today = datetime.date.today() + + # Mark the key with the data when it has been created + name = today.strftime("%Y-%m-%d") + + # XXX add expiration + + # Generate the key + await self.key.generate_subkey(name) + + @property + def signing_keys(self): + """ + Returns a list of all keys that are being used to sign this repository + """ + return [key for key in self.key.subkeys \ + if not key.has_expired() and not key.is_revoked()] + + # Key Rotation + + async def rotate_keys(self): + """ + This function rotates any keys (if possible) + """ + log.info("Rotating keys in repository %s" % self) + + # If no keys exist, create a new key + if not self.signing_keys: + await self._create_subkey() + return + + # Check the time + now = datetime.datetime.now() + + log.debug("Current signing keys:") + for key in self.signing_keys: + log.debug(" %s" % key) + log.debug(" Created at: %s" % key.created_at) + if key.expires_at: + log.debug(" Expires at: %s" % key.expires_at) + + # Pick the key created last + signing_key = max(self.signing_keys, key=lambda k: k.created_at) + + # Is the key expiring soon? + if key.expires_at and now >= key.expires_at - KEY_ROLLOVER_TIME: + await self._create_subkey() + + # Is this key approaching the end of its lifetime? + elif now >= key.created_at + KEY_LIFETIME - KEY_ROLLOVER_TIME: + await self._create_subkey() + + # Check if any other keys need to be revoked + for key in self.signing_keys: + if now >= key.created_at + KEY_LIFETIME + KEY_ROLLOVER_TIME: + await key.revoke() + @property def arches(self): return self.distro.arches + ["src"] diff --git a/src/crontab/pakfire-build-service b/src/crontab/pakfire-build-service index f227a78e..71654a5f 100644 --- a/src/crontab/pakfire-build-service +++ b/src/crontab/pakfire-build-service @@ -9,6 +9,9 @@ MAILTO=pakfire@ipfire.org # Cleanup */5 * * * * _pakfire pakfire-build-service --logging=warning cleanup +# Repositories - Key Rotation +@daily _pakfire pakfire-build-service --logging=warning repos:rotate-keys + # Pull sources #*/5 * * * * _pakfire pakfire-build-service pull-sources &>/dev/null diff --git a/src/scripts/pakfire-build-service b/src/scripts/pakfire-build-service index e3ea3af8..7af55cda 100644 --- a/src/scripts/pakfire-build-service +++ b/src/scripts/pakfire-build-service @@ -28,6 +28,7 @@ class Cli(object): "keys:generate" : self.backend.keys.generate, # Repositories + "repos:rotate-keys": self.backend.repos.rotate_keys, "repos:write" : self.backend.repos.write, # Sync