Link Temporal UI from ops console; propose SSO access WP-0025
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 5s
Build and Publish Container Image / build-and-push (push) Successful in 1m45s

Add nav/deep link to Temporal Web UI (configurable URL, default
127.0.0.1:8080 for port-forward). Document dual port-forward and draft
ACTIVITY-WP-0025 for Keycloak SSO ingress without port-forward.
This commit is contained in:
tegwick 2026-07-22 00:30:17 +02:00
parent 6c34e2c1f1
commit 7761acf86a
4 changed files with 283 additions and 3 deletions

View file

@ -0,0 +1,236 @@
---
id: ACTIVITY-WP-0025
type: workplan
title: "SSO access control for ops console and Temporal UI"
domain: infotech
repo: activity-core
status: proposed
owner: codex
topic_slug: activity-core
created: "2026-07-22"
updated: "2026-07-22"
---
# SSO access control for ops console and Temporal UI
## Origin
ACTIVITY-WP-0024 shipped the operator automation console (`/ops/ui`) with
ClusterIP + port-forward access and a shared operator token for mutations.
Operators also use Temporal Web UI (`actcore-temporal-ui:8080`) for workflow
debugging; the ops UI now deep-links to it.
**Pain:** port-forward + kubeconfig switching is fragile; shared token is not
identity-aware. **Goal:** proper browser access via SSO without port-forward.
Closes SCOPE **G10** (API external access) for the **ops and Temporal UIs**
(not necessarily for unauthenticated programmatic API from the public internet).
## Goal
Expose **activity-core ops UI** and **Temporal Web UI** behind authenticated
ingress with **Keycloak / key-cape OIDC SSO**, so an authorized operator can:
1. Open HTTPS URLs in a browser (no `kubectl port-forward`)
2. Authenticate with org SSO (MFA per IAM profile where required)
3. Use ops console mutations under **their identity** (not only a shared token)
4. Open Temporal UI under the same access policy
## Non-goals
- Public anonymous access to `/ops` or Temporal UI
- Replacing Temporals own RBAC model with fine-grained multi-tenant Temporal
namespaces for all of Coulomb (single activity-core Temporal is fine for MVP)
- Full API productization for third-party clients (focus is **human UI access**)
- Moving Temporal server hosting ownership out of the activity-core namespace
(unless railiance-platform already owns that path)
## Architecture sketch (proposed)
```
Browser
│ HTTPS
Ingress (TLS) ──► oauth2-proxy / key-cape edge ── OIDC ──► Keycloak (key-cape)
│ │
│ allowlisted groups │ identity headers / session
▼ ▼
actcore-api (/ops, /ops/ui) actcore-temporal-ui
mutations: require SSO subject
(retire shared token as primary; keep break-glass optional)
```
Credential routing: **key-cape** owns OIDC/login (`warden route show key-cape-oidc-login`);
**flex-auth** for authorization decisions if used; **do not** invent secrets in
git or State Hub.
## Design decisions to lock in T01
| Decision | Options | Lean |
| --- | --- | --- |
| Hostnames | e.g. `activity.ops.…` / `temporal.ops.…` vs path-based single host | Prefer **two hostnames** or one host with path prefixes |
| Auth edge | oauth2-proxy vs nginx auth_request vs key-cape native | Prefer **oauth2-proxy** or fleet-standard edge if one already exists |
| Mutation identity | SSO email/sub as principal; drop shared token for normal use | Shared token = break-glass only |
| Temporal UI auth | Same edge in front of `actcore-temporal-ui` | Yes for MVP |
| Who may access | Explicit Keycloak group (e.g. `activity-core-operators`) | Least privilege |
## Tasks
## Task: Access policy brief and hostnames
```task
id: ACTIVITY-WP-0025-T01
status: todo
priority: high
```
1. Document intended audience (founders / operators), MFA expectation, and
allowed IdP groups.
2. Propose hostnames + TLS cert path (cluster issuer / railiance DNS).
3. Align with railiance-platform / key-cape conventions; open coordination
messages if host or client registration is external.
4. Update SCOPE G10 with the chosen posture (UI SSO vs full public API).
**Done when:** short design note in `docs/` or this workplan is approved;
hostnames and IdP group named.
## Task: Inventory fleet SSO ingress patterns
```task
id: ACTIVITY-WP-0025-T02
status: todo
priority: high
```
1. Find an existing oauth2-proxy / OIDC ingress pattern on railiance01 or
coulombcore (railiance-platform, state-hub dashboard, etc.).
2. Prefer **reuse** over inventing a new edge stack.
3. Record the pattern (manifest paths, secrets ownership, callback URLs).
**Done when:** “pattern to copy” is written with repo/file pointers.
## Task: Ingress + TLS for ops API (read path)
```task
id: ACTIVITY-WP-0025-T03
status: todo
priority: high
```
1. Add Ingress (or Gateway) for `actcore-api` serving at least `/ops` and
`/ops/ui` (and health for probes as needed).
2. Terminate TLS with the platform cert strategy.
3. Gate with SSO edge; unauthenticated browser hits redirect to login.
4. Keep ClusterIP service; no LoadBalancer unless required.
**Done when:** authorized browser reaches `/ops/ui` over HTTPS without
port-forward; unauthorized gets login or 403.
## Task: Ingress + SSO for Temporal Web UI
```task
id: ACTIVITY-WP-0025-T04
status: todo
priority: high
```
1. Expose `actcore-temporal-ui` behind the same auth edge (hostname or path).
2. Ensure Temporal UI env (`TEMPORAL_ADDRESS`, public URL / CORS if required)
works behind the external hostname.
3. Ops console `ACTIVITY_CORE_TEMPORAL_UI_URL` points at the **public SSO URL**.
**Done when:** Temporal UI opens from the ops nav link without a second
port-forward, under SSO.
## Task: Map SSO identity into ops mutations
```task
id: ACTIVITY-WP-0025-T05
status: todo
priority: high
```
1. Accept trusted identity from the auth edge (e.g. `X-Forwarded-User` /
`X-Auth-Request-Email` — exact headers depend on T02 pattern).
2. Prefer SSO principal over shared `ACTIVITY_CORE_OPERATOR_TOKEN` for
mutations when present.
3. Audit log records SSO subject (never tokens).
4. Optional break-glass: keep operator token for emergency; document custody.
**Done when:** Run now / pause audit shows human identity from SSO; shared
token not required for normal operator use.
## Task: Keycloak client and group membership
```task
id: ACTIVITY-WP-0025-T06
status: todo
priority: medium
```
1. Register OIDC client(s) via key-cape / netkingdom process (not ad-hoc).
2. Create/bind operator group; grant only named operators.
3. Document how to add/remove operators.
**Done when:** login works for a test operator and fails for an unprivileged
account.
## Task: Docs and runbook cutover
```task
id: ACTIVITY-WP-0025-T07
status: todo
priority: medium
```
1. Runbook: primary access = SSO URLs; port-forward = break-glass only.
2. k8s README: Ingress manifests and secret ownership.
3. Credential routing notes: key-cape for login; no secrets in git.
**Done when:** an operator can follow runbook without kubectl for daily use.
## Task: Verify and harden
```task
id: ACTIVITY-WP-0025-T08
status: todo
priority: medium
```
1. Confirm ClusterIP APIs without Ingress path remain not publicly reachable.
2. Confirm mutations without SSO (and without break-glass token) fail closed.
3. Smoke: inventory, status, Run now dry path, Temporal UI namespaces list.
4. Optional: network policies locking ingress → services.
**Done when:** checklist signed off in workplan closeout notes.
## Success criteria
- [ ] HTTPS SSO URLs for ops console and Temporal UI (no port-forward required)
- [ ] Only authorized IdP group can access
- [ ] Ops mutations attribute to SSO identity; audits show principal
- [ ] Ops nav Temporal link points at SSO Temporal URL
- [ ] Runbook documents SSO primary + break-glass port-forward
- [ ] SCOPE G10 updated to reflect UI SSO posture
## Dependencies / coordination
- **key-cape** / Keycloak: OIDC clients, groups (`warden route show key-cape-oidc-login`)
- **railiance-platform**: DNS, cert-manager, ingress controller norms
- **ACTIVITY-WP-0024**: ops console exists; this WP unlocks production access
## Implementation order
1. T01T02 design + pattern reuse
2. T06 IdP client (can parallel with T03)
3. T03 ops Ingress + SSO
4. T04 Temporal UI Ingress + env URL
5. T05 mutation identity
6. T07T08 docs and verify
## References
- `docs/runbook.md` — operator console + dual port-forward (pre-SSO)
- `src/activity_core/ops_api.py``ACTIVITY_CORE_TEMPORAL_UI_URL`
- `k8s/railiance/``actcore-api`, `actcore-temporal-ui` ClusterIP services
- ACTIVITY-WP-0024 operator automation console