Files
net-kingdom/sso-mfa/k8s/privacyidea/deployment.yaml
Bernd Worsch f227dfbd3d fix(privacyidea): add PI_ADDRESS/PI_PORT; switch readiness probe to tcpSocket
gpappsoft entrypoint requires PI_ADDRESS and PI_PORT env vars to build
the gunicorn bind argument. Without them the container crashes immediately.

/token/ returns 401 for unauthenticated GET requests so the httpGet
readiness probe was permanently failing. Switch to tcpSocket to match
the startup and liveness probes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 10:41:13 +00:00

162 lines
6.2 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deployment + Service — privacyIDEA (namespace: mfa)
#
# Prerequisites (apply in order):
# 1. pvc.yaml — privacyidea-data and privacyidea-logs PVCs
# 2. configmap.yaml — privacyidea-cfg (pi.cfg template)
# 3. create-secrets.sh — privacyidea-config Secret (PI_SECRET_KEY, PI_PEPPER, DB URI)
# 4. This file
#
# After first pod starts successfully:
# 5. enckey-bootstrap.sh — extract enckey + audit keys, create DR Secrets
# 6. bootstrap-admin.sh — create pi-admin (+ MFA enrolment) and trigger-admin
#
# Container port: 8080.
# ghcr.io/gpappsoft/privacyidea-docker listens on port 8080 (gunicorn).
apiVersion: apps/v1
kind: Deployment
metadata:
name: privacyidea
namespace: mfa
labels:
app.kubernetes.io/name: privacyidea
app.kubernetes.io/part-of: net-kingdom-sso-mfa
net-kingdom/component: mfa
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: privacyidea
strategy:
type: Recreate # single-node — avoid split-brain on PVC
template:
metadata:
labels:
app.kubernetes.io/name: privacyidea
app.kubernetes.io/part-of: net-kingdom-sso-mfa
net-kingdom/component: mfa
spec:
# ── Security context ───────────────────────────────────────────────────
securityContext:
runAsNonRoot: false # privacyIDEA nginx needs root to bind port; revisit
fsGroup: 999 # privacyidea group inside container
# ── Init: ensure log dir exists and has correct permissions ───────────
initContainers:
- name: init-logdir
image: busybox:1.36
command: ["sh", "-c", "mkdir -p /var/log/privacyidea && chmod 777 /var/log/privacyidea"]
volumeMounts:
- name: logs
mountPath: /var/log/privacyidea
containers:
- name: privacyidea
# Pin to a specific release; update via image update policy.
# Official image: https://github.com/gpappsoft/privacyidea-docker
# privacyidea/privacyidea:3.12 and privacyidea/otpserver:3.12.2 do not exist.
# Correct image: ghcr.io/gpappsoft/privacyidea-docker:3.12.2 (port 8080)
image: ghcr.io/gpappsoft/privacyidea-docker:3.12.2
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
protocol: TCP
# ── Environment ─────────────────────────────────────────────────
# Tell gpappsoft entrypoint to use our mounted pi.cfg instead of
# the image's built-in /privacyidea/etc/pi.cfg.
env:
- name: PRIVACYIDEA_CONFIGFILE
value: /etc/privacyidea/pi.cfg
# gpappsoft entrypoint passes these to gunicorn as the bind address/port.
- name: PI_ADDRESS
value: "0.0.0.0"
- name: PI_PORT
value: "8080"
# Sensitive values from Secret (PI_SECRET_KEY, PI_PEPPER, PI_SQLALCHEMY_DATABASE_URI)
envFrom:
- secretRef:
name: privacyidea-config
# ── Volume mounts ───────────────────────────────────────────────
volumeMounts:
# pi.cfg overlaid into the data PVC as a single file (subPath).
- name: config
mountPath: /etc/privacyidea/pi.cfg
subPath: pi.cfg
readOnly: true
# Data PVC: enckey, audit keys, scripts, and other PI runtime files.
- name: data
mountPath: /etc/privacyidea
# Logs PVC: persistent application logs.
- name: logs
mountPath: /var/log/privacyidea
# ── Probes ──────────────────────────────────────────────────────
# Startup probe: give PI up to 3 min to run DB migrations on first boot.
startupProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 18 # 18 × 10s = 3 min
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 0
periodSeconds: 15
failureThreshold: 3
readinessProbe:
# /token/ returns 401 for unauthenticated GET; use tcpSocket instead.
# Switch to httpGet /healthz or similar once confirmed in the image.
tcpSocket:
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
# ── Resources ───────────────────────────────────────────────────
# Raise limits for production; privacyIDEA handles crypto and DB queries.
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# ── Volumes ─────────────────────────────────────────────────────────
volumes:
- name: config
configMap:
name: privacyidea-cfg
- name: data
persistentVolumeClaim:
claimName: privacyidea-data
- name: logs
persistentVolumeClaim:
claimName: privacyidea-logs
---
# Service — ClusterIP; Traefik and Keycloak reach privacyIDEA via this.
apiVersion: v1
kind: Service
metadata:
name: privacyidea
namespace: mfa
labels:
app.kubernetes.io/name: privacyidea
app.kubernetes.io/part-of: net-kingdom-sso-mfa
net-kingdom/component: mfa
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: privacyidea
ports:
- name: http
port: 8080
targetPort: 8080
protocol: TCP