# 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: 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
