Record founder approval of the MAS backup reader.
CCR-2026-0062 is approved with the live delivery account confirmed. The OpenBao policy is not installed and no provider key was created. Assistant: grok Assistant-Session: 01a198fa-fd27-7e41-a0a2-57355c3e4efd
This commit is contained in:
parent
a274938232
commit
0fb2305def
4 changed files with 184 additions and 34 deletions
|
|
@ -4,7 +4,7 @@
|
|||
"schema_version": 1,
|
||||
"request_type": "workload-kv-read",
|
||||
"title": "MAS evidence archive node-delivery reader",
|
||||
"status": "draft",
|
||||
"status": "approved",
|
||||
"created": "2026-11-02",
|
||||
"updated": "2026-11-02",
|
||||
"requester": {
|
||||
|
|
@ -17,7 +17,22 @@
|
|||
"platform-owner",
|
||||
"mas-owner"
|
||||
],
|
||||
"comments": []
|
||||
"comments": [
|
||||
{
|
||||
"at": "2026-11-02T01:35:00Z",
|
||||
"reviewer": "User (platform-owner)",
|
||||
"decision": "approved",
|
||||
"comment": "Founder approved this reader on 2026-11-02. One read policy, ServiceAccount backup-primary-delivery in namespace mas, audience openbao.",
|
||||
"source_ref": "founder-approval-2026-11-02"
|
||||
},
|
||||
{
|
||||
"at": "2026-11-02T01:35:00Z",
|
||||
"reviewer": "User (mas-owner)",
|
||||
"decision": "approved",
|
||||
"comment": "Founder approved MAS node delivery of this backup pair. The IAM application, pair custody, age escrow, and offsite publication stay separate.",
|
||||
"source_ref": "founder-approval-2026-11-02"
|
||||
}
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"domain": "communication",
|
||||
|
|
@ -59,13 +74,13 @@
|
|||
"mas"
|
||||
]
|
||||
},
|
||||
"bound_claims_confirmed": false
|
||||
"bound_claims_confirmed": true
|
||||
}
|
||||
},
|
||||
"access_frontdoor": {
|
||||
"type": "platform-owner",
|
||||
"catalog_id": "mas-backup-primary-delivery",
|
||||
"readiness": "pending-review",
|
||||
"readiness": "approved-pending-apply",
|
||||
"resolvable": false
|
||||
},
|
||||
"delivery": {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
# MAS evidence publisher grant — 2026-11-02
|
||||
|
||||
Prepared for RPF-WP-0070. CCR-2026-0062 is a **draft**. Its Kubernetes binding
|
||||
is unconfirmed and its delivery admission is false. This packet creates no
|
||||
provider identity, OpenBao policy, role, or value, ServiceAccount, node file,
|
||||
offsite upload, or timer.
|
||||
Prepared for RPF-WP-0070. The founder approved CCR-2026-0062 on 2026-11-02
|
||||
as platform-owner and mas-owner. The Kubernetes binding is confirmed against
|
||||
ServiceAccount `backup-primary-delivery` in namespace `mas`, which exists with
|
||||
automount disabled. Delivery admission is still false. No provider identity,
|
||||
OpenBao policy, role, or value, node file, offsite upload, or timer exists yet.
|
||||
|
||||
The accepted shape is the email-connect ledger reader, CCR-2026-0037. The MAS
|
||||
prefix is `platform-pg/application-archives/mas/` in bucket
|
||||
|
|
@ -13,14 +14,14 @@ local. It is not an offsite object.
|
|||
|
||||
| Item | Prepared value | Live state |
|
||||
| --- | --- | --- |
|
||||
| Reader request | CCR-2026-0062 | draft, not approved |
|
||||
| Reader request | CCR-2026-0062 | approved; not applied |
|
||||
| KV path | platform/workloads/mas/backup-primary | no value written |
|
||||
| Fields | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY | absent |
|
||||
| Policy | workload-kv-read-mas-backup-primary | source only |
|
||||
| Policy | workload-kv-read-mas-backup-primary | source only; install is silent and attended |
|
||||
| Role | mas-backup-primary-workload-kv-read | not created |
|
||||
| ServiceAccount | backup-primary-delivery in namespace mas | source list only; automount false; no RoleBinding |
|
||||
| ServiceAccount | backup-primary-delivery in namespace mas | live, automount false, uid c0e5867b-b4be-49b7-94db-6aae0a6e566e; no RoleBinding |
|
||||
| Node file | /etc/mas-backup/primary.env, root:root 0600, directory 0700 | not created |
|
||||
| Provider identity | proposed application mas-evidence-backup-primary | not created |
|
||||
| Provider identity | proposed application mas-evidence-backup-primary | not created; this workstation has no Scaleway credential |
|
||||
|
||||
Do not reuse the email-connect publisher or its node file. Object-storage IAM
|
||||
is not prefix-scoped inside a project, so a later admission needs its own IAM
|
||||
|
|
@ -30,19 +31,23 @@ and is not created here.
|
|||
|
||||
## Still required before any offsite copy
|
||||
|
||||
1. Founder reviews as platform-owner and mas-owner, with the namespace,
|
||||
ServiceAccount, and audience confirmed.
|
||||
2. Create the separate IAM application and prove the prefix boundary, including
|
||||
denial of sibling prefixes, retained delete, and IAM or bucket administration.
|
||||
3. Apply the policy and role through the delegated metadata applier. Ops-mason
|
||||
remains survey-only.
|
||||
4. Provision the pair atomically with KV CAS=0 under an explicit platform
|
||||
custody act. The single-field paste desk is unsuitable. Pair-custody and
|
||||
slot-preparation lists stay closed to coulomb-social and email-connect until
|
||||
that act.
|
||||
5. Record positive reader proof and negative denials, then set delivery
|
||||
admission. Export the admitted CCR and pin its SHA256 before creating the
|
||||
node file.
|
||||
1. Founder review is recorded: platform-owner and mas-owner, source
|
||||
`founder-approval-2026-11-02`. The live account matches the binding.
|
||||
2. Apply the policy and role with the attended platform-admin login:
|
||||
`BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 warden access openbao-platform-admin-login --exec -- /home/worsch/railiance-platform/scripts/install-mas-backup-primary-reader.sh`
|
||||
Sign in as platform-root. The helper is silent and writes only a receipt.
|
||||
3. In the Scaleway console, create a new IAM application named
|
||||
`mas-evidence-backup-primary`. Do not add a key to email-connect or to a
|
||||
broad application. Then say that the application exists. This workstation
|
||||
has no Scaleway credential, so it cannot create the application or its key.
|
||||
4. Add three bucket-policy statements for that application, limited to
|
||||
`platform-pg/application-archives/mas/`, with no object delete. Prove sibling
|
||||
denial, retained-delete denial, and no IAM or bucket administration.
|
||||
5. Provision the pair atomically with KV CAS=0 under the pair-writer policy
|
||||
extended to this one path. The single-field paste desk is unsuitable.
|
||||
6. Record positive reader proof and negative denials, then set delivery
|
||||
admission. Export the admitted CCR and pin its SHA256 before creating
|
||||
`/etc/mas-backup/primary.env`.
|
||||
|
||||
Age-identity escrow is a separate custody act. This grant does not move the
|
||||
workstation private key, and the node must not hold it. TEM and SMTP
|
||||
|
|
|
|||
128
scripts/install-mas-backup-primary-reader.sh
Executable file
128
scripts/install-mas-backup-primary-reader.sh
Executable file
|
|
@ -0,0 +1,128 @@
|
|||
#!/bin/sh
|
||||
# Install the MAS backup-primary reader policy and Kubernetes auth role.
|
||||
# Silent: warden access rejects any child output. Receipts stay on absolute paths.
|
||||
# This does not create a provider key, write a KV value, or create a node file.
|
||||
set -eu
|
||||
export BAO_ADDR="${BAO_ADDR:-http://127.0.0.1:18200}"
|
||||
export VAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:18200}"
|
||||
receipt_dir=/home/worsch/.local/state/railiance/receipts
|
||||
umask 077
|
||||
mkdir -p "$receipt_dir"
|
||||
stage="$receipt_dir/mas-backup-primary-reader.stage"
|
||||
: >"$stage"
|
||||
python3 - "$stage" <<'PY'
|
||||
import importlib.util
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
stage = Path(sys.argv[1])
|
||||
root = Path("/home/worsch/railiance-platform")
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"credential_change", root / "scripts/credential-change.py"
|
||||
)
|
||||
cc = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(cc)
|
||||
|
||||
name = "CCR-2026-0062-mas-backup-primary.yaml"
|
||||
safe_role_keys = (
|
||||
"audience",
|
||||
"bound_service_account_names",
|
||||
"bound_service_account_namespaces",
|
||||
"policies",
|
||||
"token_explicit_max_ttl",
|
||||
"token_max_ttl",
|
||||
"token_no_default_policy",
|
||||
"token_num_uses",
|
||||
"token_policies",
|
||||
"token_type",
|
||||
"ttl",
|
||||
)
|
||||
|
||||
def run(argv):
|
||||
result = subprocess.run(argv, cwd=root, capture_output=True)
|
||||
if result.returncode != 0:
|
||||
stage.write_text(f"exit {result.returncode} step {argv[1]}\n", encoding="utf-8")
|
||||
raise SystemExit(result.returncode or 1)
|
||||
return result.stdout.decode()
|
||||
|
||||
ccr = json.loads((root / "credential-change-requests" / name).read_text(encoding="utf-8"))
|
||||
if ccr.get("status") != "approved" or ccr["openbao"]["auth"].get("bound_claims_confirmed") is not True:
|
||||
stage.write_text(f"{ccr.get('id')} not approved for apply\n", encoding="utf-8")
|
||||
raise SystemExit(44)
|
||||
blockers = cc.applier_readiness_blockers(ccr)
|
||||
if blockers:
|
||||
stage.write_text(ccr["id"] + " blocked\n", encoding="utf-8")
|
||||
raise SystemExit(45)
|
||||
openbao = ccr["openbao"]
|
||||
auth = openbao["auth"]
|
||||
policy_path = root / openbao["policy_file"]
|
||||
run(["bao", "policy", "write", openbao["policy_name"], str(policy_path)])
|
||||
installed = run(["bao", "policy", "read", openbao["policy_name"]])
|
||||
if cc.normalized_policy_body(installed) != cc.normalized_policy_body(policy_path.read_text(encoding="utf-8")):
|
||||
stage.write_text(openbao["policy_name"] + " readback differs\n", encoding="utf-8")
|
||||
raise SystemExit(46)
|
||||
payload = cc.auth_payload(ccr)
|
||||
role_file = Path(f"/home/worsch/.local/state/railiance/receipts/{auth['role']}.payload.json")
|
||||
role_file.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
role_file.chmod(0o600)
|
||||
run(["bao", "write", f"auth/{auth['mount']}/role/{auth['role']}", f"@{role_file}"])
|
||||
raw = run(["bao", "read", "-format=json", f"auth/{auth['mount']}/role/{auth['role']}"])
|
||||
data = json.loads(raw).get("data", {})
|
||||
names = data.get("bound_service_account_names")
|
||||
namespaces = data.get("bound_service_account_namespaces")
|
||||
policies = data.get("token_policies") or data.get("policies")
|
||||
audience = data.get("audience") or data.get("audiences")
|
||||
problems = []
|
||||
if names != payload["bound_service_account_names"]:
|
||||
problems.append("names")
|
||||
if namespaces != payload["bound_service_account_namespaces"]:
|
||||
problems.append("namespaces")
|
||||
if policies != payload["policies"].split(","):
|
||||
problems.append("policies")
|
||||
if audience not in (payload.get("audience"), [payload.get("audience")]):
|
||||
problems.append("audience")
|
||||
if data.get("token_no_default_policy") is not True:
|
||||
problems.append("no_default")
|
||||
if data.get("token_num_uses") != 4:
|
||||
problems.append("uses")
|
||||
if data.get("token_type") != "service":
|
||||
problems.append("type")
|
||||
if data.get("ttl") not in (300, "5m", "300s"):
|
||||
problems.append("ttl")
|
||||
if data.get("token_max_ttl") not in (300, "5m", "300s"):
|
||||
problems.append("max_ttl")
|
||||
if data.get("token_explicit_max_ttl") not in (300, "5m", "300s"):
|
||||
problems.append("explicit_max_ttl")
|
||||
if problems:
|
||||
observed = {key: data.get(key) for key in safe_role_keys if key in data}
|
||||
stage.write_text(
|
||||
json.dumps({"role": auth["role"], "problems": problems, "observed": observed}, indent=2)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
raise SystemExit(47)
|
||||
receipt = {
|
||||
"status": "installed",
|
||||
"id": ccr["id"],
|
||||
"policy": openbao["policy_name"],
|
||||
"role": auth["role"],
|
||||
"policies": policies,
|
||||
"bound_service_account_names": names,
|
||||
"bound_service_account_namespaces": namespaces,
|
||||
"ttl": data.get("ttl"),
|
||||
"token_max_ttl": data.get("token_max_ttl"),
|
||||
"token_explicit_max_ttl": data.get("token_explicit_max_ttl"),
|
||||
"token_num_uses": data.get("token_num_uses"),
|
||||
"token_no_default_policy": data.get("token_no_default_policy"),
|
||||
"token_type": data.get("token_type"),
|
||||
}
|
||||
out = Path("/home/worsch/.local/state/railiance/receipts/mas-backup-primary-reader.json")
|
||||
out.write_text(json.dumps(receipt, indent=2) + "\n", encoding="utf-8")
|
||||
out.chmod(0o600)
|
||||
PY
|
||||
if [ -s "$stage" ]; then
|
||||
exit 48
|
||||
fi
|
||||
exit 0
|
||||
|
|
@ -40,7 +40,7 @@ status: progress
|
|||
priority: medium
|
||||
needs_human: true
|
||||
depends_on: [RPF-WP-0053-T05]
|
||||
intervention_note: "Draft CCR-2026-0062 prepares the MAS prefix publisher. It is not approved or applied. Age-identity escrow is still required before offsite publication."
|
||||
intervention_note: "Founder approved CCR-2026-0062. The delivery account exists. OpenBao policy/role install still needs the attended platform-admin login, and the IAM application is not created. Age-identity escrow remains separate."
|
||||
state_hub_task_id: "4f6484fb-8303-5dd5-94cb-b2622e1322c8"
|
||||
```
|
||||
|
||||
|
|
@ -61,12 +61,14 @@ The age identity is only on the operator workstation. It is not escrowed,
|
|||
and nothing was published to `platform-pg/application-archives/mas/`.
|
||||
The profile is accepted by `scripts/primary_application_archive.py`.
|
||||
|
||||
2026-11-02: the prefix publisher is prepared as draft `CCR-2026-0062`.
|
||||
It names `platform/workloads/mas/backup-primary`, policy
|
||||
`workload-kv-read-mas-backup-primary`, ServiceAccount
|
||||
`backup-primary-delivery` in namespace `mas`, and delivery at
|
||||
`/etc/mas-backup/primary.env`. Bindings are unconfirmed and delivery
|
||||
admission is false. The IAM application, OpenBao value, live
|
||||
ServiceAccount, and node file are not created. Pair-custody and
|
||||
slot-preparation lists still name only coulomb-social and email-connect.
|
||||
2026-11-02: the founder approved `CCR-2026-0062` as platform-owner and
|
||||
mas-owner. ServiceAccount `backup-primary-delivery` now exists in
|
||||
namespace `mas` with automount disabled and no RoleBinding. The binding
|
||||
is confirmed. Delivery admission stays false. The OpenBao policy and
|
||||
role are not installed: the local token is denied, so the install waits
|
||||
for `warden access openbao-platform-admin-login --exec` running
|
||||
`scripts/install-mas-backup-primary-reader.sh`. No Scaleway credential
|
||||
is configured here, so IAM application `mas-evidence-backup-primary`
|
||||
is not created. The KV pair and `/etc/mas-backup/primary.env` do not
|
||||
exist. Pair-custody lists still name only coulomb-social and email-connect.
|
||||
Design: `docs/credential-lane-designs/mas-evidence-backup-grant-2026-11-02.md`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue