Add NK-WP-0021 for activity-core ops SSO least-privilege.

Promote NK-IN-0001/0002 after scope/intent review into a single ready
workplan (LLDAP operator group, membership runbook, Authelia domain rules,
live verification). Hub workstream and tasks registered via fix-consistency.
This commit is contained in:
tegwick 2026-07-22 15:21:22 +02:00
parent 101f91cbda
commit 3580e43e5f
3 changed files with 293 additions and 16 deletions

View file

@ -12,6 +12,7 @@
| workplan | NET-WP-0020 | finished | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
| workplan | NK-WP-0009 | backlog | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
| workplan | NK-WP-0011 | backlog | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
| workplan | NK-WP-0021 | ready | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
| task | ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
| task | ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
| task | NET-WP-0020-T01 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
@ -33,5 +34,10 @@
| task | NK-WP-0011-T6 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
| task | NK-WP-0011-T7 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
| task | NK-WP-0011-T8 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
| intake | NK-IN-0001 | open | blue | docs/intakes/activity-core-ops-sso-operators.md |
| intake | NK-IN-0002 | open | blue | docs/intakes/activity-core-ops-sso-operators.md |
| task | NK-WP-0021-T01 | todo | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
| task | NK-WP-0021-T02 | todo | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
| task | NK-WP-0021-T03 | todo | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
| task | NK-WP-0021-T04 | todo | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
| task | NK-WP-0021-T05 | todo | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
| intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
| intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |

View file

@ -8,16 +8,12 @@ request least-privilege group gating on the public hosts.
Source design: `activity-core/docs/ops-sso-access.md`
Hosts: `activity.coulomb.social`, `temporal.coulomb.social`
Promote with (after route + owner review):
```bash
statehub promote-intake <state_hub_intake_id> \
--to workplan \
--repo-path ~/net-kingdom \
--repo-slug net-kingdom \
--domain communication
# or --to task --workplan-file workplans/<existing>.md
```
**Promotion (2026-07-22):** both intakes accepted after scope/intent review and
promoted into **`NK-WP-0021`**
(`workplans/NK-WP-0021-activity-core-ops-sso-operators.md`). Single workplan
because LLDAP group and Authelia domain rules share one deploy surface and a
hard dependency. See that workplan for refined tasks, non-goals, and the
verification matrix.
---
@ -28,13 +24,15 @@ id: NK-IN-0001
kind: intake
title: "Create LLDAP group activity-core-operators and membership runbook"
lane: blue
status: routed
status: closed
outcome: promoted
promoted_to: NK-WP-0021
priority: high
owner: net-kingdom
repo: net-kingdom
origin: "ACTIVITY-WP-0025-T06"
origin_ref: "activity-core/workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md"
routed_note: "Implement in net-kingdom sso-mfa. Origin ACTIVITY-WP-0025-T06 (activity-core)."
routed_note: "Promoted 2026-07-22 into NK-WP-0021 (T01T02). Implement in net-kingdom sso-mfa."
description: |
activity-core shipped Authelia SSO for the ops console and Temporal UI
(ACTIVITY-WP-0025). MVP accepts any authenticated Authelia user.
@ -54,6 +52,7 @@ description: |
notes: |
Related Authelia domain rules are NK-IN-0002 (depends on this group existing).
Requester residual task: ACTIVITY-WP-0025-T06 (status wait).
Promoted to NK-WP-0021 (combined workplan with NK-IN-0002).
state_hub_intake_id: "019f88ff-c5f1-7433-9d13-4ffe37dfef00"
```
@ -66,13 +65,15 @@ id: NK-IN-0002
kind: intake
title: "Authelia access_control for activity + temporal.coulomb.social"
lane: blue
status: routed
status: closed
outcome: promoted
promoted_to: NK-WP-0021
priority: high
owner: net-kingdom
repo: net-kingdom
origin: "ACTIVITY-WP-0025-T06"
origin_ref: "activity-core/workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md"
routed_note: "Depends on NK-IN-0001 group. Implement in Authelia ConfigMap. Origin ACTIVITY-WP-0025-T06."
routed_note: "Promoted 2026-07-22 into NK-WP-0021 (T03T04). Depends on NK-IN-0001 group."
description: |
Restrict browser access to activity-core public SSO hosts to members of
LLDAP group `activity-core-operators` (see NK-IN-0001).
@ -104,5 +105,6 @@ notes: |
Depends on NK-IN-0001 (group must exist). activity-core Ingress + middleware
already applied (k8s/railiance/3032). No activity-core code change required
for group enforcement — headers already carry Remote-Groups.
Promoted to NK-WP-0021 (combined workplan with NK-IN-0001).
state_hub_intake_id: "019f88ff-cc9e-76b4-8ce3-eb354aabefe7"
```

