diff --git a/authentik/crypto/api.py b/authentik/crypto/api.py index f787d2935..3b284baee 100644 --- a/authentik/crypto/api.py +++ b/authentik/crypto/api.py @@ -99,6 +99,7 @@ class CertificateKeyPairSerializer(ModelSerializer): "private_key_available", "certificate_download_url", "private_key_download_url", + "managed", ] extra_kwargs = { "key_data": {"write_only": True}, @@ -134,7 +135,7 @@ class CertificateKeyPairFilter(FilterSet): class Meta: model = CertificateKeyPair - fields = ["name"] + fields = ["name", "managed"] class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet): diff --git a/authentik/crypto/apps.py b/authentik/crypto/apps.py index a7c5419b4..8f84f0839 100644 --- a/authentik/crypto/apps.py +++ b/authentik/crypto/apps.py @@ -1,4 +1,6 @@ """authentik crypto app config""" +from importlib import import_module + from django.apps import AppConfig @@ -8,3 +10,6 @@ class AuthentikCryptoConfig(AppConfig): name = "authentik.crypto" label = "authentik_crypto" verbose_name = "authentik Crypto" + + def ready(self): + import_module("authentik.crypto.managed") diff --git a/authentik/crypto/builder.py b/authentik/crypto/builder.py index 07318aeeb..f78c8692f 100644 --- a/authentik/crypto/builder.py +++ b/authentik/crypto/builder.py @@ -24,16 +24,17 @@ class CertificateBuilder: self.__builder = None self.__certificate = None self.common_name = "authentik Self-signed Certificate" + self.cert = CertificateKeyPair() def save(self) -> Optional[CertificateKeyPair]: """Save generated certificate as model""" if not self.__certificate: raise ValueError("Certificated hasn't been built yet") - return CertificateKeyPair.objects.create( - name=self.common_name, - certificate_data=self.certificate, - key_data=self.private_key, - ) + self.cert.name = self.common_name + self.cert.certificate_data = self.certificate + self.cert.key_data = self.private_key + self.cert.save() + return self.cert def build( self, diff --git a/authentik/crypto/managed.py b/authentik/crypto/managed.py new file mode 100644 index 000000000..ef4f96040 --- /dev/null +++ b/authentik/crypto/managed.py @@ -0,0 +1,40 @@ +"""Crypto managed objects""" +from datetime import datetime +from typing import Optional + +from authentik.crypto.builder import CertificateBuilder +from authentik.crypto.models import CertificateKeyPair +from authentik.managed.manager import ObjectManager + +MANAGED_KEY = "goauthentik.io/crypto/jwt-managed" + + +class CryptoManager(ObjectManager): + """Crypto managed objects""" + + def _create(self, cert: Optional[CertificateKeyPair] = None): + builder = CertificateBuilder() + builder.common_name = "goauthentik.io" + builder.build( + subject_alt_names=["goauthentik.io"], + validity_days=360, + ) + if not cert: + cert = CertificateKeyPair() + cert.certificate_data = builder.certificate + cert.key_data = builder.private_key + cert.name = "authentik Internal JWT Certificate" + cert.managed = MANAGED_KEY + cert.save() + + def reconcile(self): + certs = CertificateKeyPair.objects.filter(managed=MANAGED_KEY) + if not certs.exists(): + self._create() + return [] + cert: CertificateKeyPair = certs.first() + now = datetime.now() + if now < cert.certificate.not_valid_before or now > cert.certificate.not_valid_after: + self._create(cert) + return [] + return [] diff --git a/authentik/crypto/migrations/0003_certificatekeypair_managed.py b/authentik/crypto/migrations/0003_certificatekeypair_managed.py new file mode 100644 index 000000000..e0697c762 --- /dev/null +++ b/authentik/crypto/migrations/0003_certificatekeypair_managed.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2021-10-09 17:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_crypto", "0002_create_self_signed_kp"), + ] + + operations = [ + migrations.AddField( + model_name="certificatekeypair", + name="managed", + field=models.TextField( + default=None, + help_text="Objects which are managed by authentik. These objects are created and updated automatically. This is flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.", + null=True, + unique=True, + verbose_name="Managed by authentik", + ), + ), + ] diff --git a/authentik/crypto/models.py b/authentik/crypto/models.py index 60b1f69b6..e9c395dbb 100644 --- a/authentik/crypto/models.py +++ b/authentik/crypto/models.py @@ -13,9 +13,10 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from authentik.lib.models import CreatedUpdatedModel +from authentik.managed.models import ManagedModel -class CertificateKeyPair(CreatedUpdatedModel): +class CertificateKeyPair(ManagedModel, CreatedUpdatedModel): """CertificateKeyPair that can be used for signing or encrypting if `key_data` is set, otherwise it can be used to verify remote data.""" diff --git a/schema.yml b/schema.yml index 4d489dcdc..e724b2d89 100644 --- a/schema.yml +++ b/schema.yml @@ -3087,6 +3087,10 @@ paths: schema: type: boolean description: Only return certificate-key pairs with keys + - in: query + name: managed + schema: + type: string - in: query name: name schema: @@ -19174,6 +19178,14 @@ components: private_key_download_url: type: string readOnly: true + managed: + type: string + nullable: true + title: Managed by authentik + description: Objects which are managed by authentik. These objects are created + and updated automatically. This is flag only indicates that an object + can be overwritten by migrations. You can still modify the objects via + the API, but expect changes to be overwritten in a later update. required: - cert_expiry - cert_subject @@ -19199,6 +19211,14 @@ components: writeOnly: true description: Optional Private Key. If this is set, you can use this keypair for encryption. + managed: + type: string + nullable: true + title: Managed by authentik + description: Objects which are managed by authentik. These objects are created + and updated automatically. This is flag only indicates that an object + can be overwritten by migrations. You can still modify the objects via + the API, but expect changes to be overwritten in a later update. required: - certificate_data - name @@ -25310,6 +25330,14 @@ components: writeOnly: true description: Optional Private Key. If this is set, you can use this keypair for encryption. + managed: + type: string + nullable: true + title: Managed by authentik + description: Objects which are managed by authentik. These objects are created + and updated automatically. This is flag only indicates that an object + can be overwritten by migrations. You can still modify the objects via + the API, but expect changes to be overwritten in a later update. PatchedConsentStageRequest: type: object description: ConsentStage Serializer