infra(gated): stage authentik-tenant-mapping-sync CronJob (Au-3, suspended)
Gated substrate (Cl2-4 / Cl-infra-3) — outside apps/ so the ApplicationSet will not deploy it, and spec.suspend: true. Reconciles the 1Password tenant-mapping doc into Authentik groups via Connect REST. Activate at Au-3 public-go (un-suspend + materialize the script ConfigMap). Pairs Codex Cx2-7. Canonical script: FlowerCore.Notes/scripts/authentik/authentik-tenant-mapping-sync.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
151
gated/authentik-tenant-sync/cronjob.yaml
Normal file
151
gated/authentik-tenant-sync/cronjob.yaml
Normal file
@@ -0,0 +1,151 @@
|
||||
# =====================================================================================
|
||||
# authentik-tenant-mapping-sync — GATED nightly CronJob (Au-3 / ADR-198 §2.A P1)
|
||||
#
|
||||
# STATUS: GATED. spec.suspend: true (belt-and-suspenders). This manifest lives in a Notes
|
||||
# STAGING path (docs/gated-manifests/) and is NOT under bluejay-infra apps/, so ArgoCD
|
||||
# does not deploy it. It does NOTHING until Au-3 public-go (see README.md in this dir).
|
||||
#
|
||||
# WHAT IT RUNS: scripts/authentik/authentik-tenant-mapping-sync.py (Notes repo) — reads the
|
||||
# 1Password Document `flowercore-tenant-mapping` via Connect REST and reconciles its
|
||||
# mappings[].authentikGroup entries into Authentik groups (idempotent; never deletes
|
||||
# unmanaged groups). Pairs Codex Cx2-7.
|
||||
#
|
||||
# SECRETS (referenced, NOT invented — no secret VALUES in this file):
|
||||
# AUTHENTIK_TOKEN <- Secret authentik/authentik-credentials key BOOTSTRAP_ADMIN_TOKEN (exists)
|
||||
# OP_TOKEN <- Secret authentik/tenant-mapping-sync-op-token key token
|
||||
# (a copy of onepassword-system/onepassword-token — see README "OP_TOKEN
|
||||
# cross-namespace" for the one-liner that mints it; OR mint via the
|
||||
# OnePasswordItem CRD per feedback_1password_operator_pattern).
|
||||
#
|
||||
# The script is delivered via the ConfigMap below (same pattern as guacamole guac-k8s-sync).
|
||||
# When this lane is libraryized/imaged later (ADR-198 §2.B P3) this ConfigMap can be replaced
|
||||
# by a baked image; for now ConfigMap-delivery keeps the script the single source of truth.
|
||||
# =====================================================================================
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: authentik-tenant-mapping-sync
|
||||
namespace: authentik
|
||||
labels:
|
||||
app.kubernetes.io/name: authentik-tenant-mapping-sync
|
||||
app.kubernetes.io/component: sync
|
||||
app.kubernetes.io/part-of: flowercore-identity
|
||||
flowercore.io/adr: "198"
|
||||
flowercore.io/gated: "true"
|
||||
annotations:
|
||||
flowercore.io/gate: "Au-3 public-go — suspended until tenant self-registration goes live"
|
||||
flowercore.io/pairs-with: "Codex Cx2-7"
|
||||
spec:
|
||||
# GATE: suspended so it never fires until an operator un-suspends at Au-3 public-go.
|
||||
suspend: true
|
||||
# Nightly at 03:17 (off-peak; jittered minute to avoid colliding with other 03:00 jobs).
|
||||
schedule: "17 3 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
startingDeadlineSeconds: 600
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
backoffLimit: 2
|
||||
activeDeadlineSeconds: 600
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: authentik-tenant-mapping-sync
|
||||
app.kubernetes.io/component: sync
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
fsGroup: 65532
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: sync
|
||||
# python:3.12-slim is sufficient: the script uses only the stdlib (urllib/json/ssl).
|
||||
# No pip install needed. Pin a digest at activation time for air-gap reproducibility.
|
||||
image: python:3.12-slim
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- python3
|
||||
- /scripts/authentik-tenant-mapping-sync.py
|
||||
# NOTE: no --dry-run here -> this is the real reconcile. Operators wanting a
|
||||
# dry-run first should `kubectl create job --from=cronjob/... ` with the arg
|
||||
# appended, or run the script from a workstation. See README.
|
||||
env:
|
||||
- name: AUTHENTIK_URL
|
||||
value: "https://id.iamworkin.lan"
|
||||
- name: OP_CONNECT_URL
|
||||
value: "http://10.0.56.10:8180/v1" # port 8180, NOT 8443
|
||||
- name: OP_VAULT_ID
|
||||
value: "qaphopopkryhbg353ukzhhuqoq" # IAmWorkin
|
||||
- name: TENANT_MAPPING_ITEM
|
||||
value: "flowercore-tenant-mapping"
|
||||
- name: TENANT_MAPPING_FIELD
|
||||
value: "mapping"
|
||||
- name: AUTHENTIK_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: authentik-credentials
|
||||
key: BOOTSTRAP_ADMIN_TOKEN
|
||||
- name: OP_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
# A same-namespace copy of onepassword-system/onepassword-token.
|
||||
# See README "OP_TOKEN cross-namespace". Until Au-3 this Secret need
|
||||
# not exist (the job is suspended).
|
||||
name: tenant-mapping-sync-op-token
|
||||
key: token
|
||||
resources:
|
||||
requests:
|
||||
cpu: 25m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
volumeMounts:
|
||||
- name: script
|
||||
mountPath: /scripts
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: script
|
||||
configMap:
|
||||
name: authentik-tenant-mapping-sync-script
|
||||
defaultMode: 0555
|
||||
---
|
||||
# The reconcile script, delivered as a ConfigMap (single source of truth = the Notes repo
|
||||
# scripts/authentik/authentik-tenant-mapping-sync.py). At activation, regenerate this
|
||||
# ConfigMap from the live script so the two never drift, e.g.:
|
||||
# kubectl create configmap authentik-tenant-mapping-sync-script -n authentik \
|
||||
# --from-file=authentik-tenant-mapping-sync.py=scripts/authentik/authentik-tenant-mapping-sync.py \
|
||||
# --dry-run=client -o yaml > docs/gated-manifests/authentik-tenant-sync/configmap.script.yaml
|
||||
# (kept as a placeholder body here so the manifest set is self-describing; the real body is
|
||||
# the script file — DO NOT hand-edit a divergent copy into this ConfigMap.)
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: authentik-tenant-mapping-sync-script
|
||||
namespace: authentik
|
||||
labels:
|
||||
app.kubernetes.io/name: authentik-tenant-mapping-sync
|
||||
app.kubernetes.io/component: sync
|
||||
flowercore.io/gated: "true"
|
||||
annotations:
|
||||
flowercore.io/source: "scripts/authentik/authentik-tenant-mapping-sync.py (Notes repo) — regenerate at activation, do not hand-edit"
|
||||
data:
|
||||
authentik-tenant-mapping-sync.py: |
|
||||
# PLACEHOLDER — regenerate from the canonical script at activation (see annotation above).
|
||||
# The Notes repo file scripts/authentik/authentik-tenant-mapping-sync.py is the source of
|
||||
# truth; embedding a hand-copy here would drift. The orchestrator (or the activation
|
||||
# runbook) materializes this ConfigMap from the live script via `kubectl create configmap
|
||||
# ... --from-file=...` before un-suspending the CronJob.
|
||||
import sys
|
||||
sys.exit("authentik-tenant-mapping-sync ConfigMap not materialized from the canonical "
|
||||
"script — regenerate with kubectl create configmap --from-file before activation.")
|
||||
Reference in New Issue
Block a user