Record whynot OpenBao lane apply evidence

This commit is contained in:
2026-06-28 12:41:39 +02:00
parent 3ef25cb787
commit 271aa94642
8 changed files with 134 additions and 12 deletions

View File

@@ -55,6 +55,9 @@ openbao:
method: oidc
mount: netkingdom
role: whynot-design-workload-kv-read
allowed_redirect_uris:
- https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
- http://localhost:8250/oidc/callback
user_claim: sub
groups_claim: groups
bound_claims:
@@ -91,6 +94,16 @@ verification:
- OIDC role bound to confirmed whynot-design claim or approved service account.
- Secret value provisioned directly in OpenBao through approved operator custody.
- Positive and negative verification recorded with non-secret audit ids or timestamps.
evidence:
- at: '2026-06-28T10:37:42+00:00'
actor: codex
kind: non_secret_openbao_apply_check
result: passed
details:
- Policy read succeeded for workload-kv-read-whynot-design-npm-publish.
- OIDC role read showed the whynot-design bound claim, read policy, and callback URIs.
- Metadata read showed catalog-id whynot-design-npm-publish.
- Secret field presence check found NPM_AUTH_TOKEN without printing or recording the value.
lifecycle:
deactivate: Disable ops-warden catalog entry and remove or detach auth role policy.
rotate: Replace NPM_AUTH_TOKEN value directly in OpenBao and record non-secret rotation

View File

