# T04 — Phase 3: Deploy privacyIDEA Phase 3 of NK-WP-0001: deploys the MFA core (privacyIDEA) in the `mfa` namespace. **Hostnames (config points CP-NK-002 / CP-NK-003):** - `pink.coulomb.social` — main portal and API - `pink-account.coulomb.social` — self-service token portal **Prerequisites:** - T02 complete: `mfa` namespace and NetworkPolicies applied, cert-manager running. - T03 complete: PostgreSQL cluster `net-kingdom-pg` in `databases` namespace is Ready. - T01 Phase 0a complete: `gen-secrets.sh` run, all secrets in KeePassXC. --- ## Apply order ### Step 1 — Create the config Secret ```bash cd sso-mfa/k8s/privacyidea chmod +x create-secrets.sh enckey-bootstrap.sh bootstrap-admin.sh ./create-secrets.sh ``` Creates `privacyidea-config` in the `mfa` namespace (PI_SECRET_KEY, PI_PEPPER, PI_SQLALCHEMY_DATABASE_URI). --- ### Step 2 — Apply manifests ```bash # From sso-mfa/k8s/privacyidea/ kubectl apply -f pvc.yaml kubectl apply -f configmap.yaml kubectl apply -f middleware.yaml kubectl apply -f deployment.yaml kubectl apply -f ingress.yaml ``` **Wait for the pod to reach Running state** (DB migrations run on first start — allow up to 3 minutes): ```bash kubectl get pods -n mfa -w # Expected: privacyidea- 1/1 Running ``` If the pod is stuck in `Init` or `CrashLoopBackOff`, check logs: ```bash kubectl logs -n mfa -l app.kubernetes.io/name=privacyidea --previous ``` Common causes: - `privacyidea-config` Secret missing → run `create-secrets.sh` - PostgreSQL not reachable → verify T03, check NetworkPolicies - Wrong DB password → re-run `create-secrets.sh` with corrected secrets --- ### Step 3 — Extract key material and create DR Secrets Run **once** after the pod reaches `Running`: ```bash ./enckey-bootstrap.sh ``` This generates RSA audit keys (if not already created), extracts the encryption key and audit keys from the pod, and creates two K8s Secrets as disaster-recovery copies: - `privacyidea-enckey` - `privacyidea-auditkeys` Follow the printed instructions to store the key files in KeePassXC, then shred the local copies. --- ### Step 4 — Bootstrap admin accounts ```bash ./bootstrap-admin.sh ``` Creates `pi-admin` (full admin) and `trigger-admin` (triggerchallenge only). Also creates the `privacyidea-trigger-admin` K8s Secret used by Keycloak in T05. **Immediately after the script completes:** 1. Log in to `https://pink.coulomb.social` as `pi-admin`. 2. Navigate to **Users → pi-admin → Enroll token** and enroll a TOTP or hardware token. 3. Log out, log back in — the MFA challenge must appear. 4. Verify the `trigger-admin-rights` policy at Config → Policies. --- ### Step 5 — Verify ```bash cd sso-mfa/k8s chmod +x verify-t04.sh ./verify-t04.sh ``` --- ## Container port note The deployment uses `containerPort: 8080`. The official `privacyidea/privacyidea` image uses nginx internally; the default nginx port may be 80 depending on the image version. **If the pod starts but requests return "connection refused":** ```bash # Check what port the container actually listens on: kubectl exec -n mfa -- ss -tlnp | grep LISTEN ``` If the container uses port 80, update: 1. `deployment.yaml`: `containerPort: 80`, Service `targetPort: 80` 2. `sso-mfa/k8s/network-policies/netpol-mfa.yaml`: `port: 80` in privacyIDEA rules 3. Reapply both files. --- ## NetworkPolicy design privacyIDEA sits entirely behind the NetworkPolicies applied in T02 (netpol-mfa.yaml): | Source | Destination | Port | Purpose | |--------|-------------|------|---------| | Traefik (kube-system) | privacyIDEA (mfa) | 8080 | User-facing portal | | Keycloak (sso) | privacyIDEA (mfa) | 8080 | Provider API (triggerchallenge) | | privacyIDEA (mfa) | PostgreSQL (databases) | 5432 | Database | Outbound to anything other than PostgreSQL and kube-dns is denied. --- ## Post-deploy steps (after verify-t04.sh passes) ### Rate limiting adjustment The default rate limit (20 req/min, burst 5) is conservative. If Keycloak's triggerchallenge calls trigger false positives (HTTP 429), raise the average in `middleware.yaml` and reapply. Alternatively, the Keycloak-to-PI path is cluster-internal and not subject to the Ingress middleware. ### Admin WebUI IP restriction Update `middleware.yaml` `privacyidea-admin-allowlist.spec.ipWhiteList.sourceRange` to your actual VPN/office CIDRs and reapply: ```bash kubectl apply -f middleware.yaml ``` ### Self-service portal Enable the self-service portal policy in privacyIDEA: Config → Policies → New policy: - Scope: `user` - Action: `enrollTOTP`, `enrollHOTP`, or token types you want users to manage - URL for self-service: `https://pink-account.coulomb.social` --- ## Disaster recovery If the `privacyidea-data` PVC is lost: 1. Create a new PVC with `pvc.yaml`. 2. Restore enckey and audit keys from KeePassXC: ```bash # Copy pi.enc and private/public.pem from KeePassXC into a temporary pod kubectl run restore-helper --image=busybox -n mfa --restart=Never -- sleep 3600 kubectl cp ./pi.enc mfa/restore-helper:/etc/privacyidea/enckey kubectl cp ./private.pem mfa/restore-helper:/etc/privacyidea/private.pem kubectl cp ./public.pem mfa/restore-helper:/etc/privacyidea/public.pem kubectl delete pod -n mfa restore-helper ``` Or, if the K8s Secrets survived (created by `enckey-bootstrap.sh`): ```bash kubectl get secret privacyidea-enckey -n mfa -o jsonpath='{.data.enckey}' | base64 -d > pi.enc ``` 3. Restart the privacyIDEA deployment — it will run DB migrations and use the restored key material.