Files
net-kingdom/sso-mfa/k8s/keycape/README.md
Bernd Worsch 0754dc32e6 feat(sso-mfa): T05 SSO stack pivot — Keycloak → Authelia + LLDAP + KeyCape (NK-WP-0001-T05)
Replaces the Keycloak+privacyIDEA SSO tier with the lightweight stack built
during KEY-WP-0001: Authelia (password frontend), LLDAP (directory), and
KeyCape (OIDC orchestration). privacyIDEA is retained as the MFA engine.

Stack:
  kc.coulomb.social   — KeyCape OIDC server (stateless, custom Go)
  auth.coulomb.social — Authelia login portal (password auth → Authelia OIDC → KeyCape)
  lldap.coulomb.social — LLDAP admin UI (IP-restricted)
  pink.coulomb.social — privacyIDEA MFA engine (unchanged)

Changes:
- Remove sso-mfa/k8s/keycloak/ (7 files)
- Add sso-mfa/k8s/lldap/ (pvc, deployment, middleware, ingress, create-secrets, README)
- Add sso-mfa/k8s/authelia/ (pvc, configmap, deployment, ingress, create-secrets, README)
- Add sso-mfa/k8s/keycape/ (deployment, middleware, ingress, create-secrets, create-pi-token, README)
- Update network-policies/netpol-sso.yaml for new component topology
- Update verify-t05.sh: checks LLDAP + Authelia + KeyCape (23 checks)
- Update CONFIG.md: fix CP-NK-004 (KeyCape), add CP-NK-005 (Authelia), CP-NK-006 (LLDAP)
- Update bootstrap/gen-secrets.sh: add LLDAP/Authelia/KeyCape sections, remove Keycloak
- Update k8s/README.md: network policy table reflects new traffic paths
- Add sso-mfa/WORKPLAN.md: resumable task checklist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:31:51 +00:00

146 lines
4.7 KiB
Markdown

# T05c — KeyCape (OIDC Orchestration Layer)
KeyCape is the stateless OIDC server that ties the stack together. It orchestrates
the full authentication flow:
1. User visits a registered application
2. Application redirects to KeyCape (`kc.coulomb.social`) for login
3. KeyCape redirects the browser to Authelia (`auth.coulomb.social`) for password auth
4. Authelia validates the password against LLDAP and returns an authorization code
5. KeyCape exchanges the code for user identity, then calls privacyIDEA for MFA
6. On success, KeyCape issues a signed OIDC token to the application
KeyCape is stateless — all state lives in Authelia (sessions), LLDAP (users), and
privacyIDEA (MFA tokens). No PVC is required.
## Prerequisites
- T04 complete (privacyIDEA is Running and bootstrapped — admin account + enckey done)
- T05a complete (LLDAP is Running)
- T05b complete (Authelia is Running)
- KeyCape container image built and available (see "Building the image" below)
- `bootstrap/gen-secrets.sh` run
- `kubectl` configured with cluster access
## Building the image
KeyCape has no published image. Build it from the source repository and make it
available to K3s before applying `deployment.yaml`.
### Option A — Local import into K3s (dev/single-node)
```bash
cd ~/key-cape
docker build -t keycape:v0.1 .
# Import directly into the K3s containerd runtime (no registry needed)
docker save keycape:v0.1 | sudo k3s ctr images import -
# After import, set imagePullPolicy: Never in deployment.yaml
# (the image is now in the K3s local store, not a registry)
```
### Option B — Private registry (production)
```bash
cd ~/key-cape
docker build -t <registry>/keycape:v0.1 .
docker push <registry>/keycape:v0.1
# Update the image field in deployment.yaml:
# image: <registry>/keycape:v0.1
# imagePullPolicy: IfNotPresent (default) is correct for registry images.
```
After building, update `deployment.yaml` line:
```yaml
image: keycape:v0.1 # replace with your actual tag
```
## Apply order
```bash
# 1. Create Secrets (config.yaml + key.pem)
# Run this AFTER T04 bootstrap if you want the privacyIDEA token included.
# If T04 is not yet done, run it now and re-run after create-pi-token.sh.
cd sso-mfa/k8s/keycape
chmod +x create-secrets.sh create-pi-token.sh
./create-secrets.sh
# 2. Apply manifests
kubectl apply -f deployment.yaml
kubectl apply -f middleware.yaml
kubectl apply -f ingress.yaml
# 3. Wait for pod to be ready
kubectl rollout status deployment/keycape -n sso --timeout=60s
```
## Post-deploy: inject privacyIDEA admin token
If T04 was not complete when you ran `create-secrets.sh`, the privacyIDEA admin
token is a placeholder. After T04 bootstrap is done:
```bash
# 1. Fetch the token from privacyIDEA and store it
chmod +x create-pi-token.sh
./create-pi-token.sh
# 2. Re-run create-secrets.sh to update keycape-config with the real token
./create-secrets.sh
# 3. Restart KeyCape to pick up the new Secret
kubectl rollout restart deployment/keycape -n sso
```
## OIDC client registration
Downstream applications are registered in the `clients:` block in
`keycape/create-secrets.sh`. After editing:
```bash
./create-secrets.sh # regenerates keycape-config Secret
kubectl rollout restart deployment/keycape -n sso
```
Example entry (public client, PKCE, for a SPA):
```yaml
clients:
- clientId: "my-app"
displayName: "My Application"
redirectUris:
- "https://my-app.coulomb.social/callback"
allowedScopes: ["openid", "profile", "email", "groups"]
grantTypes: ["authorization_code"]
clientType: "public"
```
## Secrets managed
| Secret name | Keys | Purpose |
|-------------|------|---------|
| `keycape-config` | `config.yaml` | Full KeyCape configuration (LLDAP URL + creds, Authelia URL + client secret, privacyIDEA URL + admin token, OIDC clients) |
| | `key.pem` | RSA-2048 private key for signing OIDC tokens issued to downstream applications |
| `keycape-pi-token` | `pi_admin_token` | privacyIDEA admin JWT — created by `create-pi-token.sh`, referenced in `config.yaml` |
Store `key.pem` in KeePassXC as a binary attachment. If it is lost, all active
sessions become invalid (tokens cannot be verified) and all applications must
re-authenticate.
## Verify
```bash
# Pod status
kubectl get pod -n sso -l app.kubernetes.io/name=keycape
# Health check
kubectl run -n sso --rm -it kc-test --image=busybox --restart=Never \
-- wget -qO- http://keycape.sso.svc.cluster.local:8080/healthz
# OIDC discovery (public endpoint)
curl -s https://kc.coulomb.social/.well-known/openid-configuration | jq .
# Check issuer matches CP-NK-004
curl -s https://kc.coulomb.social/.well-known/openid-configuration \
| jq -r .issuer # should be: https://kc.coulomb.social
```