View file

@ -0,0 +1,269 @@
---
id: NK-WP-0021
type: workplan
title: "Activity-core ops/Temporal UI least-privilege SSO"
domain: infotech
repo: net-kingdom
status: ready
owner: codex
topic_slug: netkingdom
priority: high
created: "2026-07-22"
updated: "2026-07-22"
origin: "intake:019f88ff-c5f1-7433-9d13-4ffe37dfef00+019f88ff-cc9e-76b4-8ce3-eb354aabefe7"
origin_ref: "ACTIVITY-WP-0025-T06"
state_hub_workstream_id: "3e40e29a-a13a-47b6-9575-4fdc7024af6a"
---
# NK-WP-0021 — Activity-core ops/Temporal UI least-privilege SSO
## Goal
Close the residual least-privilege gap for public activity-core operator UIs
by owning the **directory group** and **Authelia access rules** in this repo
(SSO/MFA stack). Browser access to `activity.coulomb.social` and
`temporal.coulomb.social` should require membership in
`activity-core-operators`, not merely “any authenticated Authelia user”.
## Origin and review
| Intake | Hub UUID | Title | Decision |
| --- | --- | --- | --- |
| NK-IN-0001 | `019f88ff-c5f1-7433-9d13-4ffe37dfef00` | LLDAP group + membership runbook | **Accept** → T01T02 |
| NK-IN-0002 | `019f88ff-cc9e-76b4-8ce3-eb354aabefe7` | Authelia domain rules | **Accept** → T03T04 |
Cross-repo handoff from activity-core `ACTIVITY-WP-0025-T06` (residual wait).
Source design: `activity-core/docs/ops-sso-access.md`. File intakes:
`docs/intakes/activity-core-ops-sso-operators.md`.
### Scope / intent fit
| Lens | Assessment |
| --- | --- |
| **SCOPE in** | SSO/MFA platform (Authelia + LLDAP), identity as control plane, least-privilege access for platform operators |
| **SCOPE out** | activity-core Ingress/middleware (already live), KeyCape OIDC clients, Temporal/API application code, railiance runtime |
| **INTENT** | Principle 2 (identity is the control plane) and 6 (minimize exposure / least privilege from the start). Progressive capability: tighten MVP “any Authelia user” to named operator group without changing auth edge pattern |
| **Repo boundary** | net-kingdom owns LLDAP groups + Authelia `access_control`; activity-core owns ForwardAuth middleware, break-glass token, and app authorization |
### Refinements vs raw intakes
1. **Single workplan** — both intakes share one deploy surface (LLDAP → Authelia) and a hard dependency (group must exist before domain rules). One plan avoids split status and double-apply risk.
2. **No KeyCape / Keycloak OIDC clients** — explicit non-goal. Path is Traefik ForwardAuth → Authelia `/api/verify` only (already shipped in activity-core).
3. **MFA posture unchanged** — keep Authelia `one_factor`; MFA remains KeyCape + privacyIDEA. Do not set Authelia `two_factor` for these hosts.
4. **App-scoped operator group pattern**`activity-core-operators` extends the existing `net-kingdom-users` / `net-kingdom-admins` seed style for a *consumer app*, not platform-wide admin. Document as the template for future app operator groups.
5. **Membership is operator policy** — scripts create the empty group and document add/remove; named founder assignment is a live operator step (not committed identity).
6. **Verification matrix** — unauthenticated → login; authenticated non-member → deny; member → pass; break-glass remains independent of Authelia groups (activity-core runbook).
7. **Default policy stays open for other hosts** — only the two public activity-core hosts get restrictive domain rules; do not change global `default_policy: one_factor` for the rest of the fleet without a separate decision.
## Context (current state)
- Authelia SSO edge for both hosts is **live** (activity-core manifests 3032).
- Authelia `access_control` today: `default_policy: one_factor`, **no** domain rules
(`sso-mfa/k8s/authelia/configmap.yaml`).
- LLDAP bootstrap seeds only `net-kingdom-users` and `net-kingdom-admins`
(`sso-mfa/k8s/lldap/bootstrap-users.sh`).
- Any authenticated Authelia user can currently open both public UIs.
## In scope
- Create LLDAP group `activity-core-operators` (bootstrap script + README).
- Document how operators add/remove humans (WebUI + scripted GraphQL path).
- Authelia domain rules for `activity.coulomb.social` and
`temporal.coulomb.social` requiring that group.
- Apply/reload Authelia; verify member / non-member / unauthenticated paths.
- Close intakes as promoted; notify activity-core so T06 can finish.
## Out of scope
- New OIDC clients in KeyCape or Keycloak for these hosts.
- Changing activity-core Ingress, middleware, or ops API authorization.
- Platform-root / break-glass LLDAP accounts.
- Enterprise federation / SAML (NK-WP-0011) or tutorials backlog (NK-WP-0009).
- Automatically granting named users without operator confirmation.
## Acceptance (workplan-level)
- [ ] Group `activity-core-operators` exists in LLDAP and is re-seeded by bootstrap.
- [ ] Membership procedure is documented under `sso-mfa` docs (and README pointers).
- [ ] Authelia denies non-members and allows members on both hosts after login.
- [ ] Unauthenticated users still hit the Authelia login portal (not open backends).
- [ ] activity-core break-glass (port-forward + operator token) remains valid and
independent of Authelia group membership.
- [ ] Intakes NK-IN-0001 / NK-IN-0002 closed as promoted to this workplan.
---
## Tasks
### T01 — LLDAP group `activity-core-operators` in bootstrap
```task
id: NK-WP-0021-T01
status: todo
priority: high
state_hub_task_id: "50d59099-d3df-4087-93ef-9a4e537807b8"
```
Extend LLDAP group seeding so the operator group is created the same way as
platform groups:
- Update `sso-mfa/k8s/lldap/bootstrap-users.sh` to create
`activity-core-operators` (idempotent `create_group`).
- Update `sso-mfa/k8s/lldap/README.md` and post-deploy hints in
`create-secrets.sh` to list the third group and its purpose
(activity-core ops console + Temporal UI only — not platform admin).
- Optional small helper or flag on `create-user.sh` only if it stays simple
(e.g. documented GraphQL one-liner is enough for v1).
**Done when:** re-running bootstrap is safe/idempotent and the group exists
(or will exist on next bootstrap) with clear docs.
**Promotes:** NK-IN-0001 (group half).
---
### T02 — Operator membership runbook
```task
id: NK-WP-0021-T02
status: todo
priority: high
state_hub_task_id: "e8d6dae5-257f-4862-98de-ab4cef6cc722"
```
Document least-privilege membership for founders/operators only:
- WebUI path: `https://lldap.coulomb.social` → add/remove group members.
- Scripted path: GraphQL `addUserToGroup` / `removeUserFromGroup` using the
same auth pattern as `bootstrap-users.sh` / `break-glass.sh` (no secrets in
git or workplan).
- Explicit: do **not** auto-add all `net-kingdom-admins`; membership is a
conscious grant for activity-core ops.
- Point activity-core operators at this runbook from a short note in
`docs/intakes/activity-core-ops-sso-operators.md` (or a thin
`sso-mfa/k8s/lldap/OPERATOR-GROUPS.md` if the README grows too large).
**Done when:** an operator can add and remove a test user without reading
activity-core internals.
**Promotes:** NK-IN-0001 (runbook half).
---
### T03 — Authelia domain rules for activity + temporal hosts
```task
id: NK-WP-0021-T03
status: todo
priority: high
state_hub_task_id: "71bfd7ea-1637-4c1a-ac3b-dc8dca38b82f"
```
Implement access control in the live Authelia ConfigMap:
- File: `sso-mfa/k8s/authelia/configmap.yaml`
- Keep `default_policy: one_factor` for the rest of the fleet.
- Add `rules` (or equivalent for the deployed Authelia version) for:
- `activity.coulomb.social`
- `temporal.coulomb.social`
- Require subject group `activity-core-operators` with policy `one_factor`
(Authelia subject syntax: `subject: "group:activity-core-operators"`
confirm against the running Authelia major version before apply).
- Comment in-file: MFA remains KeyCape/privacyIDEA; do not use Authelia
`two_factor` here.
- Update `sso-mfa/k8s/authelia/README.md` with the new domain-rule pattern so
the next app can copy it.
**Depends on:** T01 (group name must resolve from LLDAP via Authelias LDAP
backend). Rules may be committed before the group exists, but live apply of
enforcement should wait until the group is present.
**Done when:** config is reviewed and ready to apply; no accidental lockdown
of unrelated hosts (auth portal, KeyCape, LLDAP admin, etc.).
**Promotes:** NK-IN-0002 (config half).
---
### T04 — Live apply and verification matrix
```task
id: NK-WP-0021-T04
status: todo
priority: high
state_hub_task_id: "f74a2a59-f126-45e3-832a-5574bd0feb6f"
```
Roll out and prove least privilege on the live railiance01 SSO stack:
1. Ensure group exists (run bootstrap if needed) and assign **one** known
operator (operator-confirmed username).
2. Apply Authelia ConfigMap; restart/reload Authelia in namespace `sso`.
3. Verification matrix (record non-secret results in progress notes):
| Case | Expected |
| --- | --- |
| Unauthenticated → both hosts | Redirect to Authelia login |
| Authenticated **non-member** | Denied (403 / Authelia unauthorized) |
| Authenticated **member** | Pass-through to ops UI and Temporal UI |
| Break-glass port-forward + operator token | Still works per activity-core runbook |
4. If verification fails, roll back ConfigMap to previous `access_control`
and re-open the blocking task — do not leave partial rules that lock out
all operators without a recovery path.
**Done when:** matrix passes for both hosts; recovery path still documented.
**Promotes:** NK-IN-0002 (verify half).
---
### T05 — Close loop with intakes and activity-core
```task
id: NK-WP-0021-T05
status: todo
priority: medium
state_hub_task_id: "a1acd507-9532-45ea-bb08-387b11a96868"
```
- Keep intake source file statuses in sync (`promoted` / closed) after hub
close (done at workplan registration for the intake close step; re-check
if reopened).
- Message `activity-core` (State Hub) that NK-WP-0021 covers residual T06 and
can move T06 to done once T04 verification lands.
- Brief progress event on finish.
**Done when:** activity-core residual task has a clear unblock path and this
workplans hub registration is consistent.
---
## Implementation notes
### Suggested Authelia rule shape (confirm version)
```yaml
access_control:
default_policy: one_factor
rules:
- domain: activity.coulomb.social
subject: "group:activity-core-operators"
policy: one_factor
- domain: temporal.coulomb.social
subject: "group:activity-core-operators"
policy: one_factor
```
If Authelia requires an explicit deny for non-matching subjects on those
domains, use the versions documented pattern (subject rules are exclusive
when matched; default_policy must not re-open the host). Prefer a
deny-by-default domain rule only if the running version needs it — verify
in staging or with a short maintenance window.
### Non-goals reminder
Do **not** invent KeyCape OIDC clients, oauth2-proxy, or Keycloak realm
clients for this path. activity-core already uses ForwardAuth headers
(`Remote-User`, `Remote-Groups`, …).
)