diff --git a/authentik/crypto/api.py b/authentik/crypto/api.py index 673537a28..e71199fee 100644 --- a/authentik/crypto/api.py +++ b/authentik/crypto/api.py @@ -12,10 +12,11 @@ from django_filters.filters import BooleanFilter from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema from rest_framework.decorators import action +from rest_framework.exceptions import ValidationError from rest_framework.fields import CharField, DateTimeField, IntegerField, SerializerMethodField from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.serializers import ModelSerializer, ValidationError +from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import ModelViewSet from structlog.stdlib import get_logger diff --git a/authentik/crypto/management/commands/import_certificate.py b/authentik/crypto/management/commands/import_certificate.py new file mode 100644 index 000000000..d98a242fd --- /dev/null +++ b/authentik/crypto/management/commands/import_certificate.py @@ -0,0 +1,51 @@ +"""Import certificate""" +from sys import exit as sys_exit + +from django.core.management.base import BaseCommand, no_translations +from rest_framework.exceptions import ValidationError +from structlog.stdlib import get_logger + +from authentik.crypto.api import CertificateKeyPairSerializer +from authentik.crypto.models import CertificateKeyPair + +LOGGER = get_logger() + + +class Command(BaseCommand): + """Import certificate""" + + @no_translations + def handle(self, *args, **options): + """Import certificate""" + keypair = CertificateKeyPair.objects.filter(name=options["name"]).first() + dirty = False + if not keypair: + keypair = CertificateKeyPair(name=options["name"]) + dirty = True + with open(options["certificate"], mode="r", encoding="utf-8") as _cert: + cert_data = _cert.read() + if keypair.certificate_data != cert_data: + dirty = True + keypair.certificate_data = cert_data + if options["private_key"]: + with open(options["private_key"], mode="r", encoding="utf-8") as _key: + key_data = _key.read() + if keypair.key_data != key_data: + dirty = True + keypair.key_data = key_data + # Validate that cert and key are actually PEM and valid + serializer = CertificateKeyPairSerializer(instance=keypair) + try: + serializer.validate_certificate_data(keypair.certificate_data) + if keypair.key_data != "": + serializer.validate_certificate_data(keypair.key_data) + except ValidationError as exc: + self.stderr.write(exc) + sys_exit(1) + if dirty: + keypair.save() + + def add_arguments(self, parser): + parser.add_argument("--certificate", type=str, required=True) + parser.add_argument("--private-key", type=str, required=False) + parser.add_argument("--name", type=str, required=True) diff --git a/website/docs/core/certificates.md b/website/docs/core/certificates.md index 43af6f41b..0c72a1007 100644 --- a/website/docs/core/certificates.md +++ b/website/docs/core/certificates.md @@ -57,6 +57,18 @@ certs/ Files are checked every 5 minutes, and will trigger an Outpost refresh if the files differ. +#### Manual imports + +Starting with authentik 2022.9, you can also import certificates with any folder structure directly. To do this, run the following command within the worker container: + +```shell +ak import_certificate --certificate /certs/mycert.pem --private-key /certs/something.pem --name test +# --private-key can be omitted to only import a certificate, i.e. to trust other connections +# ak import_certificate --certificate /certs/othercert.pem --name test2 +``` + +This will import the certificate into authentik under the given name. This command is idempotent, meaning you can run it via a cron-job and authentik will only update the certificate when it changes. + ## Web certificates Starting with authentik 2021.12.4, you can configure the certificate authentik uses for its core webserver. For most deployments this will not be relevant and reverse proxies are used, but this can be used to create a very compact and self-contained authentik install.