Stack: - PostgreSQL 16 StatefulSet (Longhorn RWO 5Gi) - Redis 7 Deployment (no persistence) - Authentik server + worker (ghcr.io/goauthentik/server:2024.12.3) - Shared media PVC (Longhorn RWO 2Gi) between server+worker - Certificate via step-ca-acme ClusterIssuer - Traefik IngressRoute at id.iamworkin.lan Secrets sourced from 1Password item 'authentik-credentials' (IAmWorkin vault, id y6i74ch22q5wvm7znquq4nhhcu) via OnePasswordItem CRD. Fields: AUTHENTIK_SECRET_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD, BOOTSTRAP_ADMIN_PASSWORD, BOOTSTRAP_ADMIN_TOKEN, BOOTSTRAP_ADMIN_EMAIL. DNS A record id.iamworkin.lan -> 10.0.56.200 added via scripts/pfsense-add-id-host.py (FlowerCore.DNS service was 502'ing on pfSense diag_command.php response parsing). Closes the immediate gap from PiManager OIDC Cohort 3 wire-up: PiManager (a87cd6f) configures id.iamworkin.lan as JWT authority but the backend was never deployed. Pirelay specifically is on Mode:apikey until this backend is bootstrapped and a pimanager service-account exists. Post-deploy bootstrap (manual once pods Ready): 1. Login at https://id.iamworkin.lan/if/admin/ as akadmin using BOOTSTRAP_ADMIN_PASSWORD from 1Password. 2. Create OAuth2/OpenID Provider for pimanager (issuer https://id.iamworkin.lan/application/o/pimanager/, audience 'pimanager'). 3. Create Application binding the provider. 4. Create service account user 'pimanager-service-account', generate long-lived token, store in 1Password as 'pimanager-service-account'. 5. Re-enable jwt mode on pirelay + un-mask puppet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
429 lines
12 KiB
YAML
429 lines
12 KiB
YAML
# Authentik OIDC backend
|
|
# ArgoCD-managed. BlueJay Lab.
|
|
#
|
|
# Stack:
|
|
# - PostgreSQL 16 StatefulSet (single replica, Longhorn RWO 5Gi)
|
|
# - Redis 7 Deployment (no persistence — session/cache only)
|
|
# - Authentik server + worker Deployments (image ghcr.io/goauthentik/server:2024.12.3)
|
|
# - Media PVC shared between server + worker (Longhorn RWO 2Gi)
|
|
# - Certificate via step-ca-acme ClusterIssuer
|
|
# - Traefik IngressRoute at id.iamworkin.lan
|
|
#
|
|
# Secrets come from 1Password item "authentik-credentials" (IAmWorkin vault, id y6i74ch22q5wvm7znquq4nhhcu)
|
|
# via the OnePasswordItem CRD, materialized into k8s Secret authentik/authentik-credentials.
|
|
#
|
|
# Why the discovery URL is /application/o/pimanager/ : Authentik issues per-application OIDC providers.
|
|
# The pimanager OIDC application/provider is created after the cluster pods are healthy (manual or
|
|
# via API once the bootstrap token is available — see Notes substrate).
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: authentik
|
|
labels:
|
|
app.kubernetes.io/part-of: bluejay-infra
|
|
|
|
---
|
|
# 1Password operator pulls the authentik-credentials item into a k8s Secret of the same name.
|
|
# Field labels in 1P become Secret keys: AUTHENTIK_SECRET_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD,
|
|
# BOOTSTRAP_ADMIN_PASSWORD, BOOTSTRAP_ADMIN_TOKEN, BOOTSTRAP_ADMIN_EMAIL.
|
|
apiVersion: onepassword.com/v1
|
|
kind: OnePasswordItem
|
|
metadata:
|
|
name: authentik-credentials
|
|
namespace: authentik
|
|
spec:
|
|
itemPath: "vaults/IAmWorkin/items/authentik-credentials"
|
|
|
|
---
|
|
# Shared media volume for server + worker pods.
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: authentik-media
|
|
namespace: authentik
|
|
spec:
|
|
storageClassName: longhorn
|
|
accessModes: [ReadWriteOnce]
|
|
resources:
|
|
requests:
|
|
storage: 2Gi
|
|
|
|
---
|
|
# PostgreSQL 16 StatefulSet — Authentik's primary store.
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: authentik-postgres
|
|
namespace: authentik
|
|
labels:
|
|
app: authentik-postgres
|
|
argocd.argoproj.io/instance: infra-authentik
|
|
spec:
|
|
persistentVolumeClaimRetentionPolicy:
|
|
whenDeleted: Retain
|
|
whenScaled: Retain
|
|
podManagementPolicy: OrderedReady
|
|
serviceName: authentik-postgres
|
|
replicas: 1
|
|
revisionHistoryLimit: 10
|
|
selector:
|
|
matchLabels:
|
|
app: authentik-postgres
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: authentik-postgres
|
|
spec:
|
|
containers:
|
|
- name: postgres
|
|
image: postgres:16-alpine
|
|
ports:
|
|
- containerPort: 5432
|
|
name: postgres
|
|
env:
|
|
- name: POSTGRES_USER
|
|
value: authentik
|
|
- name: POSTGRES_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: POSTGRES_PASSWORD
|
|
- name: POSTGRES_DB
|
|
value: authentik
|
|
- name: POSTGRES_INITDB_ARGS
|
|
value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
|
|
- name: PGDATA
|
|
value: /var/lib/postgresql/data/pgdata
|
|
readinessProbe:
|
|
exec:
|
|
command: ["pg_isready", "-U", "authentik"]
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 5
|
|
livenessProbe:
|
|
exec:
|
|
command: ["pg_isready", "-U", "authentik"]
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
resources:
|
|
requests: { cpu: 100m, memory: 256Mi }
|
|
limits: { cpu: 1000m, memory: 1Gi }
|
|
volumeMounts:
|
|
- name: pgdata
|
|
mountPath: /var/lib/postgresql/data
|
|
volumeClaimTemplates:
|
|
- metadata:
|
|
name: pgdata
|
|
spec:
|
|
storageClassName: longhorn
|
|
accessModes: [ReadWriteOnce]
|
|
volumeMode: Filesystem
|
|
resources:
|
|
requests:
|
|
storage: 5Gi
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: authentik-postgres
|
|
namespace: authentik
|
|
spec:
|
|
clusterIP: None
|
|
selector:
|
|
app: authentik-postgres
|
|
ports:
|
|
- name: postgres
|
|
port: 5432
|
|
targetPort: 5432
|
|
|
|
---
|
|
# Redis 7 — session storage + Celery broker. No persistence needed (cache).
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: authentik-redis
|
|
namespace: authentik
|
|
labels:
|
|
app: authentik-redis
|
|
argocd.argoproj.io/instance: infra-authentik
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate
|
|
selector:
|
|
matchLabels:
|
|
app: authentik-redis
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: authentik-redis
|
|
spec:
|
|
containers:
|
|
- name: redis
|
|
image: redis:7-alpine
|
|
args:
|
|
- "--save"
|
|
- ""
|
|
- "--appendonly"
|
|
- "no"
|
|
- "--requirepass"
|
|
- "$(REDIS_PASSWORD)"
|
|
env:
|
|
- name: REDIS_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: REDIS_PASSWORD
|
|
ports:
|
|
- containerPort: 6379
|
|
name: redis
|
|
readinessProbe:
|
|
tcpSocket: { port: 6379 }
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 5
|
|
livenessProbe:
|
|
tcpSocket: { port: 6379 }
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
resources:
|
|
requests: { cpu: 50m, memory: 64Mi }
|
|
limits: { cpu: 500m, memory: 256Mi }
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: authentik-redis
|
|
namespace: authentik
|
|
spec:
|
|
selector:
|
|
app: authentik-redis
|
|
ports:
|
|
- name: redis
|
|
port: 6379
|
|
targetPort: 6379
|
|
|
|
---
|
|
# Authentik server Deployment — HTTP frontend on :9000.
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: authentik-server
|
|
namespace: authentik
|
|
labels:
|
|
app: authentik-server
|
|
argocd.argoproj.io/instance: infra-authentik
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate # shares /media RWO PVC with worker
|
|
selector:
|
|
matchLabels:
|
|
app: authentik-server
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: authentik-server
|
|
spec:
|
|
containers:
|
|
- name: server
|
|
image: ghcr.io/goauthentik/server:2024.12.3
|
|
args: ["server"]
|
|
ports:
|
|
- containerPort: 9000
|
|
name: http
|
|
- containerPort: 9443
|
|
name: https
|
|
env:
|
|
- name: AUTHENTIK_SECRET_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: AUTHENTIK_SECRET_KEY
|
|
- name: AUTHENTIK_REDIS__HOST
|
|
value: authentik-redis
|
|
- name: AUTHENTIK_REDIS__PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: REDIS_PASSWORD
|
|
- name: AUTHENTIK_POSTGRESQL__HOST
|
|
value: authentik-postgres
|
|
- name: AUTHENTIK_POSTGRESQL__NAME
|
|
value: authentik
|
|
- name: AUTHENTIK_POSTGRESQL__USER
|
|
value: authentik
|
|
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: POSTGRES_PASSWORD
|
|
- name: AUTHENTIK_BOOTSTRAP_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: BOOTSTRAP_ADMIN_PASSWORD
|
|
- name: AUTHENTIK_BOOTSTRAP_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: BOOTSTRAP_ADMIN_TOKEN
|
|
- name: AUTHENTIK_BOOTSTRAP_EMAIL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: BOOTSTRAP_ADMIN_EMAIL
|
|
- name: AUTHENTIK_DISABLE_UPDATE_CHECK
|
|
value: "true"
|
|
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
|
value: "false"
|
|
- name: AUTHENTIK_LOG_LEVEL
|
|
value: info
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /-/health/ready/
|
|
port: 9000
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 10
|
|
timeoutSeconds: 5
|
|
failureThreshold: 6
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /-/health/live/
|
|
port: 9000
|
|
initialDelaySeconds: 60
|
|
periodSeconds: 30
|
|
timeoutSeconds: 10
|
|
failureThreshold: 3
|
|
resources:
|
|
requests: { cpu: 150m, memory: 512Mi }
|
|
limits: { cpu: 1500m, memory: 1Gi }
|
|
volumeMounts:
|
|
- name: media
|
|
mountPath: /media
|
|
volumes:
|
|
- name: media
|
|
persistentVolumeClaim:
|
|
claimName: authentik-media
|
|
|
|
---
|
|
# Authentik worker Deployment — runs Celery background tasks.
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: authentik-worker
|
|
namespace: authentik
|
|
labels:
|
|
app: authentik-worker
|
|
argocd.argoproj.io/instance: infra-authentik
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate # shares /media RWO PVC with server
|
|
selector:
|
|
matchLabels:
|
|
app: authentik-worker
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: authentik-worker
|
|
spec:
|
|
containers:
|
|
- name: worker
|
|
image: ghcr.io/goauthentik/server:2024.12.3
|
|
args: ["worker"]
|
|
env:
|
|
- name: AUTHENTIK_SECRET_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: AUTHENTIK_SECRET_KEY
|
|
- name: AUTHENTIK_REDIS__HOST
|
|
value: authentik-redis
|
|
- name: AUTHENTIK_REDIS__PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: REDIS_PASSWORD
|
|
- name: AUTHENTIK_POSTGRESQL__HOST
|
|
value: authentik-postgres
|
|
- name: AUTHENTIK_POSTGRESQL__NAME
|
|
value: authentik
|
|
- name: AUTHENTIK_POSTGRESQL__USER
|
|
value: authentik
|
|
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: authentik-credentials
|
|
key: POSTGRES_PASSWORD
|
|
- name: AUTHENTIK_DISABLE_UPDATE_CHECK
|
|
value: "true"
|
|
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
|
value: "false"
|
|
- name: AUTHENTIK_LOG_LEVEL
|
|
value: info
|
|
resources:
|
|
requests: { cpu: 100m, memory: 256Mi }
|
|
limits: { cpu: 1000m, memory: 768Mi }
|
|
volumeMounts:
|
|
- name: media
|
|
mountPath: /media
|
|
volumes:
|
|
- name: media
|
|
persistentVolumeClaim:
|
|
claimName: authentik-media
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: authentik-server
|
|
namespace: authentik
|
|
spec:
|
|
selector:
|
|
app: authentik-server
|
|
ports:
|
|
- name: http
|
|
port: 9000
|
|
targetPort: 9000
|
|
- name: https
|
|
port: 9443
|
|
targetPort: 9443
|
|
|
|
---
|
|
# step-ca leaf certificate for id.iamworkin.lan.
|
|
# step-ca container resolver uses pfSense Unbound, so the public A record for id.iamworkin.lan
|
|
# MUST exist before this Certificate is applied (cert-manager HTTP-01 will silently 2h-backoff
|
|
# otherwise). Added 2026-05-25 via scripts/pfsense-add-id-host.py.
|
|
apiVersion: cert-manager.io/v1
|
|
kind: Certificate
|
|
metadata:
|
|
name: authentik-tls
|
|
namespace: authentik
|
|
spec:
|
|
secretName: authentik-tls
|
|
dnsNames:
|
|
- id.iamworkin.lan
|
|
issuerRef:
|
|
name: step-ca-acme
|
|
kind: ClusterIssuer
|
|
|
|
---
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: IngressRoute
|
|
metadata:
|
|
name: authentik
|
|
namespace: authentik
|
|
spec:
|
|
entryPoints: [websecure]
|
|
routes:
|
|
- match: Host(`id.iamworkin.lan`)
|
|
kind: Rule
|
|
services:
|
|
- name: authentik-server
|
|
port: 9000
|
|
tls:
|
|
secretName: authentik-tls
|