# 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: 5001. # privacyidea/otpserver listens on port 5001 internally. 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://hub.docker.com/r/privacyidea/otpserver # privacyidea/privacyidea:3.12 does not exist — correct repo is otpserver. image: privacyidea/otpserver:3.12.2 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 5001 protocol: TCP # ── Environment — sensitive values from Secret ────────────────── 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: 5001 initialDelaySeconds: 15 periodSeconds: 10 failureThreshold: 18 # 18 × 10s = 3 min livenessProbe: tcpSocket: port: 5001 initialDelaySeconds: 0 periodSeconds: 15 failureThreshold: 3 readinessProbe: httpGet: path: /token/ port: 5001 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: 5001 targetPort: 5001 protocol: TCP