diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 178931e97b08..504cd4d460e6 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -18,7 +18,7 @@ runs: run: | sudo apt-get remove --purge man-db sudo apt-get update - sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext libkrb5-dev krb5-kdc krb5-user krb5-admin-server + sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext krb5-multidev libkrb5-dev heimdal-multidev libclang-dev krb5-kdc krb5-user krb5-admin-server - name: Install uv if: ${{ contains(inputs.dependencies, 'python') }} uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v5 diff --git a/Dockerfile b/Dockerfile index c320d24bbec9..393c3665ad01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -114,7 +114,7 @@ RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/v # postgresql libpq-dev \ # python-kadmin-rs - clang libkrb5-dev sccache \ + krb5-multidev libkrb5-dev heimdal-multidev libclang-dev \ # xmlsec libltdl-dev && \ curl https://sh.rustup.rs -sSf | sh -s -- -y @@ -156,7 +156,7 @@ WORKDIR / RUN apt-get update && \ apt-get upgrade -y && \ # Required for runtime - apt-get install -y --no-install-recommends libpq5 libmaxminddb0 ca-certificates libkrb5-3 libkadm5clnt-mit12 libkdb5-10 libltdl7 libxslt1.1 && \ + apt-get install -y --no-install-recommends libpq5 libmaxminddb0 ca-certificates libkrb5-3 libkadm5clnt-mit12 libkadm5clnt7t64-heimdal libkdb5-10 libltdl7 libxslt1.1 && \ # Required for bootstrap & healtcheck apt-get install -y --no-install-recommends runit && \ pip3 install --no-cache-dir --upgrade pip && \ diff --git a/authentik/sources/kerberos/migrations/0004_alter_kerberossource_kadmin_type.py b/authentik/sources/kerberos/migrations/0004_alter_kerberossource_kadmin_type.py new file mode 100644 index 000000000000..21318514c5b9 --- /dev/null +++ b/authentik/sources/kerberos/migrations/0004_alter_kerberossource_kadmin_type.py @@ -0,0 +1,22 @@ +# Generated by Django 5.2.7 on 2025-10-31 15:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_sources_kerberos", "0003_migrate_userkerberossourceconnection_identifier"), + ] + + operations = [ + migrations.AlterField( + model_name="kerberossource", + name="kadmin_type", + field=models.TextField( + choices=[("MIT", "Mit"), ("Heimdal", "Heimdal")], + default="MIT", + help_text="KAdmin server type", + ), + ), + ] diff --git a/authentik/sources/kerberos/models.py b/authentik/sources/kerberos/models.py index 1a9e3950a9a4..c82b40643bdd 100644 --- a/authentik/sources/kerberos/models.py +++ b/authentik/sources/kerberos/models.py @@ -1,6 +1,7 @@ """authentik Kerberos Source Models""" import os +from base64 import b64decode from pathlib import Path from tempfile import gettempdir from typing import Any @@ -8,14 +9,13 @@ import gssapi import pglock from django.db import connection, models -from django.db.models.fields import b64decode from django.http import HttpRequest from django.shortcuts import reverse from django.templatetags.static import static from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ -from kadmin import KAdmin, KAdminApiVersion -from kadmin.exceptions import PyKAdminException +from kadmin import KAdm5Variant, KAdmin, KAdminApiVersion +from kadmin import exceptions as kadmin_exceptions from rest_framework.serializers import Serializer from structlog.stdlib import get_logger @@ -43,7 +43,6 @@ class KAdminType(models.TextChoices): MIT = "MIT" HEIMDAL = "Heimdal" - OTHER = "other" class KerberosSource(ScheduledModel, Source): @@ -55,7 +54,7 @@ class KerberosSource(ScheduledModel, Source): help_text=_("Custom krb5.conf to use. Uses the system one by default"), ) kadmin_type = models.TextField( - choices=KAdminType.choices, default=KAdminType.OTHER, help_text=_("KAdmin server type") + choices=KAdminType.choices, default=KAdminType.MIT, help_text=_("KAdmin server type") ) sync_users = models.BooleanField( @@ -239,13 +238,14 @@ def krb5_conf_path(self) -> str | None: return str(conf_path) def _kadmin_init(self) -> KAdmin | None: - api_version = None + variant = KAdm5Variant.MitClient + api_version = KAdminApiVersion.Version2 match self.kadmin_type: case KAdminType.MIT: + variant = KAdm5Variant.MitClient api_version = KAdminApiVersion.Version4 case KAdminType.HEIMDAL: - api_version = KAdminApiVersion.Version2 - case KAdminType.OTHER: + variant = KAdm5Variant.HeimdalClient api_version = KAdminApiVersion.Version2 # kadmin doesn't use a ccache for its connection # as such, we don't need to create a separate ccache for each source @@ -253,6 +253,7 @@ def _kadmin_init(self) -> KAdmin | None: return None if self.sync_password: return KAdmin.with_password( + variant, self.sync_principal, self.sync_password, api_version=api_version, @@ -265,12 +266,14 @@ def _kadmin_init(self) -> KAdmin | None: keytab_path.write_bytes(b64decode(self.sync_keytab)) keytab = f"FILE:{keytab_path}" return KAdmin.with_keytab( + variant, self.sync_principal, keytab, api_version=api_version, ) if self.sync_ccache: return KAdmin.with_ccache( + variant, self.sync_principal, self.sync_ccache, api_version=api_version, @@ -285,9 +288,9 @@ def connection(self) -> KAdmin | None: _kadmin_connections[str(self.pk)] = self._kadmin_init() return _kadmin_connections.get(str(self.pk), None) - def check_connection(self) -> dict[str, str]: + def check_connection(self) -> dict[str, str | bool]: """Check Kerberos Connection""" - status = {"status": "ok"} + status: dict[str, str | bool] = {"status": "ok"} if not self.sync_users: return status with Krb5ConfContext(self): @@ -297,7 +300,7 @@ def check_connection(self) -> dict[str, str]: status["status"] = "no connection" return status status["principal_exists"] = kadm.principal_exists(self.sync_principal) - except PyKAdminException as exc: + except kadmin_exceptions.PyKAdminException as exc: status["status"] = str(exc) return status diff --git a/authentik/sources/kerberos/signals.py b/authentik/sources/kerberos/signals.py index 3b1bc99ec648..90d848d1933d 100644 --- a/authentik/sources/kerberos/signals.py +++ b/authentik/sources/kerberos/signals.py @@ -1,7 +1,7 @@ """authentik kerberos source signals""" from django.dispatch import receiver -from kadmin.exceptions import PyKAdminException +from kadmin import exceptions as kadmin_exceptions from rest_framework.serializers import ValidationError from structlog.stdlib import get_logger @@ -38,7 +38,7 @@ def kerberos_sync_password(sender, user: User, password: str, **_): kadm, password, ) - except PyKAdminException as exc: + except kadmin_exceptions.PyKAdminException as exc: LOGGER.warning("failed to set Kerberos password", exc=exc, source=source) Event.new( EventAction.CONFIGURATION_ERROR, diff --git a/pyproject.toml b/pyproject.toml index d38fb7f7f8de..c678bb2a2037 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,6 +126,7 @@ django-channels-postgres = { workspace = true } django-dramatiq-postgres = { workspace = true } django-postgres-cache = { workspace = true } opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "ceb4fcc090851717a3069d78e85ceb1e86c2740c" } +python-kadmin-rs = { git = "https://github.com/authentik-community/kadmin-rs.git", rev = "2e82b7a59466b6267890f937054a97b8be006c99" } [tool.uv.workspace] members = [ diff --git a/uv.lock b/uv.lock index 192428b83516..86484d2e718e 100644 --- a/uv.lock +++ b/uv.lock @@ -327,7 +327,7 @@ requires-dist = [ { name = "pydantic-scim", specifier = "==0.0.8" }, { name = "pyjwt", specifier = "==2.10.1" }, { name = "pyrad", specifier = "==2.4" }, - { name = "python-kadmin-rs", specifier = "==0.6.1" }, + { name = "python-kadmin-rs", git = "https://github.com/authentik-community/kadmin-rs.git?rev=2e82b7a59466b6267890f937054a97b8be006c99" }, { name = "pyyaml", specifier = "==6.0.2" }, { name = "requests-oauthlib", specifier = "==2.0.0" }, { name = "scim2-filter-parser", specifier = "==0.7.0" }, @@ -2896,23 +2896,8 @@ wheels = [ [[package]] name = "python-kadmin-rs" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/64/05942061f851c0973ce351dfa7a6358fe14e7622527ecc883ce2eccd3177/python_kadmin_rs-0.6.1.tar.gz", hash = "sha256:0fdcf5310504eeb51a8a82d89313a88ef7f7141a596a6cf609d82eed425f21f4", size = 89030, upload-time = "2025-06-18T13:24:55.839Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/e3/109e5aa8176c70946836d6ec68b9397f97222adc8d2537f4ff8570a951db/python_kadmin_rs-0.6.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:96156b83e6843c773f289d3de1316d42581bb473ff21c22e06c44cb927075ef2", size = 1415305, upload-time = "2025-06-18T13:24:05.359Z" }, - { url = "https://files.pythonhosted.org/packages/9c/43/fca43da7ddf6a3d7332edacfe18e1a27dc86d26f5fd915ab1b96eb6d460b/python_kadmin_rs-0.6.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:2d157a8457c7d29753da1e7028c614f0eeef3593cd4a21d2c48730d8820e1096", size = 1498856, upload-time = "2025-06-18T13:24:07.783Z" }, - { url = "https://files.pythonhosted.org/packages/24/a8/c85618d37d09daf36ce54c8a4f576f9d3308e7a584f8bbc6d80733add4bb/python_kadmin_rs-0.6.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c6d066a932ad15236cc3c41cf07ab3a5055298431b98ea4f63070ffe93715bfd", size = 3264793, upload-time = "2025-06-18T13:24:09.373Z" }, - { url = "https://files.pythonhosted.org/packages/90/53/d8734d7c2adec3f17ac6dd5837e2bc23dfd627d693141ee9a6f85151c1c8/python_kadmin_rs-0.6.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3737c556c1295959e54d24ab5e7b6140c1136dda0505937ecbb10ddb93e07cdd", size = 3379183, upload-time = "2025-06-18T13:24:10.877Z" }, - { url = "https://files.pythonhosted.org/packages/0c/0a/45fb5dc7eb6e3550181ac94a1ed9a75936b7059641e5ace67cf2db2033f1/python_kadmin_rs-0.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa3e0e576354b1e56633488f919faa3196281eca404651fc7944538ae5cf8f0b", size = 1574931, upload-time = "2025-06-18T13:24:12.76Z" }, - { url = "https://files.pythonhosted.org/packages/eb/7a/e4eb9e90ba3ffed8e1e897bde5da03a335a66c470ffc916242bbd7adb6a5/python_kadmin_rs-0.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b17cd39f612c390aa054a3c97a9de6d574f7a418720605c904a6b56280e4410", size = 1615466, upload-time = "2025-06-18T13:24:15.924Z" }, - { url = "https://files.pythonhosted.org/packages/8a/0d/01ab59a52819514134a051861ab9639efa63ac1435aae07a7b29cebd7000/python_kadmin_rs-0.6.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:c4d4b54c23dd0e5a0bd2e7c84a2656556a89d5afda7e9573f36cfd805c22b2c2", size = 1409256, upload-time = "2025-06-18T13:24:17.34Z" }, - { url = "https://files.pythonhosted.org/packages/b9/29/d83fde4e4920b2f78325d52622b2ca7e5efcfb393a0bfa3bb2ca48288769/python_kadmin_rs-0.6.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:c9c6ec440f3cba8be671b1db27e328b39ca7e0ed4e9b254596728702b291b086", size = 1496843, upload-time = "2025-06-18T13:24:19.041Z" }, - { url = "https://files.pythonhosted.org/packages/85/13/e75f869aba9571b75a8c132a3be3cf035097f7fbafbc6407ed993a3c21d3/python_kadmin_rs-0.6.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:6431f702218c438818e288c84cf00b972453c9ca2d745e8dbd5bf2a27fa5440d", size = 3257239, upload-time = "2025-06-18T13:24:21.069Z" }, - { url = "https://files.pythonhosted.org/packages/31/db/6c182e645e8d138340933901ad30fc8fac5191956822bbfafb00e93fd27f/python_kadmin_rs-0.6.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:75544db4642771af04ff4888fe89448b9004ae11eaa7274394c46862a599be13", size = 3376292, upload-time = "2025-06-18T13:24:23.024Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b0/96a27cd7c6fc3ff0c2723d6659c49198815eeab2da3885ef923ea346e571/python_kadmin_rs-0.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f4c2876bf8e598466752fda9d79ef3719ce6b3936ba322004280601fea64d99c", size = 1567502, upload-time = "2025-06-18T13:24:24.539Z" }, - { url = "https://files.pythonhosted.org/packages/d7/96/e396b927ce6ccaa4b385b9b54440c133406a3ac070ce6a8a0786d8ba0308/python_kadmin_rs-0.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b271f5849548a7c248309fa30e4ee90465d6838aad11557fd208ba54da10a072", size = 1612724, upload-time = "2025-06-18T13:24:26.359Z" }, -] +version = "0.6.3" +source = { git = "https://github.com/authentik-community/kadmin-rs.git?rev=2e82b7a59466b6267890f937054a97b8be006c99#2e82b7a59466b6267890f937054a97b8be006c99" } [[package]] name = "pywin32"