# Authentik OAuth provisioning — PostSync hook, reruns on every ArgoCD sync # (hook-delete-policy: BeforeHookCreation deletes the previous run's Job before # creating a new one, so this stays reconciled the same way the rest of the # cluster does — no separate manual bootstrap step like setup_talos_iam.sh / # provision_oidc.py, which never got migrated off the old helmfile workflow). # # What it does (see scripts/authentik-provision.py docstring): creates the # "groups" scope mapping, homelab-admins / grafana-admins groups, the "rock" # admin user, OAuth2 providers + Applications for grafana/minio/forgejo/argocd, # and binds homelab-admins to all of them. The script is generated into the # authentik-provision-script ConfigMap by kustomize configMapGenerator (see # kustomization.yaml), not embedded here. # # RBAC: this Job only touches Secrets (get existing client secrets, create new # ones for forgejo/argocd/rock) across the namespaces those services live in. # It never touches any other resource type. apiVersion: v1 kind: ServiceAccount metadata: name: authentik-provisioner namespace: iam --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: authentik-provisioner rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "create", "update", "patch"] --- # One RoleBinding per namespace the script touches (least-privilege: Secrets # only, and only in these 5 namespaces — not a cluster-wide ClusterRoleBinding). apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: iam subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: cicd subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: argocd subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: logging subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: storage subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: paperless subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: immich subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: authentik-provisioner namespace: llm-serving subjects: - kind: ServiceAccount name: authentik-provisioner namespace: iam roleRef: kind: ClusterRole name: authentik-provisioner apiGroup: rbac.authorization.k8s.io --- apiVersion: batch/v1 kind: Job metadata: name: authentik-provision namespace: iam annotations: argocd.argoproj.io/hook: PostSync argocd.argoproj.io/hook-delete-policy: BeforeHookCreation spec: ttlSecondsAfterFinished: 600 backoffLimit: 3 template: spec: serviceAccountName: authentik-provisioner restartPolicy: Never securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers: - name: provision image: python:3.12-alpine securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] env: - name: AUTHENTIK_BOOTSTRAP_TOKEN valueFrom: secretKeyRef: name: authentik-secrets key: AUTHENTIK_BOOTSTRAP_TOKEN volumeMounts: - name: script mountPath: /script command: - /bin/sh - -c - | set -e echo "waiting for authentik-server..." until wget -q -O /dev/null http://authentik-server.iam.svc.cluster.local/-/health/ready/ 2>/dev/null; do sleep 5 done echo "installing kubectl (via python urllib - no apk/curl: this" echo "container runs as non-root UID 1000 and can't write to" echo "apk's directories or /usr/local/bin, both root-owned in" echo "the python:3.12-alpine image; /tmp is world-writable)..." python3 -c " import urllib.request, os, stat kver = urllib.request.urlopen('https://dl.k8s.io/release/stable.txt').read().decode().strip() url = f'https://dl.k8s.io/release/{kver}/bin/linux/amd64/kubectl' urllib.request.urlretrieve(url, '/tmp/kubectl') st = os.stat('/tmp/kubectl') os.chmod('/tmp/kubectl', st.st_mode | stat.S_IEXEC) " export PATH="/tmp:$PATH" echo "running provisioning script..." python3 /script/authentik-provision.py volumes: - name: script configMap: name: authentik-provision-script