Seed tenant-engine sender token into its dedicated KV path (MASON-WP-0007 row 7)
Attended, silent, check-and-set. Evidence 2026-10-02: created, version 1, no values. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 292700@bnt-lap001 Assistant-Session: 42de6b79-9019-4cfd-b49a-6db3bbfe9341
This commit is contained in:
parent
f0dff91eb5
commit
e7ee6d0694
4 changed files with 308 additions and 0 deletions
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"step": "done",
|
||||
"observed_at": "2026-10-02T22:44:39+00:00",
|
||||
"sender": "tenant-engine",
|
||||
"source": "platform/workloads/audit-core/senders",
|
||||
"destination": "platform/workloads/tenant-engine/audit-core-sender",
|
||||
"credential_values_emitted": false,
|
||||
"registry_version": 11,
|
||||
"destination_version": 1,
|
||||
"action": "created",
|
||||
"field": "token"
|
||||
}
|
||||
13
scripts/attended-seed-tenant-engine-audit-core-sender.sh
Executable file
13
scripts/attended-seed-tenant-engine-audit-core-sender.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
# Parent-env wrapper for the attended seed of the tenant-engine sender token
|
||||
# into platform/workloads/tenant-engine/audit-core-sender (field token).
|
||||
# MASON-WP-0007 row 7 / TEN-IN-0005. Founder-run; no agent executes this.
|
||||
#
|
||||
# Operator login is the named tunnel openbao-ui-railiance01 ->
|
||||
# http://127.0.0.1:18200. railiance-platform's openbao-attended-exec.py pins
|
||||
# that address for both OIDC and the child. Run `bridge status` first.
|
||||
# Read the line warden prints, not the exit code.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
exec python3 /home/worsch/railiance-platform/scripts/openbao-attended-exec.py -- \
|
||||
"$PWD/scripts/seed-tenant-engine-audit-core-sender.py"
|
||||
180
scripts/seed-tenant-engine-audit-core-sender.py
Executable file
180
scripts/seed-tenant-engine-audit-core-sender.py
Executable file
|
|
@ -0,0 +1,180 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Attended seed of the tenant-engine sender token into its dedicated KV path.
|
||||
|
||||
ops-mason MASON-WP-0007 / tenant-engine TEN-IN-0005, plan row 7. The registry
|
||||
document holds every sender, and OpenBao policies scope by path, not by field,
|
||||
so External Secrets reads a one-field copy instead:
|
||||
|
||||
source: platform/data/workloads/audit-core/senders (sender "tenant-engine")
|
||||
destination: platform/data/workloads/tenant-engine/audit-core-sender (field "token")
|
||||
|
||||
Read and write happen in this one process. The token is never printed, never
|
||||
passed on a command line, never written to evidence; evidence holds versions,
|
||||
booleans and counts only. Silent: warden access --exec fails closed on any
|
||||
child stdout/stderr.
|
||||
|
||||
./scripts/attended-seed-tenant-engine-audit-core-sender.sh
|
||||
|
||||
Re-running is safe. Equal value: no write. Different value (rotation re-seed):
|
||||
one check-and-set write against the version just read. No destination: cas 0.
|
||||
When the registry holds several tokens for the sender (rotation overlap), the
|
||||
last one is seeded, which is the newest appended.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
REGISTRY = "platform/data/workloads/audit-core/senders"
|
||||
SENDER = "tenant-engine"
|
||||
DEST_DATA = "platform/data/workloads/tenant-engine/audit-core-sender"
|
||||
DEST_META = "platform/metadata/workloads/tenant-engine/audit-core-sender"
|
||||
FIELD = "token"
|
||||
EVIDENCE_DIR = Path("/home/worsch/audit-core/docs/evidence")
|
||||
|
||||
|
||||
class NotFound(Exception):
|
||||
"""The path holds no value. Distinct from a failed call."""
|
||||
|
||||
|
||||
def _now() -> str:
|
||||
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
||||
|
||||
|
||||
def evidence_path() -> Path:
|
||||
return EVIDENCE_DIR / f"{_now()[:10]}-tenant-engine-audit-core-sender-seed.json"
|
||||
|
||||
|
||||
def _evidence(step: str, extra: dict | None = None) -> None:
|
||||
doc = {
|
||||
"step": step,
|
||||
"observed_at": _now(),
|
||||
"sender": SENDER,
|
||||
"source": "platform/workloads/audit-core/senders",
|
||||
"destination": "platform/workloads/tenant-engine/audit-core-sender",
|
||||
"credential_values_emitted": False,
|
||||
}
|
||||
if extra:
|
||||
doc.update(extra)
|
||||
path = evidence_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(doc, indent=2) + "\n")
|
||||
|
||||
|
||||
def _fail(reason: str, extra: dict | None = None) -> int:
|
||||
payload = {"reason": reason}
|
||||
if extra:
|
||||
payload.update(extra)
|
||||
_evidence("failed", payload)
|
||||
return 1
|
||||
|
||||
|
||||
def bao(args: list[str], *, payload: dict | None = None) -> dict:
|
||||
env = os.environ.copy()
|
||||
for name in ("BAO_TOKEN", "VAULT_TOKEN", "OPENBAO_TOKEN"):
|
||||
env.pop(name, None)
|
||||
proc = subprocess.run(
|
||||
["bao", *args],
|
||||
input=None if payload is None else json.dumps(payload).encode(),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=env,
|
||||
check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
# stderr is inspected, never emitted.
|
||||
err = proc.stderr.decode(errors="replace").lower()
|
||||
if "no value found" in err or "404" in err:
|
||||
raise NotFound()
|
||||
raise RuntimeError("bao_failed")
|
||||
return json.loads(proc.stdout.decode() or "{}")
|
||||
|
||||
|
||||
def pick_token(rows: list) -> str:
|
||||
"""The sender's newest token. Raises ValueError with a reason, never a value."""
|
||||
row = next((r for r in rows if isinstance(r, dict) and r.get("name") == SENDER), None)
|
||||
if row is None:
|
||||
raise ValueError("sender_not_in_registry")
|
||||
tokens = [t for t in (row.get("tokens") or []) if isinstance(t, str) and t]
|
||||
if not tokens:
|
||||
raise ValueError("sender_has_no_tokens")
|
||||
return tokens[-1]
|
||||
|
||||
|
||||
def seed(token: str, call=bao) -> dict:
|
||||
"""Write token to the destination if needed. Returns metadata only."""
|
||||
try:
|
||||
current = call(["read", "-format=json", DEST_DATA])["data"]
|
||||
version = current["metadata"]["version"]
|
||||
existing = (current.get("data") or {}).get(FIELD)
|
||||
except NotFound:
|
||||
version, existing = 0, None
|
||||
if existing == token:
|
||||
return {"action": "already_seeded", "version": version}
|
||||
call(
|
||||
["write", "-format=json", DEST_DATA, "-"],
|
||||
payload={"options": {"cas": version}, "data": {FIELD: token}},
|
||||
)
|
||||
after = call(["read", "-format=json", DEST_DATA])["data"]
|
||||
if (after.get("data") or {}).get(FIELD) != token:
|
||||
raise RuntimeError("readback_mismatch")
|
||||
return {
|
||||
"action": "created" if version == 0 else "reseeded",
|
||||
"version": after["metadata"]["version"],
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
_evidence("started")
|
||||
if Path.home().parent.name != ".warden-attended-login":
|
||||
return _fail("attended_warden_envelope_required")
|
||||
if os.environ.get("BAO_TOKEN") or os.environ.get("VAULT_TOKEN"):
|
||||
return _fail("token_env_must_be_unset")
|
||||
try:
|
||||
identity = bao(["token", "lookup", "-format=json"])["data"]
|
||||
except Exception:
|
||||
return _fail("token_lookup_failed")
|
||||
policies = identity.get("policies") or []
|
||||
if "platform-admin" not in policies or "root" in policies:
|
||||
return _fail("attended_platform_admin_required")
|
||||
|
||||
try:
|
||||
raw = bao(["read", "-format=json", REGISTRY])["data"]
|
||||
registry_version = raw["metadata"]["version"]
|
||||
rows = json.loads(raw["data"]["senders.json"])
|
||||
except Exception:
|
||||
return _fail("registry_read_failed")
|
||||
try:
|
||||
token = pick_token(rows)
|
||||
except ValueError as exc:
|
||||
return _fail(str(exc), {"registry_version": registry_version})
|
||||
_evidence("registry_read", {"registry_version": registry_version})
|
||||
|
||||
try:
|
||||
result = seed(token)
|
||||
except Exception as exc:
|
||||
return _fail("seed_failed", {"error_class": exc.__class__.__name__})
|
||||
_evidence(
|
||||
"done",
|
||||
{
|
||||
"registry_version": registry_version,
|
||||
"destination_version": result["version"],
|
||||
"action": result["action"],
|
||||
"field": FIELD,
|
||||
},
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception:
|
||||
_fail("contained_operation_failed")
|
||||
raise SystemExit(1)
|
||||
103
tests/test_seed_tenant_engine_audit_core_sender.py
Normal file
103
tests/test_seed_tenant_engine_audit_core_sender.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
"""Seed script for the tenant-engine sender token (MASON-WP-0007 row 7).
|
||||
|
||||
Uses a fake OpenBao and obviously fake token strings. No real credential.
|
||||
"""
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPT = Path(__file__).parents[1] / "scripts" / "seed-tenant-engine-audit-core-sender.py"
|
||||
SPEC = importlib.util.spec_from_file_location("seed_tenant_engine_sender", SCRIPT)
|
||||
SEED = importlib.util.module_from_spec(SPEC)
|
||||
sys.modules[SPEC.name] = SEED
|
||||
SPEC.loader.exec_module(SEED)
|
||||
|
||||
FAKE_OLD = "fake-token-old-not-a-secret"
|
||||
FAKE_NEW = "fake-token-new-not-a-secret"
|
||||
|
||||
|
||||
class FakeBao:
|
||||
"""Minimal KV v2 behaviour: cas enforcement, versions, NotFound."""
|
||||
|
||||
def __init__(self, existing=None):
|
||||
self.version = 0
|
||||
self.value = None
|
||||
self.writes = []
|
||||
if existing is not None:
|
||||
self.version, self.value = 1, existing
|
||||
|
||||
def __call__(self, args, *, payload=None):
|
||||
path = args[-2] if args[0] == "write" else args[-1]
|
||||
assert path == SEED.DEST_DATA
|
||||
if args[0] == "read":
|
||||
if self.version == 0:
|
||||
raise SEED.NotFound()
|
||||
return {"data": {"metadata": {"version": self.version}, "data": {"token": self.value}}}
|
||||
assert args[0] == "write"
|
||||
assert payload["options"]["cas"] == self.version, "cas must match the version read"
|
||||
self.writes.append(payload)
|
||||
self.version += 1
|
||||
self.value = payload["data"]["token"]
|
||||
return {}
|
||||
|
||||
|
||||
def test_pick_token_takes_newest_and_rejects_missing():
|
||||
rows = [
|
||||
{"name": "other", "tokens": ["x"]},
|
||||
{"name": "tenant-engine", "tokens": [FAKE_OLD, FAKE_NEW]},
|
||||
]
|
||||
assert SEED.pick_token(rows) == FAKE_NEW
|
||||
with pytest.raises(ValueError, match="sender_not_in_registry"):
|
||||
SEED.pick_token([{"name": "other", "tokens": ["x"]}])
|
||||
with pytest.raises(ValueError, match="sender_has_no_tokens"):
|
||||
SEED.pick_token([{"name": "tenant-engine", "tokens": []}])
|
||||
|
||||
|
||||
def test_creates_with_cas_zero():
|
||||
fake = FakeBao()
|
||||
result = SEED.seed(FAKE_NEW, call=fake)
|
||||
assert result == {"action": "created", "version": 1}
|
||||
assert fake.writes[0]["options"]["cas"] == 0
|
||||
assert fake.writes[0]["data"] == {"token": FAKE_NEW}
|
||||
|
||||
|
||||
def test_rerun_with_same_value_does_not_write():
|
||||
fake = FakeBao(existing=FAKE_NEW)
|
||||
assert SEED.seed(FAKE_NEW, call=fake) == {"action": "already_seeded", "version": 1}
|
||||
assert fake.writes == []
|
||||
|
||||
|
||||
def test_rotation_reseeds_against_current_version():
|
||||
fake = FakeBao(existing=FAKE_OLD)
|
||||
assert SEED.seed(FAKE_NEW, call=fake) == {"action": "reseeded", "version": 2}
|
||||
assert fake.value == FAKE_NEW
|
||||
|
||||
|
||||
def test_readback_mismatch_fails():
|
||||
class Lossy(FakeBao):
|
||||
def __call__(self, args, *, payload=None):
|
||||
out = super().__call__(args, payload=payload)
|
||||
if args[0] == "read" and self.writes:
|
||||
out["data"]["data"]["token"] = "something-else"
|
||||
return out
|
||||
|
||||
with pytest.raises(RuntimeError, match="readback_mismatch"):
|
||||
SEED.seed(FAKE_NEW, call=Lossy())
|
||||
|
||||
|
||||
def test_evidence_never_contains_a_token(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(SEED, "EVIDENCE_DIR", tmp_path)
|
||||
SEED._evidence("done", {"destination_version": 1, "action": "created", "field": "token"})
|
||||
SEED._fail("seed_failed", {"error_class": "RuntimeError"})
|
||||
text = "".join(p.read_text() for p in tmp_path.iterdir())
|
||||
assert FAKE_NEW not in text and FAKE_OLD not in text
|
||||
assert json.loads(next(tmp_path.iterdir()).read_text())["credential_values_emitted"] is False
|
||||
|
||||
|
||||
def test_source_never_prints_or_passes_the_token_on_a_command_line():
|
||||
src = SCRIPT.read_text()
|
||||
assert "print(" not in src
|
||||
assert "sys.stdout" not in src and "sys.stderr" not in src
|
||||
Loading…
Add table
Add a link
Reference in a new issue