- Add local-llm OAuth2 provider and application to Authentik provisioning - Configure JWT-compatible OAuth2 provider (client_id: local-llm) - Generate client secret on first run, stored in llm-serving/local-llm-jwt Secret - Bind llm-admins group to local-llm application for admin access - Add RBAC for provisioning job to create secrets in llm-serving namespace - Output JWT issuer URL and certificate for local-llm token validation
210 lines
6.1 KiB
YAML
210 lines
6.1 KiB
YAML
# 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
|