ops-mason/workplans/MASON-WP-0015-construct-grant.md
tegwick 9b705ec62c MASON-WP-0015: gate CCR evidence and qualify the kube wait
T07 generates a CCR policy through the platform generator and refuses
record-evidence unless the built policy matches the CCR file. T06 stays
wait on the open founder decision after a fresh can-i of yes, yes, yes.

Assistant: grok
Assistant-Session: 01a16071-5425-78d3-80b4-20fe2ee30f0a
2026-10-22 01:59:49 +02:00

6.3 KiB

id type title domain repo status owner topic_slug created updated flavor depends_on state_hub_workstream_id
MASON-WP-0015 workplan Restore ops-mason as the OpenBao builder: the ops-mason-construct grant infotech ops-mason active claude netkingdom-security-architecture 2026-10-18 2026-10-22 implementation
384b3efc-f9a5-5ea9-b224-0ce900e79804

ops-mason-construct grant

Decision: intakes/intakes.md MASON-IN-0005 (founder, 2026-10-18, "Build, but never log in"). ops-mason builds structure in OpenBao; ops-warden finds and provides it. The grant breaks the MASON-IN-0004 chain at the login step.

Review both INTENTs and draft the grant

id: MASON-WP-0015-T01
status: done
priority: high
state_hub_task_id: "55448e8c-44a0-5509-9acc-c88e65c680a0"

Reviewed ops-mason and ops-warden INTENT.md. The bodies agree (ops-mason builds, ops-warden routes and assists); the read-only grant did not. Wrote policies/ops-mason-construct.hcl and tests/test_construct_policy.py, added bao-session.sh build <task>, and updated INTENT.md frontmatter and the AGENTS.md OpenBao section.

Install the policy (founder act)

id: MASON-WP-0015-T02
status: done
priority: high
state_hub_task_id: "1c8893bd-8dfe-5293-87dd-8678b20e7313"

Same silent wrapper and hash receipt as the survey policy (AGENTS.md), with ops-mason-construct and /home/worsch/ops-mason/policies/ops-mason-construct.hcl. Confirm your OIDC session can mint a token carrying it (bao-session.sh build MASON-WP-0015-T03).

Live negative and positive tests

id: MASON-WP-0015-T03
status: done
priority: high
state_hub_task_id: "8b512822-ace0-50b0-bf19-52985b229090"

Under the build grant, against throwaway names (workload-kv-read-mason-probe, external-secrets-mason-probe, platform/suppliers/mason/probe): policy and role create/delete succeed; a role with token_policies=default, or with legacy policies, is refused (this confirms glob support in allowed_parameters); role-id read, secret-id write, token create, data read, and a write over the existing probe path are all refused; a policy write outside workload-kv-read-* is refused. Record the results as a names-only receipt. If the glob check fails, stop and redesign the role stanzas before any build.

Done 2026-10-18: docs/evidence/2026-10-18-ops-mason-construct-probes.md. Run 1 found the comma-string bypass (fixed in 482a08b); run 2 passed all 31 checks.

Executor and post-build check

id: MASON-WP-0015-T04
status: done
priority: high
state_hub_task_id: "7b7a436f-c585-5fa8-8afc-5bb033f205f8"

src/ops_mason/executor.py build_approle_kv_lane mints the role-id and secret-id itself (lines ~420-426). Split it so that it stops after the role definition and emits the founder delivery step. Add a post-build check that reads each written policy and compares it with the plan's policy file (normalising the trailing newline). Run the check in every build; a mismatch fails the build record.

Done 2026-10-18: build_approle_kv_lane now builds structure only, and deliver_approle_credentials handles delivery as a founder or applier step. _verify_written_policy reads each new policy back before the role is written. build_kubernetes_kv_lane now writes token_policies/token_ttl, because the legacy policies is denied by ops-mason-construct. 260 tests pass.

Align ops-warden and railiance-platform references

id: MASON-WP-0015-T05
status: done
priority: medium
state_hub_task_id: "8fbac212-1b42-5e08-b65e-8217c481d995"

ops-warden registry/routing/catalog.yaml and wiki/playbooks/openbao-structure-survey.md describe ops-mason as read-only. The railiance-platform references are in docs/credential-lane-designs/application-backup-delivery-2026-10-08.md and RPF-WP-0053 and RPF-WP-0057. Message each owner with MASON-IN-0005, pointers only. They edit their own files.

Close the Kubernetes variant

id: MASON-WP-0015-T06
status: wait
priority: medium
needs_human: true
decision_id: "30d29060-8d14-4a8e-9cde-34aa826eb8d1"
intervention_note: "Founder chooses MASON-IN-0006 option (a), (b), or (c). ops-mason did not choose and did not change cluster RBAC."
state_hub_task_id: "feb17422-9490-51f4-a62d-55647a5f6b05"

A Kubernetes role ops-mason builds can be logged in to by anyone holding a token for the bound ServiceAccount. Confirm that the kube identity ops-mason applies with cannot create serviceaccounts/token or Pods in namespaces it binds roles to (kubectl auth can-i). kubernetes_plane already refuses the Pod and Secret kinds. Record the result in INTENT.md's kubernetes-plane-apply entry.

Result 2026-10-18: open. The kube identity is system:admin (system:masters). It can create serviceaccounts/token, pods and impersonate, cluster-wide. Raised as MASON-IN-0006.

Result 2026-10-22, context default, captured 2026-10-21T23:55:17Z, --all-namespaces, name-only: create serviceaccounts/token yes, create pods yes, impersonate serviceaccounts yes. The identity is still cluster-admin. MASON-IN-0006 stays the open residual and still names options (a), (b), and (c). This task does not choose one and does not change cluster RBAC. It waits on founder decision 30d29060-8d14-4a8e-9cde-34aa826eb8d1 (open, pending).

Submit CCRs from approved plans (RPF-WP-0061)

id: MASON-WP-0015-T07
status: done
priority: medium
state_hub_task_id: "ebc420c6-be1a-5abb-b766-fda0bdf2680b"

railiance-platform RPF-WP-0061 makes ops-mason the only CCR submitter (credential-change.py submit). RPF-WP-0061-T03 and T04 are done, so this no longer waits on them. RPF-WP-0061-T05 still waits for a first live self-service submission; none was filed here because no approved plan is waiting.

2026-10-22: ops-mason generates the CCR draft and its policy from the plan through the platform generator (credential-change.py render-policy, src/ops_mason/ccr_gate.py draft_from_plan). Plan approval is the single hub decision recorded on the draft (state_hub.decision_id). ops-mason did not run sync-decision; that stays a platform or founder step. Before record-evidence, record_evidence compares the built policy text with the CCR policy file and refuses the call unless they match, with a trailing newline normalised. The draft, the policy, and the evidence contain no secret value. Tests: tests/test_ccr_gate.py and tests/test_construct_policy.py.