From 8eb57f9c19bd4fd277d8dc2359024f49e100ab05 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Wed, 26 Aug 2026 23:19:24 +1000 Subject: [PATCH] authentik: deploy LDAP outpost for in-cluster app-password binds (#422) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Why Jellyfin authenticates users against Authentik over LDAP (app-password binds). The `authentik-ldap` Service, Gateway, and TLSRoute already exist in the authentik base, but nothing backed the Service — no LDAP outpost was ever deployed. This adds the missing Deployment so in-cluster clients (Jellyfin) can bind. Deployed as a separate Deployment inside the existing `authentik` namespace rather than a new app/namespace: the authentik overlay is already wired into the `platform` ApplicationSet and the LDAP Service/Gateway/TLSRoute already live here, so this needs no new AppProject/ApplicationSet wiring and keeps the outpost next to the core it serves. ## How - `ldap-outpost-deployment.yaml`: 2-replica (stateless) Deployment, image `ghcr.io/goauthentik/ldap:2026.5.3` (canonical upstream name; matches the deployed authentik chart version; containerd mirrors route via artifactapi). Pod labels match the existing `authentik-ldap` Service selector. - `AUTHENTIK_HOST=https://identity.k8s.syd1.au.unkin.net`, `AUTHENTIK_INSECURE=false`. - Trusts the internal CA via the established combine-certs initContainer pattern (concatenates the base roots with the reflected `vault-ca-cert`) + `SSL_CERT_FILE`. - `AUTHENTIK_TOKEN` sourced from secret `authentik-ldap-outpost-token`. - Resources set; TCP probes on the LDAP port; reloader annotation for token + CA rotation. - `ldap-outpost-vaultstaticsecret.yaml`: VSS (reuses the namespace `default` VaultAuth) materialising the token from KV `kubernetes/namespace/authentik/default/outpost-token` (key `token`). - `ldap-service.yaml`: adds the plaintext `ldap` port 3389 alongside the existing `ldaps` 6636 (ClusterIP only, no public exposure — external reach is via the pre-existing internal Gateway/TLSRoute). - `ldap-outpost-vmpodscrape.yaml`: VMPodScrape for the outpost metrics endpoint (:9300). - No sync-wave annotations. ## Dependency — token seed (blocking) The outpost pods CrashLoop until the token exists. After merge, the Authentik LDAP outpost token must be seeded into Vault KV at `kv/kubernetes/namespace/authentik/default/outpost-token` with key `token`. This path is provided by the corresponding terraform-authentik PR; the value must match the outpost token authentik issues. ## Validation `kustomize build --enable-helm apps/overlays/au-syd1/authentik` + repo kubeconform: 38 resources, Valid: 38, Invalid: 0, Errors: 0. Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/422 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/authentik/kustomization.yaml | 3 + .../authentik/ldap-outpost-deployment.yaml | 104 ++++++++++++++++++ .../ldap-outpost-vaultstaticsecret.yaml | 20 ++++ .../authentik/ldap-outpost-vmpodscrape.yaml | 16 +++ apps/base/authentik/ldap-service.yaml | 4 + 5 files changed, 147 insertions(+) create mode 100644 apps/base/authentik/ldap-outpost-deployment.yaml create mode 100644 apps/base/authentik/ldap-outpost-vaultstaticsecret.yaml create mode 100644 apps/base/authentik/ldap-outpost-vmpodscrape.yaml diff --git a/apps/base/authentik/kustomization.yaml b/apps/base/authentik/kustomization.yaml index fbc2dcf..8aa078a 100644 --- a/apps/base/authentik/kustomization.yaml +++ b/apps/base/authentik/kustomization.yaml @@ -10,6 +10,9 @@ resources: - httproute.yaml - ldap-gateway.yaml - ldap-httproute.yaml + - ldap-outpost-deployment.yaml + - ldap-outpost-vaultstaticsecret.yaml + - ldap-outpost-vmpodscrape.yaml - ldap-service.yaml - ldap-tlsroute.yaml - namespace.yaml diff --git a/apps/base/authentik/ldap-outpost-deployment.yaml b/apps/base/authentik/ldap-outpost-deployment.yaml new file mode 100644 index 0000000..7cabdb4 --- /dev/null +++ b/apps/base/authentik/ldap-outpost-deployment.yaml @@ -0,0 +1,104 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: authentik-ldap-outpost + namespace: authentik + labels: + app.kubernetes.io/name: authentik + app.kubernetes.io/component: ldap +spec: + # Outposts are stateless; run two replicas for availability. + replicas: 2 + selector: + matchLabels: + app.kubernetes.io/name: authentik + app.kubernetes.io/component: ldap + template: + metadata: + annotations: + secret.reloader.stakater.com/reload: "authentik-ldap-outpost-token,vault-ca-cert" + labels: + app.kubernetes.io/name: authentik + app.kubernetes.io/component: ldap + spec: + # The outpost validates the authentik core cert (identity.k8s.syd1.au.unkin.net, + # signed by the internal unkin.net CA). Combine the base image's public roots + # with the reflected vault-ca-cert into one bundle that SSL_CERT_FILE points at, + # so AUTHENTIK_INSECURE stays false. + initContainers: + - name: combine-certs + image: alpine:3 + command: + - sh + - -c + - cat /etc/ssl/certs/ca-certificates.crt /custom-ca/ca.crt > /combined-certs/ca-certificates.crt + volumeMounts: + - name: vault-ca-cert + mountPath: /custom-ca + readOnly: true + - name: combined-certs + mountPath: /combined-certs + resources: + limits: + cpu: 100m + memory: 64Mi + requests: + cpu: 25m + memory: 32Mi + containers: + - name: ldap + image: ghcr.io/goauthentik/ldap:2026.5.3 + imagePullPolicy: IfNotPresent + env: + - name: AUTHENTIK_HOST + value: https://identity.k8s.syd1.au.unkin.net + - name: AUTHENTIK_INSECURE + value: "false" + - name: SSL_CERT_FILE + value: /etc/ssl/combined/ca-certificates.crt + - name: AUTHENTIK_TOKEN + valueFrom: + secretKeyRef: + name: authentik-ldap-outpost-token + key: token + ports: + - containerPort: 3389 + name: ldap + protocol: TCP + - containerPort: 6636 + name: ldaps + protocol: TCP + - containerPort: 9300 + name: metrics + protocol: TCP + livenessProbe: + tcpSocket: + port: ldap + initialDelaySeconds: 10 + periodSeconds: 15 + readinessProbe: + tcpSocket: + port: ldap + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: "1" + memory: 512Mi + requests: + cpu: 50m + memory: 128Mi + volumeMounts: + - name: combined-certs + mountPath: /etc/ssl/combined + readOnly: true + volumes: + - name: vault-ca-cert + secret: + secretName: vault-ca-cert + items: + - key: ca.crt + path: ca.crt + - name: combined-certs + emptyDir: {} diff --git a/apps/base/authentik/ldap-outpost-vaultstaticsecret.yaml b/apps/base/authentik/ldap-outpost-vaultstaticsecret.yaml new file mode 100644 index 0000000..d068d6e --- /dev/null +++ b/apps/base/authentik/ldap-outpost-vaultstaticsecret.yaml @@ -0,0 +1,20 @@ +--- +# Outpost API token, issued by authentik for the LDAP outpost and seeded into +# Vault by the terraform-authentik apply. The KV value must exist at this path +# with a `token` key before the outpost can connect. +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: authentik-ldap-outpost-token + namespace: authentik +spec: + destination: + create: true + name: authentik-ldap-outpost-token + overwrite: true + hmacSecretData: true + mount: kv + path: kubernetes/namespace/authentik/default/outpost-token + refreshAfter: 5m + type: kv-v2 + vaultAuthRef: default diff --git a/apps/base/authentik/ldap-outpost-vmpodscrape.yaml b/apps/base/authentik/ldap-outpost-vmpodscrape.yaml new file mode 100644 index 0000000..5b0c1b1 --- /dev/null +++ b/apps/base/authentik/ldap-outpost-vmpodscrape.yaml @@ -0,0 +1,16 @@ +--- +# Scrape the LDAP outpost's Prometheus endpoint (:9300). Picked up by the +# observability VMAgent (selectAllByDefault). +apiVersion: operator.victoriametrics.com/v1beta1 +kind: VMPodScrape +metadata: + name: authentik-ldap-outpost + namespace: authentik +spec: + selector: + matchLabels: + app.kubernetes.io/name: authentik + app.kubernetes.io/component: ldap + podMetricsEndpoints: + - port: metrics + path: /metrics diff --git a/apps/base/authentik/ldap-service.yaml b/apps/base/authentik/ldap-service.yaml index b9c4079..ec5c36a 100644 --- a/apps/base/authentik/ldap-service.yaml +++ b/apps/base/authentik/ldap-service.yaml @@ -7,6 +7,10 @@ metadata: spec: internalTrafficPolicy: Cluster ports: + - name: ldap + port: 3389 + protocol: TCP + targetPort: 3389 - name: ldaps port: 6636 protocol: TCP