@@ -7,7 +7,7 @@ This is the next-session handoff for `CCR-2026-0001` and the
- CCR: `CCR-2026-0001`
- Decision: `e6381a56-6b04-4fd5-b2de-f3ef59cde888`
- Status: approved
- Status: approved; non-secret OpenBao apply checks passed 2026-06-28
- Front door: `template`, `resolvable=false`
- Catalog id: `whynot-design-npm-publish`
- Tenant/org: `coulomb`
@@ -18,9 +18,11 @@ This is the next-session handoff for `CCR-2026-0001` and the
`https://gitea.coulomb.social/api/packages/coulomb/npm/`
The operator reported that the Gitea token was generated and stored in OpenBao.
Codex could not verify the metadata from the current token-helper identity:
metadata lookup, policy read, and auth-role read all returned `403 permission
denied`. No secret value was read or printed.
Using the temporary operator token only for non-secret infrastructure work, Codex
confirmed that the policy exists, the OIDC role exists with the whynot-design
binding and redirect URIs, the secret metadata has the expected catalog id, and
the `NPM_AUTH_TOKEN` field is present. No secret value was printed, recorded,
or copied into Git, State Hub, chat, or workplans.
## Safety Rules
@@ -94,6 +96,10 @@ Role payload:
```json
{
"role_type": "oidc",
"allowed_redirect_uris": [
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"http://localhost:8250/oidc/callback"
],
"user_claim": "sub",
"groups_claim": "groups",
"bound_claims": {
@@ -111,6 +117,10 @@ role_payload_file="$(mktemp)"
trap 'rm -f "$role_payload_file"' EXIT
cat >"$role_payload_file" <<'JSON'
{
"allowed_redirect_uris": [
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"http://localhost:8250/oidc/callback"
],
"bound_claims": {
"groups": [
"whynot-design"
@@ -181,7 +191,8 @@ Only after these are true:
- secret metadata confirmed;
- policy exists and is scoped to the corrected `coulomb/whynot-design` path;
- OIDC role exists and binds only `groups=["whynot-design"]`;
- OIDC role exists and binds only `groups=["whynot-design"]` with approved
browser and local CLI callback URIs;
- positive verification passed;
- negative verification passed;

View File

@@ -35,6 +35,7 @@ Ops-warden batch follow-up:
| Policy file | `openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl` |
| OIDC auth mount | `netkingdom` |
| OIDC role | `whynot-design-workload-kv-read` |
| OIDC callback URIs | `https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback`, `http://localhost:8250/oidc/callback` |
| Kubernetes auth role | `whynot-design-workload-kv-read` if an in-cluster service account consumes this lane |
| flex-auth ref | `secret.read:whynot-design` if tenant policy requires pre-approval |
@@ -109,6 +110,14 @@ The role must attach only:
workload-kv-read-whynot-design-npm-publish
```
The OIDC role must include the browser and local CLI callback URIs accepted by
OpenBao:
```text
https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
http://localhost:8250/oidc/callback
```
The whynot-design pilot claim is confirmed as `groups=whynot-design`. Before
applying any changed role, re-confirm the KeyCape/NetKingdom claim that
identifies the whynot-design caller. The role must bind to that claim; do not
@@ -154,9 +163,25 @@ Positive verification:
1. Authenticate as the whynot-design caller using the approved OIDC or
Kubernetes auth role.
2. Fetch the field in an attended session or through `warden access --fetch`.
2. Fetch the field in an attended session or through `warden access --exec`.
3. Record only that the fetch succeeded; do not record the value.
Safe attended command shape before the dedicated ops-warden catalog id is
activated:
```bash
set +x
bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read
warden access "npm token" \
--path platform/workloads/coulomb/whynot-design/npm-publish \
--field NPM_AUTH_TOKEN \
--no-policy \
--exec -- sh -lc 'test -n "$NPM_AUTH_TOKEN"'
```
Use `--no-policy` only while the local ops-warden config reports
`policy.enabled=false`; remove it once the flex-auth gate is enforced.
Negative verification:
1. Authenticate as a non-whynot identity.

View File

@@ -81,6 +81,11 @@ workload_kv_read:
- deactivate
- rotate
- compromised
conditional:
openbao.auth.method=oidc:
required:
- allowed_redirect_uris
allowed_redirect_uris: non-empty list of OpenBao callback URIs accepted by the role
access_frontdoor_readiness:
allowed:

View File

@@ -169,6 +169,19 @@ def validate_workload_kv_read(ccr: dict[str, Any], errors: list[str], warnings:
errors.append("openbao.auth.method must be oidc or kubernetes")
require_string(auth.get("mount"), "openbao.auth.mount", errors)
require_string(auth.get("role"), "openbao.auth.role", errors)
if method == "oidc":
redirect_uris = require_list(
auth.get("allowed_redirect_uris"),
"openbao.auth.allowed_redirect_uris",
errors,
)
if not redirect_uris:
errors.append("openbao.auth.allowed_redirect_uris must not be empty for oidc")
for index, uri in enumerate(redirect_uris):
if not isinstance(uri, str) or not uri.strip():
errors.append(
f"openbao.auth.allowed_redirect_uris[{index}] must be a non-empty string"
)
policies = [str(policy) for policy in require_list(auth.get("policies"), "openbao.auth.policies", errors)]
if policies != [policy_name]:
errors.append("openbao.auth.policies must contain exactly openbao.policy_name")
@@ -346,6 +359,8 @@ def auth_payload(ccr: dict[str, Any]) -> dict[str, Any]:
}
if auth.get("groups_claim"):
payload["groups_claim"] = auth["groups_claim"]
if auth.get("allowed_redirect_uris"):
payload["allowed_redirect_uris"] = auth["allowed_redirect_uris"]
return payload

View File

@@ -127,6 +127,18 @@ class CredentialChangeTests(unittest.TestCase):
self.assertEqual(payload["bound_service_account_namespaces"], ["issue-core"])
self.assertNotIn("bound_claims", payload)
def test_oidc_auth_payload_includes_redirect_uris(self) -> None:
ccr, errors, _warnings = credential_change.validate_ccr(self.sample)
self.assertEqual(errors, [])
payload = credential_change.auth_payload(ccr)
self.assertEqual(
payload["allowed_redirect_uris"],
[
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"http://localhost:8250/oidc/callback",
],
)
def test_apply_plan_refuses_unapproved_ccr(self) -> None:
with self.assertRaises(SystemExit):
credential_change.command_apply_plan(type("Args", (), {"ref": str(self.issue_core)})())
@@ -151,6 +163,11 @@ class CredentialChangeTests(unittest.TestCase):
)
self.assertIn('role_payload_file="$(mktemp)"', rendered)
self.assertIn('"bound_claims": {', rendered)
self.assertIn('"allowed_redirect_uris": [', rendered)
self.assertIn(
'"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback"',
rendered,
)
self.assertIn(
'bao write auth/netkingdom/role/whynot-design-workload-kv-read @"$role_payload_file"',
rendered,

View File

@@ -4,13 +4,13 @@ type: workplan
title: "Workload KV Access Lanes for ops-warden Fetch"
domain: financials
repo: railiance-platform
status: blocked
status: active
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 6
created: "2026-06-27"
updated: "2026-06-27"
updated: "2026-06-28"
depends_on_workplans:
- RAIL-PL-WP-0002
- RAILIANCE-WP-0004
@@ -152,11 +152,15 @@ denied with `403 permission denied` while writing the policy, so live policy
application waits on an approved platform-admin/operator token or a narrow
token-helper capability.
**2026-06-28:** Using the temporary operator token provided outside the repo,
Codex applied/confirmed the live policy in OpenBao. The verification read of the
policy succeeded and no secret values were printed or recorded.
## T03 - Define and apply auth bindings
```task
id: RAILIANCE-WP-0006-T03
status: wait
status: done
priority: high
state_hub_task_id: "a217371a-0f85-40c6-b691-ac67834c86b5"
```
@@ -181,11 +185,17 @@ Acceptance:
of the KeyCape/NetKingdom whynot-design bound claim or approved service-account
subject; do not create an unbounded OIDC role.
**2026-06-28:** Created/confirmed
`auth/netkingdom/role/whynot-design-workload-kv-read` with
`groups=["whynot-design"]`, only the
`workload-kv-read-whynot-design-npm-publish` policy, `ttl=15m`, and the approved
browser/local CLI callback URIs.
## T04 - Provision the KV path without exposing the token
```task
id: RAILIANCE-WP-0006-T04
status: wait
status: done
priority: high
state_hub_task_id: "c43724a3-c83e-4ab6-b7d1-e427fd93a9a9"
```
@@ -208,11 +218,17 @@ Acceptance:
provisioning is waiting on an approved operator/OpenBao custody path for the
actual `NPM_AUTH_TOKEN` value.
**2026-06-28:** Confirmed the OpenBao metadata at
`platform/workloads/coulomb/whynot-design/npm-publish` includes
`catalog-id=whynot-design-npm-publish` and that the `NPM_AUTH_TOKEN` field is
present. The value was not printed, recorded, or copied into Git, State Hub,
chat, or workplans.
## T05 - Verify caller-scoped fetch behavior
```task
id: RAILIANCE-WP-0006-T05
status: wait
status: progress
priority: high
state_hub_task_id: "dc1f470b-e78a-48a9-9957-965aed47861f"
```
@@ -233,11 +249,16 @@ Acceptance:
secret provisioning. The runbook requires positive and negative fetch evidence
without printing the token value.
**2026-06-28:** Non-secret operator checks now pass for policy, auth role,
metadata, and field presence. Remaining verification is the attended
whynot-design OIDC positive check and a non-whynot denial check, both without
printing the token.
## T06 - Coordinate ops-warden catalog activation
```task
id: RAILIANCE-WP-0006-T06
status: wait
status: progress
priority: high
state_hub_task_id: "8e84ec19-01db-4baf-a532-de87e51d4994"
```
@@ -260,6 +281,11 @@ handoff payload for ops-warden and sent the pointers by State Hub message. The
entry should remain draft/non-active until live OpenBao provisioning and
verification complete.
**2026-06-28:** The generic `openbao-api-key` ops-warden access lane can proxy
the check with explicit `--path` and `--field`, but the dedicated
`whynot-design-npm-publish` route is not yet present in the ops-warden routing
catalog. Keep activation pending until caller verification and catalog update.
## T07 - Decide whether to batch sibling workload-KV requests
```task

View File

@@ -180,6 +180,10 @@ not `approved` and also refuses unconfirmed bound claims. Remaining T04 work is
to add a richer diff against existing source artifacts and eventually bridge
from reviewed plan to the interactive live applier.
**2026-06-28:** Added OIDC `allowed_redirect_uris` to the CCR contract and
generated role payloads after live OpenBao rejected an OIDC role without
callbacks. Unit coverage now checks the generated whynot-design role payload.
## T05 - Add chat/CLI approval commands
```task
@@ -286,6 +290,12 @@ received `403 permission denied`. Prepared
policy, auth-role, metadata verification, positive verification, negative
verification, and activation without printing the token.
**2026-06-28:** With the temporary operator token, Codex applied/confirmed the
OpenBao read policy and OIDC role, confirmed metadata `catalog-id`, and confirmed
`NPM_AUTH_TOKEN` field presence without printing or recording the value. The CCR
now records non-secret evidence for that apply check. Positive whynot-design and
negative non-whynot caller verification still gate `active`/`ready`.
## T08 - Add deactivation, rotation, and compromise flows
```task