Deploys Keycloak (SSO core) in the sso namespace.
Files:
sso-mfa/k8s/keycloak/pvc.yaml — keycloak-data PVC (build cache)
sso-mfa/k8s/keycloak/middleware.yaml — rate-limit, admin-allowlist, HSTS
sso-mfa/k8s/keycloak/deployment.yaml — Deployment + Service; init container
downloads privacyIDEA provider JAR
sso-mfa/k8s/keycloak/ingress.yaml — Ingress for kc.coulomb.social (CP-NK-004)
sso-mfa/k8s/keycloak/create-secrets.sh — keycloak-config Secret
sso-mfa/k8s/keycloak/bootstrap-realm.sh— hardens master realm, creates net-kingdom realm
sso-mfa/k8s/keycloak/README.md — apply order, custom image guide, DR
sso-mfa/k8s/verify-t05.sh — T05 done-criteria verification script
Config points added: CP-NK-004 (kc.coulomb.social), CP-NK-005 (provider JAR URL).
CP-NK-005 must be set before applying deployment.yaml.
Pending: apply to live cluster, set CP-NK-005, run bootstrap-realm.sh, verify-t05.sh.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
T05 — Phase 4: Deploy Keycloak
Phase 4 of NK-WP-0001: deploys the SSO core (Keycloak) in the sso namespace.
Hostname (config point CP-NK-004):
kc.coulomb.social— OIDC/SAML SSO portal, admin console
Prerequisites:
- T02 complete:
ssonamespace and NetworkPolicies applied, cert-manager running. - T03 complete: PostgreSQL cluster
net-kingdom-pgindatabasesnamespace is Ready. - T04 complete: privacyIDEA is Running;
bootstrap-admin.shhas been run so theprivacyidea-trigger-adminSecret exists in themfanamespace. - T01 Phase 0a complete:
gen-secrets.shrun, all secrets in KeePassXC.
Before you apply: two required edits
Edit 1 — Provider JAR URL (CP-NK-005, required)
The init container in deployment.yaml downloads the privacyIDEA Keycloak Provider
JAR. You must set the URL before applying:
- Go to https://github.com/privacyIDEA/keycloak-provider/releases
- Download the JAR for a release compatible with your Keycloak image version.
- Edit
deployment.yaml: findPROVIDER_JAR_URLand replaceEDIT_BEFORE_APPLYwith the real URL. - Add the URL as CP-NK-005 in
CONFIG.md(see bottom of this README).
If your cluster has no egress internet access, see Custom image below.
Edit 2 — Admin console IP allowlist (optional but recommended)
Edit middleware.yaml: update keycloak-admin-allowlist.spec.ipAllowList.sourceRange
to your actual VPN/office CIDRs.
Apply order
Step 1 — Create secrets
cd sso-mfa/k8s/keycloak
chmod +x create-secrets.sh bootstrap-realm.sh
./create-secrets.sh
Creates keycloak-config in the sso namespace (KC_DB_URL, KC_DB_PASSWORD,
KC_BOOTSTRAP_ADMIN_PASSWORD).
Step 2 — Set provider JAR URL and apply manifests
After editing PROVIDER_JAR_URL in deployment.yaml:
# From sso-mfa/k8s/keycloak/
kubectl apply -f pvc.yaml
kubectl apply -f middleware.yaml
kubectl apply -f deployment.yaml
kubectl apply -f ingress.yaml
Wait for the pod to reach Running+Ready (DB migrations + provider build on first boot — allow up to 5 minutes):
kubectl get pods -n sso -w
# Expected: keycloak-<hash> 1/1 Running
If the pod is stuck in Init, check the init container logs first:
kubectl logs -n sso -l app.kubernetes.io/name=keycloak -c install-privacyidea-provider
Common causes of Init failure:
PROVIDER_JAR_URLstill set toEDIT_BEFORE_APPLY→ edit and reapply- No egress internet access → use custom image (see below)
If the pod is in CrashLoopBackOff, check main container logs:
kubectl logs -n sso -l app.kubernetes.io/name=keycloak --previous
Common causes of Keycloak crash:
keycloak-configSecret missing → runcreate-secrets.sh- PostgreSQL not reachable → verify T03, check NetworkPolicies
- Wrong DB password → re-run
create-secrets.shwith corrected secrets KC_DB_URLformat incorrect → must bejdbc:postgresql://...(not SQLAlchemy format)
Step 3 — Bootstrap realm
After the pod is Running and Ready:
./bootstrap-realm.sh
This:
- Authenticates to the Keycloak admin REST API inside the pod.
- Hardens the master realm (SSL required, brute-force protection, token lifetimes).
- Creates the
net-kingdomapplication realm with equivalent hardening.
Immediately after bootstrap completes:
- Log in to
https://kc.coulomb.social/adminasadmin. - Create a permanent admin account (with MFA — configure MFA flow in T06 first).
- Disable or delete the bootstrap
adminaccount once the permanent admin is enrolled.
Step 4 — Verify
cd sso-mfa/k8s
chmod +x verify-t05.sh
./verify-t05.sh
Custom image (recommended for production)
The init-container approach downloads the provider JAR from the internet on every pod restart. For production or air-gapped clusters, build a custom image:
FROM quay.io/keycloak/keycloak:26.0
# Add the privacyIDEA provider JAR (download from GitHub releases first)
COPY keycloak-provider-VERSION.jar /opt/keycloak/providers/
# Build an optimized image — this bakes in the provider at build time
RUN /opt/keycloak/bin/kc.sh build
Push to your registry, then:
- Update
deployment.yaml: change thekeycloakcontainerimageto your custom image. - Change
args: ["start"]toargs: ["start", "--optimized"]for faster startup. - Remove the
install-privacyidea-providerinit container entirely. - The
providersemptyDir volume and its mount can also be removed.
NetworkPolicy design
Keycloak sits behind the NetworkPolicies applied in T02 (netpol-sso.yaml):
| Source | Destination | Port | Purpose |
|---|---|---|---|
| Traefik (kube-system) | Keycloak (sso) | 8080 | OIDC/SAML login pages |
| Keycloak (sso) | privacyIDEA (mfa) | 8080 | MFA challenge API (T06) |
| Keycloak (sso) | PostgreSQL (databases) | 5432 | Database |
Outbound to anything other than privacyIDEA, PostgreSQL, and kube-dns is denied.
Post-deploy steps (after verify-t05.sh passes)
Rotate the bootstrap admin password
The KC_BOOTSTRAP_ADMIN_PASSWORD in keycloak-config was the initial credential.
After creating a permanent admin:
- In Keycloak admin console: Users → admin → Disable account (or delete it).
- Rotate
KC_BOOTSTRAP_ADMIN_PASSWORDin KeePassXC and re-runcreate-secrets.sh. - Restart the Keycloak deployment:
kubectl rollout restart deployment/keycloak -n sso
Admin console IP restriction
Update middleware.yaml keycloak-admin-allowlist.spec.ipAllowList.sourceRange
to your actual VPN/office CIDRs:
kubectl apply -f middleware.yaml
Adding CP-NK-005 to CONFIG.md
After setting the provider JAR URL, add it to CONFIG.md as CP-NK-005:
| CP-NK-005 | privacyIDEA provider JAR URL | <the URL you used> | `sso-mfa/k8s/keycloak/deployment.yaml` |
Disaster recovery
If the keycloak-data PVC is lost (build cache only — all state is in PostgreSQL):
- Create a new PVC with
pvc.yaml. - Restart the deployment — Keycloak will rebuild from PostgreSQL on first start. Allow 3–5 minutes for the rebuild + DB reconnect.
- Realm config, clients, and users are preserved in the database.
If the PostgreSQL keycloak_db database is lost, restore from the CNPG backup
(T03 procedure) before restarting Keycloak.