feat(authentik): automated SECRET_KEY rotation (openbao-ns) #302
@@ -4,7 +4,10 @@ metadata:
|
||||
name: authentik-secrets
|
||||
namespace: authentik
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
# 10m (was 1h) so an automated secret-key rotation propagates within ~10m;
|
||||
# Reloader then restarts server + worker. See
|
||||
# kubernetes/infrastructure/openbao/rotation/authentik.yaml.
|
||||
refreshInterval: 10m
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: openbao
|
||||
|
||||
@@ -35,6 +35,12 @@ spec:
|
||||
retries: 3
|
||||
values:
|
||||
global:
|
||||
# Reloader restarts BOTH the authentik server and worker Deployments when
|
||||
# authentik-secrets changes (e.g. after a SECRET_KEY rotation); global
|
||||
# podAnnotations propagate to every component pod. See
|
||||
# kubernetes/infrastructure/openbao/rotation/authentik.yaml.
|
||||
podAnnotations:
|
||||
secret.reloader.stakater.com/reload: authentik-secrets
|
||||
# Custom Authentik server image with the samba-bridge baked in.
|
||||
# The bridge synthesises sambaNTPassword (and the rest of the
|
||||
# Samba schema) on every User.set_password() call for any user
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# OpenBao policy: rotator-authentik
|
||||
#
|
||||
# Least-privilege policy for the authentik secret-key rotation CronJob (kubernetes
|
||||
# auth role `rotator-authentik`). Per-secret isolation — scoped to only the
|
||||
# authentik KV path (issue #213 blast-radius, #202 per-app segregation).
|
||||
# openbao-ns rotation pattern; see rotator-paperless.hcl for the template.
|
||||
#
|
||||
# NOT applied by Flux. Apply ONCE with an admin token:
|
||||
#
|
||||
# bao policy write rotator-authentik kubernetes/infrastructure/openbao/policies/rotator-authentik.hcl
|
||||
# bao write auth/kubernetes/role/rotator-authentik \
|
||||
# bound_service_account_names=authentik-secret-rotator \
|
||||
# bound_service_account_namespaces=openbao \
|
||||
# policies=rotator-authentik ttl=15m
|
||||
#
|
||||
# Do NOT set token_no_default_policy=true (kv-v2 preflight needs the default policy).
|
||||
#
|
||||
# NOTE: the authentik KV entry also holds `smtp-password`; the Job patches only
|
||||
# `secret-key`, and `bao kv patch` preserves smtp-password.
|
||||
path "homelab/data/apps/authentik" {
|
||||
capabilities = ["read", "patch"]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
# Automated rotation of AUTHENTIK_SECRET_KEY (Django signing key for the SSO IdP).
|
||||
# openbao-ns pattern (intra-namespace OpenBao access, no NetworkPolicy). Writes
|
||||
# OpenBao; ESO syncs the authentik-secrets Secret (ns authentik, 10m refresh);
|
||||
# Reloader restarts BOTH authentik server + worker (via global.podAnnotations in
|
||||
# the HelmRelease — both read the same secret-key, so ESO's one sync feeds both).
|
||||
#
|
||||
# BLAST RADIUS: rotating invalidates active authentik sessions and in-flight
|
||||
# signed links (password-reset / invitation / email-verification tokens) — users
|
||||
# re-login. Downstream SSO integrations (LDAP / proxy outposts) use separate
|
||||
# token/TLS material, NOT SECRET_KEY, so they are unaffected. Prefer an off-hours
|
||||
# window for on-demand runs. No data loss.
|
||||
#
|
||||
# ONE-TIME BOOTSTRAP (per rotator-authentik.hcl): apply the policy + role.
|
||||
# ON-DEMAND: kubectl -n openbao create job --from=cronjob/authentik-secret-rotate authentik-secret-rotate-now
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: authentik-secret-rotator
|
||||
namespace: openbao
|
||||
labels:
|
||||
app.kubernetes.io/name: authentik
|
||||
app.kubernetes.io/component: secret-rotator
|
||||
# OpenBao: auth/kubernetes/role/rotator-authentik binds this SA to the
|
||||
# rotator-authentik policy. Talks only to OpenBao → no k8s RBAC needed.
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: authentik-secret-rotate
|
||||
namespace: openbao
|
||||
labels:
|
||||
app.kubernetes.io/name: authentik
|
||||
app.kubernetes.io/component: secret-rotator
|
||||
spec:
|
||||
schedule: "0 4 1 1,4,7,10 *" # quarterly, Europe/Berlin
|
||||
timeZone: Europe/Berlin
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 2
|
||||
failedJobsHistoryLimit: 5
|
||||
startingDeadlineSeconds: 3600
|
||||
jobTemplate:
|
||||
spec:
|
||||
backoffLimit: 2
|
||||
activeDeadlineSeconds: 300
|
||||
ttlSecondsAfterFinished: 259200 # 3 d
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: authentik
|
||||
app.kubernetes.io/component: secret-rotator
|
||||
spec:
|
||||
serviceAccountName: authentik-secret-rotator
|
||||
restartPolicy: Never
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 100
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: rotate
|
||||
image: quay.io/openbao/openbao:2.5.5
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
env:
|
||||
- name: HOME
|
||||
value: /tmp
|
||||
command: ["/bin/sh", "-ec"]
|
||||
args:
|
||||
- |
|
||||
export BAO_ADDR=http://openbao.openbao.svc.cluster.local:8200
|
||||
BAO_TOKEN=$(bao write -field=token auth/kubernetes/login \
|
||||
role=rotator-authentik \
|
||||
jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)")
|
||||
export BAO_TOKEN
|
||||
# ~50-byte Django-style secret key.
|
||||
NEW=$(head -c 50 /dev/urandom | base64 | tr -d '\n')
|
||||
# Patch ONLY secret-key; smtp-password in the same KV entry untouched.
|
||||
bao kv patch homelab/apps/authentik "secret-key=${NEW}"
|
||||
echo "rotated secret-key at homelab/apps/authentik (value not logged)"
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
resources:
|
||||
requests:
|
||||
cpu: 25m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
memory: 64Mi
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
@@ -8,3 +8,4 @@ resources:
|
||||
- paperless.yaml
|
||||
- woodpecker.yaml
|
||||
- shelly-manager.yaml
|
||||
- authentik.yaml
|
||||
|
||||
Reference in New Issue
Block a user