Files
net-kingdom/sso-mfa/k8s/keycloak/README.md
Bernd Worsch d0ed7d9cd6 feat(sso-mfa): T05 Keycloak manifests (NK-WP-0001-T05)
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>
2026-03-19 02:00:51 +00:00

204 lines
6.3 KiB
Markdown
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.
# 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: `sso` namespace and NetworkPolicies applied, cert-manager running.
- T03 complete: PostgreSQL cluster `net-kingdom-pg` in `databases` namespace is Ready.
- T04 complete: privacyIDEA is Running; `bootstrap-admin.sh` has been run so the
`privacyidea-trigger-admin` Secret exists in the `mfa` namespace.
- T01 Phase 0a complete: `gen-secrets.sh` run, 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:
1. Go to https://github.com/privacyIDEA/keycloak-provider/releases
2. Download the JAR for a release compatible with your Keycloak image version.
3. Edit `deployment.yaml`: find `PROVIDER_JAR_URL` and replace `EDIT_BEFORE_APPLY`
with the real URL.
4. 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
```bash
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`:
```bash
# 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):
```bash
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:
```bash
kubectl logs -n sso -l app.kubernetes.io/name=keycloak -c install-privacyidea-provider
```
Common causes of `Init` failure:
- `PROVIDER_JAR_URL` still set to `EDIT_BEFORE_APPLY` → edit and reapply
- No egress internet access → use custom image (see below)
If the pod is in `CrashLoopBackOff`, check main container logs:
```bash
kubectl logs -n sso -l app.kubernetes.io/name=keycloak --previous
```
Common causes of Keycloak crash:
- `keycloak-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
- `KC_DB_URL` format incorrect → must be `jdbc:postgresql://...` (not SQLAlchemy format)
---
### Step 3 — Bootstrap realm
After the pod is Running and Ready:
```bash
./bootstrap-realm.sh
```
This:
1. Authenticates to the Keycloak admin REST API inside the pod.
2. Hardens the master realm (SSL required, brute-force protection, token lifetimes).
3. Creates the `net-kingdom` application realm with equivalent hardening.
**Immediately after bootstrap completes:**
1. Log in to `https://kc.coulomb.social/admin` as `admin`.
2. Create a permanent admin account (with MFA — configure MFA flow in T06 first).
3. Disable or delete the bootstrap `admin` account once the permanent admin is enrolled.
---
### Step 4 — Verify
```bash
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:
```dockerfile
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:
1. Update `deployment.yaml`: change the `keycloak` container `image` to your custom image.
2. Change `args: ["start"]` to `args: ["start", "--optimized"]` for faster startup.
3. Remove the `install-privacyidea-provider` init container entirely.
4. The `providers` emptyDir 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:
1. In Keycloak admin console: Users → admin → Disable account (or delete it).
2. Rotate `KC_BOOTSTRAP_ADMIN_PASSWORD` in KeePassXC and re-run `create-secrets.sh`.
3. 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:
```bash
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:
```markdown
| 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):
1. Create a new PVC with `pvc.yaml`.
2. Restart the deployment — Keycloak will rebuild from PostgreSQL on first start.
Allow 35 minutes for the rebuild + DB reconnect.
3. 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.