From 97d1fed9496aacb1836982096b8aecb6da13cf0d Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 14 Oct 2026 22:14:32 +0200 Subject: [PATCH] Record the passing hub-writer receipts. Member and non-member verification both passed. The verifier accepts s. tokens and calls sys/capabilities with a token that is allowed to. Assistant: grok Assistant-Session: 01a13b2d-5f6d-7a32-b2c9-384625c55ca6 --- .custodian-brief.md | 6 +- ...-10-14-reuse-surface-hub-writer-member.txt | 16 ++++ ...14-reuse-surface-hub-writer-non-member.txt | 16 ++++ docs/reuse-surface-hub-writer-attended.md | 10 ++- plans/reuse-surface-hub-writer.md | 10 ++- scripts/verify-reuse-surface-hub-writer.py | 84 ++++++++++++------- .../MASON-WP-0011-reuse-surface-hub-writer.md | 19 +++-- 7 files changed, 116 insertions(+), 45 deletions(-) create mode 100644 docs/evidence/2026-10-14-reuse-surface-hub-writer-member.txt create mode 100644 docs/evidence/2026-10-14-reuse-surface-hub-writer-non-member.txt diff --git a/.custodian-brief.md b/.custodian-brief.md index 8235c59..ce26687 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,17 +2,15 @@ # Custodian Brief — ops-mason **Domain:** infotech -**Last synced:** 2026-10-14 17:37 UTC +**Last synced:** 2026-10-14 17:49 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams ### Name a dedicated caller for the reuse-surface hub writer token -Progress: 2/4 done | workplan_id: `08e52353-b6e5-5742-b7b4-080d51a0cf57` +Progress: 3/4 done | workplan_id: `08e52353-b6e5-5742-b7b4-080d51a0cf57` **Open tasks:** -- ! Apply the role without a KV write `7c32e273` - *(wait: After T02. Founder applies proposed CCR-2026-0044 through the delegated applier. No bao kv put. Do not attach the policy to the admin entity.)* - ! Record the sanitized receipt `9e12db29` *(wait: After T03. Founder runs scripts/verify-reuse-surface-hub-writer.py member and non-member. A live receipt does not exist yet.)* diff --git a/docs/evidence/2026-10-14-reuse-surface-hub-writer-member.txt b/docs/evidence/2026-10-14-reuse-surface-hub-writer-member.txt new file mode 100644 index 0000000..6319016 --- /dev/null +++ b/docs/evidence/2026-10-14-reuse-surface-hub-writer-member.txt @@ -0,0 +1,16 @@ +mode=member +at=2026-10-14T20:05:45Z +addr=http://127.0.0.1:18200 +role=reuse-surface-hub-writer-workload-kv-read +policy=workload-kv-read-reuse-surface-runtime +login=ok +entity_id=c204099b-cb94-363f-e207-5ff4d4aa6e94 +policies=workload-kv-read-reuse-surface-runtime +identity_policies= +platform/data/workloads/reuse/reuse-surface/runtime-secrets: read +platform/metadata/workloads/reuse/reuse-surface/runtime-secrets: read +platform/data/workloads/reuse/reuse-surface: deny +platform/metadata/workloads/reuse/reuse-surface: deny +platform/data/workloads/issue-core/issue-core/issue-core-runtime: deny +revoked=yes +result=pass diff --git a/docs/evidence/2026-10-14-reuse-surface-hub-writer-non-member.txt b/docs/evidence/2026-10-14-reuse-surface-hub-writer-non-member.txt new file mode 100644 index 0000000..f926fe5 --- /dev/null +++ b/docs/evidence/2026-10-14-reuse-surface-hub-writer-non-member.txt @@ -0,0 +1,16 @@ +mode=non-member +at=2026-10-14T20:10:49Z +addr=http://127.0.0.1:18200 +role=reuse-surface-hub-writer-workload-kv-read +policy=workload-kv-read-reuse-surface-runtime +login=refused +entity_id= +policies= +identity_policies= +platform/data/workloads/reuse/reuse-surface/runtime-secrets: not-run +platform/metadata/workloads/reuse/reuse-surface/runtime-secrets: not-run +platform/data/workloads/reuse/reuse-surface: not-run +platform/metadata/workloads/reuse/reuse-surface: not-run +platform/data/workloads/issue-core/issue-core/issue-core-runtime: not-run +revoked=n/a +result=pass diff --git a/docs/reuse-surface-hub-writer-attended.md b/docs/reuse-surface-hub-writer-attended.md index 302ab4c..37efc52 100644 --- a/docs/reuse-surface-hub-writer-attended.md +++ b/docs/reuse-surface-hub-writer-attended.md @@ -2,7 +2,7 @@ Plan: `plans/reuse-surface-hub-writer.md`. Filed request: `railiance-platform/credential-change-requests/CCR-2026-0044-reuse-surface-hub-writer-caller.yaml` -(proposed). Source copy: +(applied 2026-10-14). Source copy: `builds/reuse-surface-hub-writer/credential-change-requests/CCR-DRAFT-REUSE-SURFACE-HUB-WRITER.yaml`. Run this only after CCR-2026-0044 is approved and applied. LLDAP group @@ -15,9 +15,11 @@ shape go through identity-provisioner `POST /v1/groups/ensure`; see `docs/directory-group-binding.md`. -The script leaves `~/.vault-token` in place and uses it only to call -`sys/capabilities`. The lane token is not printed and is revoked before the -script exits. A passing receipt is the sanitized verification record. +The script leaves `~/.vault-token` in place. It calls `sys/capabilities` with +the first local token that is allowed to: `BAO_TOKEN` or `VAULT_TOKEN`, then +`~/.local/openbao/platform-admin.token`, then `~/.vault-token`. The lane token +is not printed and is revoked before the script exits. A passing receipt is +the sanitized verification record. ```bash export BAO_ADDR=http://127.0.0.1:18200 diff --git a/plans/reuse-surface-hub-writer.md b/plans/reuse-surface-hub-writer.md index f1c1d70..13e48eb 100644 --- a/plans/reuse-surface-hub-writer.md +++ b/plans/reuse-surface-hub-writer.md @@ -140,7 +140,9 @@ key-cape confirms the group: 1. Copy the policy file over `railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime.hcl` and apply the CCR with the delegated applier. Do not run `bao kv put`. -2. Keep an operator token in `~/.vault-token` for the capability query. +2. The capability query uses the first local token allowed to call + `sys/capabilities`: `BAO_TOKEN` or `VAULT_TOKEN`, then + `~/.local/openbao/platform-admin.token`, then `~/.vault-token`. In a private window, as the group member: ```bash @@ -165,7 +167,11 @@ Do not export the field. Revoke the lane token when the command returns. ## 6. Result -Applied 2026-10-14T17:47:43Z. No live member receipt yet. +Applied 2026-10-14T17:47:43Z. Both attended receipts passed on 2026-10-14. +Member at 20:05:45Z is entity `c204099b-cb94-363f-e207-5ff4d4aa6e94` with +only `workload-kv-read-reuse-surface-runtime`. Non-member at 20:10:49Z was +refused. Copies are in `docs/evidence/2026-10-14-reuse-surface-hub-writer-member.txt` +and `docs/evidence/2026-10-14-reuse-surface-hub-writer-non-member.txt`. 2026-10-14: railiance-platform filed this bundle as proposed CCR-2026-0044 (`88b4aed`, message `14e23602`). The live policy file stays as it was until diff --git a/scripts/verify-reuse-surface-hub-writer.py b/scripts/verify-reuse-surface-hub-writer.py index c022db6..313c30d 100644 --- a/scripts/verify-reuse-surface-hub-writer.py +++ b/scripts/verify-reuse-surface-hub-writer.py @@ -6,11 +6,12 @@ Logs in on auth/netkingdom role reuse-surface-hub-writer-workload-kv-read with ``bao login -token-only``, so the operator's token helper is left alone. The -lane token stays in memory. Capability queries run as the operator token -already present in the environment or token helper, because the workload -policy has no sys/capabilities-self grant. The receipt records policies, -entity id, path capabilities, and pass/fail. It does not record the token or -any KV value. +lane token stays in memory. Capability queries use the first local token that +may call ``sys/capabilities`` (``BAO_TOKEN`` or ``VAULT_TOKEN``, then +``~/.local/openbao/platform-admin.token``, then ``~/.vault-token``), because +the workload policy has no sys/capabilities-self grant. The receipt records +policies, entity id, path capabilities, and pass/fail. It does not record the +token or any KV value. Sign in with a private browser window. A reused platform-admin session is the wrong principal for ``member``. @@ -43,7 +44,8 @@ from ops_mason.reuse_surface_hub_writer import ( ) BAO_ADDR = os.environ.get("BAO_ADDR", "http://127.0.0.1:18200") -TOKEN_RE = re.compile(r"\b(?:hvs|hvb|hvr)\.[A-Za-z0-9_-]{8,}\b") +# This OpenBao issues s. service tokens. hvs/hvb/hvr are the newer Vault shapes. +TOKEN_RE = re.compile(r"\b(?:hvs|hvb|hvr|s)\.[A-Za-z0-9_-]{8,}\b") PATHS = [DATA, METADATA, PARENT_DATA, PARENT_METADATA, SIBLING] @@ -58,14 +60,45 @@ def receipts_dir() -> Path: return path -def operator_token() -> str: +def token_candidates() -> list[tuple[str, str]]: + found: list[tuple[str, str]] = [] for name in ("BAO_TOKEN", "VAULT_TOKEN"): value = os.environ.get(name, "").strip() if value: - return value - helper = Path(os.environ.get("HOME", str(Path.home()))) / ".vault-token" - if helper.is_file(): - return helper.read_text(encoding="utf-8").strip() + found.append((name, value)) + home = Path(os.environ.get("HOME", str(Path.home()))) + for label, path in ( + ("~/.local/openbao/platform-admin.token", home / ".local/openbao/platform-admin.token"), + ("~/.vault-token", home / ".vault-token"), + ): + if path.is_file(): + value = path.read_text(encoding="utf-8").strip() + if value: + found.append((label, value)) + return found + + +def write_capabilities(caller: str, subject: str, paths: list[str]) -> subprocess.CompletedProcess[str]: + body = json.dumps({"token": subject, "paths": paths}) + tmp = tempfile.NamedTemporaryFile("w", prefix="rs-caps-", delete=False) + try: + os.fchmod(tmp.fileno(), stat.S_IRUSR | stat.S_IWUSR) + tmp.write(body) + tmp.close() + return run_bao(["write", "-format=json", "sys/capabilities", f"@{tmp.name}"], caller) + finally: + try: + os.remove(tmp.name) + except OSError: + pass + + +def operator_token() -> str: + """Return a token allowed to call sys/capabilities, or an empty string.""" + for _label, token in token_candidates(): + probe = write_capabilities(token, token, ["auth/token/lookup-self"]) + if probe.returncode == 0: + return token return "" @@ -90,8 +123,8 @@ def login() -> str: token="", ) token = (proc.stdout or "").strip() - if proc.returncode != 0 or not token or TOKEN_RE.fullmatch(token) is None: - sys.stderr.write(redact(proc.stderr or "login refused\n")) + if proc.returncode != 0 or TOKEN_RE.fullmatch(token) is None: + sys.stderr.write(redact(proc.stderr or "login output was not a token\n")) return "" return token @@ -107,21 +140,10 @@ def lookup(token: str) -> dict: def capabilities(operator: str, lane: str) -> dict[str, list[str]]: if not operator: raise RuntimeError( - "no operator token for sys/capabilities " - "(BAO_TOKEN, VAULT_TOKEN, or ~/.vault-token)" + "no token can call sys/capabilities " + "(BAO_TOKEN, VAULT_TOKEN, ~/.local/openbao/platform-admin.token, or ~/.vault-token)" ) - body = json.dumps({"token": lane, "paths": PATHS}) - tmp = tempfile.NamedTemporaryFile("w", prefix="rs-caps-", delete=False) - try: - os.fchmod(tmp.fileno(), stat.S_IRUSR | stat.S_IWUSR) - tmp.write(body) - tmp.close() - proc = run_bao(["write", "-format=json", "sys/capabilities", f"@{tmp.name}"], operator) - finally: - try: - os.remove(tmp.name) - except OSError: - pass + proc = write_capabilities(operator, lane, PATHS) if proc.returncode != 0: raise RuntimeError(redact(proc.stderr or "sys/capabilities failed")) payload = json.loads(proc.stdout) @@ -151,11 +173,17 @@ def main(argv: list[str]) -> int: if mode not in {"member", "non-member"}: sys.stderr.write("usage: verify-reuse-surface-hub-writer.py member|non-member\n") return 2 + operator = operator_token() + if not operator: + sys.stderr.write( + "no token can call sys/capabilities. Checked BAO_TOKEN, VAULT_TOKEN, " + "~/.local/openbao/platform-admin.token, and ~/.vault-token.\n" + ) + return 1 sys.stderr.write( f"Sign in as the {mode} identity in a private browser window.\n" "A platform-admin session is the wrong principal for member.\n" ) - operator = operator_token() now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") lane = login() report: dict = { diff --git a/workplans/MASON-WP-0011-reuse-surface-hub-writer.md b/workplans/MASON-WP-0011-reuse-surface-hub-writer.md index 921136d..2b44c95 100644 --- a/workplans/MASON-WP-0011-reuse-surface-hub-writer.md +++ b/workplans/MASON-WP-0011-reuse-surface-hub-writer.md @@ -4,7 +4,7 @@ type: workplan title: "Name a dedicated caller for the reuse-surface hub writer token" domain: infotech repo: ops-mason -status: active +status: finished flavor: planning owner: codex topic_slug: custodian @@ -117,15 +117,20 @@ Done 2026-10-14. ```task id: MASON-WP-0011-T04 -status: wait +status: done depends_on: [MASON-WP-0011-T03] -blocked_on: human -blocking_reason: "After T03. Founder runs scripts/verify-reuse-surface-hub-writer.py member and non-member. A live receipt does not exist yet." priority: high state_hub_task_id: "9e12db29-532d-5d90-8519-227799018ab0" ``` -Both modes must pass. Copy the mode-0600 receipts into `docs/evidence/` -with no token and no field value, reply to message `08b254af`, and tell -ops-warden the role name for `auth_method`. Hub registration itself stays +Both modes passed on 2026-10-14. Member receipt at 20:05:45Z: login ok, +entity `c204099b-cb94-363f-e207-5ff4d4aa6e94`, policy +`workload-kv-read-reuse-surface-runtime` only, exact paths read, parent and +sibling paths deny, token revoked. Non-member receipt at 20:10:49Z: +login refused. Copies with no token are +`docs/evidence/2026-10-14-reuse-surface-hub-writer-member.txt` and +`docs/evidence/2026-10-14-reuse-surface-hub-writer-non-member.txt`. +Replied to message `08b254af`. ops-warden can name role +`reuse-surface-hub-writer-workload-kv-read` on +`auth_method` for `reuse-surface-hub-write-token`. Hub registration stays with the-custodian.