Finish WP-0025 SSO cutover except group allowlist residual.
Mark T03–T05 and T07–T08 done after live Authelia/TLS verification, prefer SSO principal in ops UI copy and audits, and document break-glass port-forward. Leave T06 waiting on net-kingdom LLDAP/Authelia group rules.
This commit is contained in:
parent
27c087bcb4
commit
91353df7d0
8 changed files with 151 additions and 67 deletions
2
SCOPE.md
2
SCOPE.md
|
|
@ -213,7 +213,7 @@ Open product/policy workplan: **ACTIVITY-WP-0022** (IssueSink no-default-Forgejo
|
|||
| **G7. Credential delivery** | Low (residual) | **FORGEJO_TOKEN** via ESO `actcore-forgejo-admin` (WP-0023-T05, Ready). issue-core `GITEA_BACKEND_TOKEN` still 503 forgejo-inbox for path A rest — **issue-core owner** (WP-0023-T06). |
|
||||
| **G8. Live-images hygiene** | Medium (ops) | Multi-cluster `live-images-all.txt` must be refreshed after deploys or prune can delete live tags (incident 2026-07-21, restored). `scripts/refresh_live_images.sh` (T04). |
|
||||
| **G9. Evidence federation** | Low | Progress often lands on railiance01 edge/hub; workstation primary hub may not show the same feed without tunnel/outbox health. |
|
||||
| **G10. API external access** | Medium (in progress) | WP-0025: Traefik + Authelia SSO for `activity.coulomb.social` + `temporal.coulomb.social`; port-forward remains break-glass until DNS/certs Ready. |
|
||||
| **G10. API external access** | Medium (mostly done) | WP-0025: Traefik + Authelia SSO live for `activity.coulomb.social` + `temporal.coulomb.social`; port-forward break-glass; residual T06 group allowlist. |
|
||||
|
||||
### Drift risks
|
||||
|
||||
|
|
|
|||
|
|
@ -145,12 +145,12 @@
|
|||
| task | ACTIVITY-WP-0024-T09 | done | — | workplans/ACTIVITY-WP-0024-operator-automation-console.md |
|
||||
| task | ACTIVITY-WP-0025-T01 | done | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T02 | done | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T03 | progress | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T04 | progress | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T05 | progress | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T03 | done | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T04 | done | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T05 | done | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T06 | wait | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T07 | progress | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T08 | todo | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T07 | done | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ACTIVITY-WP-0025-T08 | done | — | workplans/ACTIVITY-WP-0025-ops-ui-sso-access.md |
|
||||
| task | ADHOC-2026-06-01-T01 | done | — | workplans/ADHOC-2026-06-01.md |
|
||||
| task | ADHOC-2026-06-01-T02 | done | — | workplans/ADHOC-2026-06-01.md |
|
||||
| task | ADHOC-2026-06-01-T03 | done | — | workplans/ADHOC-2026-06-01.md |
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ Founders / platform operators who already have Authelia accounts under
|
|||
| `activity.coulomb.social` | `actcore-api:8010` | Ops console `/ops/ui`, JSON `/ops/*`, existing API |
|
||||
| `temporal.coulomb.social` | `actcore-temporal-ui:8080` | Temporal Web UI (clean short name) |
|
||||
|
||||
Both resolve to the railiance01 Traefik LB (`92.205.62.239`) once DNS A records
|
||||
exist (same pattern as `forgejo.coulomb.social`).
|
||||
Both resolve to the railiance01 Traefik LB (`92.205.62.239`) — same pattern as
|
||||
`forgejo.coulomb.social`. **DNS A records + Let's Encrypt certs are live**
|
||||
(verified 2026-07-22); Certificate objects `actcore-ops-tls` /
|
||||
`actcore-temporal-ui-tls` are Ready.
|
||||
|
||||
## Auth edge (fleet pattern)
|
||||
|
||||
|
|
@ -59,20 +61,17 @@ Priority for ops mutations (`POST /ops/...`):
|
|||
|
||||
## DNS (operator)
|
||||
|
||||
Create A records (or CNAME to the forgejo host pattern):
|
||||
Expected records (live as of 2026-07-22):
|
||||
|
||||
```text
|
||||
activity.coulomb.social A 92.205.62.239
|
||||
temporal.coulomb.social A 92.205.62.239
|
||||
```
|
||||
|
||||
**Important:** do **not** leave a stale **AAAA** (IPv6) record for
|
||||
`activity.coulomb.social` pointing at parking (e.g. IONOS
|
||||
`2001:8d8:100f:f000::200` / `217.160.0.253`). Cluster resolvers prefer AAAA;
|
||||
cert-manager HTTP-01 self-check then hits the wrong host and returns 204.
|
||||
Either remove AAAA or set it to the railiance01 public IPv6.
|
||||
|
||||
Until DNS is correct, cert-manager Certificate may stay Pending.
|
||||
**Important:** do **not** leave a stale **AAAA** (IPv6) record for either host
|
||||
pointing at parking (e.g. IONOS `2001:8d8:100f:f000::200` / `217.160.0.253`).
|
||||
Cluster resolvers prefer AAAA; cert-manager HTTP-01 self-check then hits the
|
||||
wrong host. Either remove AAAA or set it to the railiance01 public IPv6.
|
||||
|
||||
## Break-glass
|
||||
|
||||
|
|
|
|||
|
|
@ -57,18 +57,23 @@ Prefer the **ops console** over ad-hoc SSH/SQL for “did automations run?” an
|
|||
|
||||
### Auth
|
||||
|
||||
| Mode | When | How |
|
||||
| --- | --- | --- |
|
||||
| **SSO (primary)** | Browser via `activity.coulomb.social` | Authelia session; app trusts `Remote-User` / `Remote-Email` from Traefik ForwardAuth |
|
||||
| **Break-glass token** | Port-forward / emergency / scripts | `X-Operator-Token` or `Authorization: Bearer` |
|
||||
| **Local dev** | No token configured | `ACTIVITY_CORE_OPS_ALLOW_UNAUTH_MUTATIONS=1` only |
|
||||
|
||||
| Env | Purpose |
|
||||
| --- | --- |
|
||||
| `ACTIVITY_CORE_OPERATOR_TOKEN` | Shared operator token; required for **mutations** |
|
||||
| `ACTIVITY_CORE_OPERATOR_TOKEN` | Shared operator token (break-glass); custody in `actcore-runtime-secret` |
|
||||
| `ACTIVITY_CORE_OPS_ALLOW_UNAUTH_MUTATIONS` | `1` only for local dev without a token |
|
||||
|
||||
Mutations: `POST /ops/automations/{id}/trigger|enable|disable|pause|unpause`
|
||||
Header: `X-Operator-Token: <token>` (or `Authorization: Bearer <token>`).
|
||||
Mutations: `POST /ops/automations/{id}/trigger|enable|disable|pause|unpause`.
|
||||
|
||||
Fail-closed: if the token is unset and unauth is not allowed, mutations return
|
||||
**403**. Reads (`GET /ops/...`) do not require the token (ClusterIP / port-forward
|
||||
posture). **Do not** put the token in git, chat, or workplans. Store in
|
||||
`actcore-runtime-secret` (or local `.env`) via operator custody.
|
||||
Fail-closed: without SSO headers and without a valid token (and unauth not
|
||||
allowed), mutations return **401/403**. Reads (`GET /ops/...`) do not require
|
||||
auth at the app layer (ingress still gates browser access via Authelia).
|
||||
**Do not** put the token in git, chat, or workplans.
|
||||
|
||||
### Daily checklist
|
||||
|
||||
|
|
@ -99,13 +104,13 @@ curl -sS -X POST "http://localhost:8010/ops/automations/<id>/disable" \
|
|||
-H "X-Operator-Token: $ACTIVITY_CORE_OPERATOR_TOKEN"
|
||||
```
|
||||
|
||||
Thin UI: open `/ops/ui`, paste the operator token into the browser field
|
||||
(localStorage only), then use Run now / pause actions. **Cron edits are not in
|
||||
Thin UI: open https://activity.coulomb.social/ops/ui (SSO). Break-glass UI still
|
||||
accepts a pasted operator token (localStorage only). **Cron edits are not in
|
||||
the UI** — change definition files and sync.
|
||||
|
||||
### Production access (railiance01)
|
||||
|
||||
**Primary (SSO — ACTIVITY-WP-0025):** after DNS A records exist:
|
||||
**Primary (SSO — ACTIVITY-WP-0025, live):**
|
||||
|
||||
| UI | URL |
|
||||
| --- | --- |
|
||||
|
|
@ -113,12 +118,12 @@ the UI** — change definition files and sync.
|
|||
| Temporal Web UI | https://temporal.coulomb.social |
|
||||
|
||||
Login via Authelia (`auth.coulomb.social`). Design: `docs/ops-sso-access.md`.
|
||||
Mutations accept SSO identity headers; shared token is break-glass only.
|
||||
Mutations use SSO identity; shared token is break-glass only.
|
||||
|
||||
**DNS (required for TLS):**
|
||||
**DNS (already set for TLS):**
|
||||
|
||||
```text
|
||||
activity.coulomb.social A 92.205.62.239
|
||||
activity.coulomb.social A 92.205.62.239
|
||||
temporal.coulomb.social A 92.205.62.239
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# Railiance01 Kubernetes Deployment
|
||||
|
||||
This bundle establishes activity-core as an internal production service on the
|
||||
railiance01 K3s cluster. It keeps the unauthenticated API as a ClusterIP service;
|
||||
publish it through an authenticated ingress only after choosing the final host
|
||||
name and access policy.
|
||||
railiance01 K3s cluster. Services remain ClusterIP; browser access to the ops
|
||||
console and Temporal UI is via Traefik + Authelia SSO Ingress
|
||||
(`activity.coulomb.social`, `temporal.coulomb.social` — ACTIVITY-WP-0025).
|
||||
|
||||
## Layout
|
||||
|
||||
|
|
@ -113,23 +113,26 @@ kubectl -n activity-core get svc
|
|||
|
||||
## Operator automation console (ACTIVITY-WP-0024 / 0025)
|
||||
|
||||
### SSO (primary, after DNS)
|
||||
### SSO (primary — live)
|
||||
|
||||
Manifests `30-`–`32-*.yaml` are applied; TLS certs Ready; Authelia ForwardAuth
|
||||
redirects unauthenticated browsers to `auth.coulomb.social`.
|
||||
|
||||
```bash
|
||||
# DNS A records → 92.205.62.239 (once):
|
||||
# activity.coulomb.social
|
||||
# temporal.coulomb.social
|
||||
|
||||
# Re-apply if needed:
|
||||
kubectl apply -f k8s/railiance/30-authelia-middleware.yaml
|
||||
kubectl apply -f k8s/railiance/31-ingress-ops-sso.yaml
|
||||
kubectl apply -f k8s/railiance/32-ingress-temporal-sso.yaml
|
||||
kubectl -n activity-core set env deploy/actcore-api \
|
||||
ACTIVITY_CORE_TEMPORAL_UI_URL=https://temporal.coulomb.social
|
||||
kubectl -n activity-core set env deploy/actcore-temporal-ui \
|
||||
TEMPORAL_CORS_ORIGINS=https://temporal.coulomb.social,http://localhost:8080,http://127.0.0.1:8080
|
||||
```
|
||||
|
||||
- Ops: https://activity.coulomb.social/ops/ui (Authelia SSO)
|
||||
- Temporal: https://temporal.coulomb.social
|
||||
- Design: `docs/ops-sso-access.md`
|
||||
- Follow-up: LLDAP group `activity-core-operators` + Authelia domain rules (T06)
|
||||
|
||||
### Break-glass port-forward
|
||||
|
||||
|
|
|
|||
|
|
@ -248,15 +248,20 @@ async def auth_status() -> dict[str, Any]:
|
|||
"yes",
|
||||
"on",
|
||||
}
|
||||
temporal = temporal_ui_url()
|
||||
return {
|
||||
"operator_token_configured": operator_token_configured(),
|
||||
"mutation_header": HEADER_NAME,
|
||||
# True when a shared token is required for *break-glass* / non-SSO clients.
|
||||
# Browser SSO (Authelia Remote-User / Remote-Email) does not need the token.
|
||||
"mutations_require_token": operator_token_configured() or not allow,
|
||||
"temporal_ui_url": temporal_ui_url(),
|
||||
"sso_docs": "/docs not required — see docs/ops-sso-access.md",
|
||||
"sso_preferred": True,
|
||||
"sso_headers": ["Remote-User", "Remote-Email", "Remote-Groups"],
|
||||
"temporal_ui_url": temporal,
|
||||
"sso_docs": "docs/ops-sso-access.md",
|
||||
"public_hosts": {
|
||||
"ops": "https://activity.coulomb.social",
|
||||
"temporal_ui": "https://temporal.coulomb.social",
|
||||
"temporal_ui": temporal,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -309,7 +314,7 @@ def _page(title: str, body: str) -> HTMLResponse:
|
|||
<a href="/ops/ui">Inventory</a>
|
||||
<a href="/ops/ui/status?since=sunday">Status</a>
|
||||
<a class="external" href="{temporal_href}" target="_blank" rel="noopener noreferrer"
|
||||
title="Temporal Web UI — port-forward actcore-temporal-ui :8080 until SSO ingress">Temporal UI</a>
|
||||
title="Temporal Web UI (SSO: temporal.coulomb.social)">Temporal UI</a>
|
||||
<a href="/ops/auth/status">Auth JSON</a>
|
||||
</nav>
|
||||
<hr/>
|
||||
|
|
@ -351,12 +356,15 @@ def _token_form() -> str:
|
|||
configured = "yes" if operator_token_configured() else "no"
|
||||
return f"""
|
||||
<div class="card">
|
||||
<label>Operator token (browser only):
|
||||
<p><strong>Auth:</strong> On
|
||||
<a href="https://activity.coulomb.social/ops/ui">activity.coulomb.social</a>,
|
||||
Authelia SSO identity is preferred for mutations (no token paste needed).</p>
|
||||
<label>Break-glass operator token (port-forward / emergency only):
|
||||
<input id="op-token" type="password" placeholder="X-Operator-Token value" autocomplete="off"/>
|
||||
</label>
|
||||
<button type="button" onclick="saveToken()">Save token</button>
|
||||
<p class="muted">Server token configured: <strong>{configured}</strong>.
|
||||
Mutations require header <code>{html.escape(HEADER_NAME)}</code>.</p>
|
||||
<p class="muted">Shared token configured on server: <strong>{configured}</strong>.
|
||||
Header <code>{html.escape(HEADER_NAME)}</code> only when not using SSO.</p>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
|
@ -405,8 +413,7 @@ async def ui_index() -> HTMLResponse:
|
|||
<p class="muted">Temporal Web UI (workflow debugger):
|
||||
<a class="external" href="{html.escape(temporal_ui_url(), quote=True)}"
|
||||
target="_blank" rel="noopener noreferrer">{html.escape(temporal_ui_url())}</a>
|
||||
— requires port-forward of <code>svc/actcore-temporal-ui 8080:8080</code>
|
||||
until SSO ingress (ACTIVITY-WP-0025).</p>
|
||||
— primary SSO URL; port-forward of <code>svc/actcore-temporal-ui</code> is break-glass only.</p>
|
||||
"""
|
||||
return _page("Inventory", body)
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,53 @@ async def test_trigger_requires_token(ops_app: FastAPI, monkeypatch: pytest.Monk
|
|||
assert res.status_code == 401
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_trigger_with_sso_principal(
|
||||
ops_app: FastAPI, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""SSO Remote-User is preferred; audit must record sso:<user>, never the token."""
|
||||
def_id = uuid.uuid4()
|
||||
row = MagicMock()
|
||||
row.name = "Weekly SBOM"
|
||||
row.context_sources = []
|
||||
row.task_templates = []
|
||||
row.trigger_config = {"trigger_type": "cron", "cron_expression": "0 9 * * 1"}
|
||||
|
||||
session = AsyncMock()
|
||||
session.get = AsyncMock(return_value=row)
|
||||
session.__aenter__ = AsyncMock(return_value=session)
|
||||
session.__aexit__ = AsyncMock(return_value=None)
|
||||
ops_app.state.session_factory.return_value = session
|
||||
|
||||
handle = MagicMock()
|
||||
handle.id = f"activity-{def_id}:manual-sso"
|
||||
ops_app.state.temporal.start_workflow = AsyncMock(return_value=handle)
|
||||
|
||||
captured: dict[str, Any] = {}
|
||||
|
||||
async def capture_audit(**kwargs: Any) -> dict[str, Any]:
|
||||
captured.update(kwargs)
|
||||
return {
|
||||
"action": kwargs["action"],
|
||||
"audit_id": "sso-a1",
|
||||
"principal": kwargs["principal"],
|
||||
}
|
||||
|
||||
monkeypatch.setattr("activity_core.ops_api.record_ops_audit", capture_audit)
|
||||
|
||||
transport = ASGITransport(app=ops_app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
res = await client.post(
|
||||
f"/ops/automations/{def_id}/trigger",
|
||||
headers={"Remote-User": "alice.operator"},
|
||||
json={},
|
||||
)
|
||||
assert res.status_code == 200
|
||||
assert res.json()["audit"]["principal"] == "sso:alice.operator"
|
||||
assert captured["principal"] == "sso:alice.operator"
|
||||
assert "token" not in str(res.json()).lower() or "test-token" not in str(res.json())
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_trigger_with_token(ops_app: FastAPI, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def_id = uuid.uuid4()
|
||||
|
|
@ -216,7 +263,7 @@ async def test_ui_index_renders(ops_app: FastAPI, monkeypatch: pytest.MonkeyPatc
|
|||
res = await client.get("/ops/ui/")
|
||||
assert res.status_code == 200
|
||||
assert "Daily Triage" in res.text
|
||||
assert "Operator token" in res.text
|
||||
assert "SSO" in res.text or "Break-glass" in res.text
|
||||
assert "Temporal UI" in res.text
|
||||
assert "temporal.coulomb.social" in res.text
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ state_hub_task_id: "650db102-ad4e-4d8e-86c1-eee50f5bafcc"
|
|||
|
||||
```task
|
||||
id: ACTIVITY-WP-0025-T03
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "7084f7d5-4181-4dfa-b239-0dad15efbae9"
|
||||
```
|
||||
|
|
@ -134,7 +134,7 @@ port-forward; unauthorized gets login or 403.
|
|||
|
||||
```task
|
||||
id: ACTIVITY-WP-0025-T04
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "99b36f31-eb19-4f96-b3f1-9ac1ab02d78f"
|
||||
```
|
||||
|
|
@ -151,7 +151,7 @@ port-forward, under SSO.
|
|||
|
||||
```task
|
||||
id: ACTIVITY-WP-0025-T05
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "77fff202-bfb6-49eb-b8e5-3760abacc8bd"
|
||||
```
|
||||
|
|
@ -186,7 +186,7 @@ account.
|
|||
|
||||
```task
|
||||
id: ACTIVITY-WP-0025-T07
|
||||
status: progress
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "c5945e64-c460-4830-a19e-a944bb44a67e"
|
||||
```
|
||||
|
|
@ -201,7 +201,7 @@ state_hub_task_id: "c5945e64-c460-4830-a19e-a944bb44a67e"
|
|||
|
||||
```task
|
||||
id: ACTIVITY-WP-0025-T08
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "f73eafbf-ad11-4d7d-b89e-e910c662ce86"
|
||||
```
|
||||
|
|
@ -215,12 +215,12 @@ state_hub_task_id: "f73eafbf-ad11-4d7d-b89e-e910c662ce86"
|
|||
|
||||
## 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
|
||||
- [x] HTTPS SSO URLs for ops console and Temporal UI (no port-forward required)
|
||||
- [ ] Only authorized IdP group can access — **MVP: any Authelia user; T06 residual**
|
||||
- [x] Ops mutations attribute to SSO identity; audits show principal (`sso:<user>`)
|
||||
- [x] Ops nav Temporal link points at SSO Temporal URL
|
||||
- [x] Runbook documents SSO primary + break-glass port-forward
|
||||
- [x] SCOPE G10 updated to reflect UI SSO posture
|
||||
|
||||
## Dependencies / coordination
|
||||
|
||||
|
|
@ -250,13 +250,36 @@ state_hub_task_id: "f73eafbf-ad11-4d7d-b89e-e910c662ce86"
|
|||
| --- | --- | --- |
|
||||
| T01 | done | Hosts: activity.coulomb.social + temporal.coulomb.social; docs/ops-sso-access.md |
|
||||
| T02 | done | Reuse Authelia + Traefik ForwardAuth (sso ns); not oauth2-proxy |
|
||||
| T03 | progress | Ingress + middleware manifests landed; **blocked on DNS A records** for cert |
|
||||
| T04 | progress | Temporal Ingress manifest + CORS env; same DNS gate |
|
||||
| T05 | progress | SSO headers preferred over shared token in ops_auth |
|
||||
| T06 | wait | LLDAP group activity-core-operators + Authelia rules (net-kingdom) |
|
||||
| T07 | progress | Runbook + design doc updated |
|
||||
| T08 | todo | After DNS/cert Ready: smoke HTTPS + SSO login |
|
||||
| T03 | done | Ingress + cert Ready; unauth → 302 auth.coulomb.social |
|
||||
| T04 | done | Temporal Ingress + cert Ready; `ACTIVITY_CORE_TEMPORAL_UI_URL` + CORS set |
|
||||
| T05 | done | SSO `Remote-User` preferred; token break-glass; tests + redeploy |
|
||||
| T06 | wait | LLDAP group activity-core-operators + Authelia domain rules (net-kingdom) |
|
||||
| T07 | done | Runbook + k8s README + design doc: SSO primary, port-forward break-glass |
|
||||
| T08 | done | See verification checklist below |
|
||||
|
||||
**Operator action required:** create DNS A records for both hosts → 92.205.62.239,
|
||||
then apply `k8s/railiance/30-*.yaml`–`32-*.yaml` and set
|
||||
`ACTIVITY_CORE_TEMPORAL_UI_URL=https://temporal.coulomb.social` on actcore-api.
|
||||
### Verification checklist (T08) — 2026-07-22
|
||||
|
||||
| Check | Result |
|
||||
| --- | --- |
|
||||
| DNS A → 92.205.62.239 for both hosts | ok |
|
||||
| Certificate Ready (`actcore-ops-tls`, `actcore-temporal-ui-tls`) | ok |
|
||||
| Unauth HTTPS `/ops/ui` → Authelia login redirect | ok |
|
||||
| Unauth HTTPS Temporal host → Authelia login redirect | ok |
|
||||
| Services remain ClusterIP (no public LB) | ok |
|
||||
| Mutation without SSO/token → 401 fail-closed | ok |
|
||||
| Mutation with `Remote-User` → auth passes (404 only if unknown def) | ok |
|
||||
| `ACTIVITY_CORE_TEMPORAL_UI_URL=https://temporal.coulomb.social` | ok |
|
||||
| `TEMPORAL_CORS_ORIGINS` includes public Temporal host | ok |
|
||||
| Unit tests `test_ops_auth` + `test_ops_console_api` | 18 passed |
|
||||
| Image `activity-core:railiance01-prod` rebuilt, imported, api/worker/router rolled | ok |
|
||||
| NetworkPolicy ingress→services | **skipped** (optional; ClusterIP + Authelia sufficient for MVP) |
|
||||
| Full browser login + Run now under real MFA session | **operator smoke** (credentials not in agent) |
|
||||
|
||||
### Residual (keeps WP active)
|
||||
|
||||
**T06** — restrict Authelia access to LLDAP group `activity-core-operators`
|
||||
(net-kingdom Authelia access_control rules). Until then any authenticated
|
||||
Authelia user can reach the UIs (org-wide SSO, not least-privilege).
|
||||
|
||||
**Operator:** open https://activity.coulomb.social/ops/ui once, confirm inventory
|
||||
loads and a safe mutation (or dry path) shows `sso:<you>` in `/ops/audits`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue