RPF-WP-0015-T02: contained-login only, single silent attended window

Harness used the retracted public OpenBao URL and a token file; lane used a
standing token file. Both now run only inside the contained ops-warden login
(tunnel, helper token), and t02-attended-window.py runs project, exercise and
cleanup under one login without printing (warden fails closed on child output).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 680842@bnt-lap001
Assistant-Session: 54eb9e8e-7d6b-4f0c-b7a6-a6db62075883
This commit is contained in:
codex 2026-10-14 14:23:46 +02:00
parent badb91c71a
commit 311aa86eef
10 changed files with 773 additions and 109 deletions

View file

@ -14,7 +14,6 @@ import hashlib
import importlib.util
import json
import os
import stat
import subprocess
import sys
import time
@ -122,14 +121,41 @@ def safe_run(
return completed
# Operator OpenBao access is the SSH tunnel; the public endpoint was retracted
# 2026-09-15 (RMASTER-WP-0020-T09).
TUNNEL_ADDR = "http://127.0.0.1:18200"
CONTAINMENT_MARKER = "WARDEN_ROUTING_CATALOG"
TOKEN_ENV = ("BAO_TOKEN", "VAULT_TOKEN", "OPENBAO_TOKEN")
def contained_bao_env(environ: dict[str, str] | None = None) -> dict[str, str]:
"""Environment for ``bao`` inside the contained attended login only.
``scripts/openbao-attended-exec.py`` runs ``warden access
openbao-platform-admin-login --exec`` with BAO_ADDR pinned to the tunnel,
no token variable, and the Warden routing marker; ``bao`` then
authenticates through Warden's isolated token helper. Anything else --
a token file, a token variable, or another address -- is refused.
"""
env = dict(os.environ if environ is None else environ)
if not env.get(CONTAINMENT_MARKER):
raise ProcedureError(
"live OpenBao access requires the contained attended login "
"(scripts/openbao-attended-exec.py)"
)
if env.get("BAO_ADDR", TUNNEL_ADDR) != TUNNEL_ADDR:
raise ProcedureError(f"BAO_ADDR must be the operator tunnel {TUNNEL_ADDR}")
if any(env.get(name) for name in TOKEN_ENV):
raise ProcedureError("token variables must be unset inside the contained login")
for name in TOKEN_ENV:
env.pop(name, None)
env["BAO_ADDR"] = TUNNEL_ADDR
return env
class Authority:
def __init__(self, token_file: Path) -> None:
if not token_file.is_file() or stat.S_IMODE(token_file.stat().st_mode) != 0o600:
raise ProcedureError("OpenBao token file must exist with mode 0600")
token = token_file.read_text(encoding="utf-8").splitlines()[0].strip()
if not token:
raise ProcedureError("OpenBao token file is empty")
self.env = dict(os.environ, BAO_ADDR="https://bao.coulomb.social", BAO_TOKEN=token)
def __init__(self) -> None:
self.env = contained_bao_env()
def bao(self, args: list[str], *, label: str) -> str:
return safe_run(["bao", *args], label=label, env=self.env).stdout.strip()
@ -333,7 +359,7 @@ def exercise(args: argparse.Namespace) -> dict[str, Any]:
if args.confirm != CONFIRM:
raise ProcedureError(f"live exercise requires --confirm {CONFIRM}")
remote = PREFLIGHT.Remote(args.remote)
authority = Authority(args.token_file)
authority = Authority()
driver = LoadDriver(
args.load_driver,
approval["synthetic_load"]["contract_id"],
@ -450,18 +476,30 @@ def exercise(args: argparse.Namespace) -> dict[str, Any]:
}
def main() -> int:
def emit_result(result: dict, result_file: "Path | None") -> None:
"""Under the contained login the child must stay silent: write, never print."""
if result_file is None:
print(json.dumps(result, indent=2, sort_keys=True))
return
fd = os.open(result_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(result, handle, indent=2, sort_keys=True)
handle.write("\n")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=["validate-approval", "exercise"])
parser.add_argument("--approval", type=Path, required=True)
parser.add_argument("--remote", default="railiance01")
parser.add_argument(
"--token-file", type=Path,
default=Path.home() / ".local/openbao/platform-admin.token",
"--contained", action="store_true",
help="required for exercise: run inside the contained attended OpenBao login",
)
parser.add_argument("--load-driver", type=Path)
parser.add_argument("--confirm")
args = parser.parse_args()
parser.add_argument("--result-file", type=Path, help="write the JSON result here (0600, new) and print nothing")
args = parser.parse_args(argv)
try:
if args.command == "validate-approval":
approval = load_approval(args.approval, datetime.now(UTC), require_open_window=False)
@ -472,13 +510,18 @@ def main() -> int:
"secret_values_observed": False,
}
else:
if not args.contained:
raise ProcedureError("live exercise requires --contained (scripts/openbao-attended-exec.py)")
if args.load_driver is None:
raise ProcedureError("live exercise requires --load-driver")
result = exercise(args)
except (OSError, IndexError, ProcedureError, PREFLIGHT.PreflightError) as exc:
print(f"database lease recovery failed: {exc}", file=sys.stderr)
if args.result_file is None:
print(f"database lease recovery failed: {exc}", file=sys.stderr)
else:
emit_result({"status": "failed", "error": str(exc), "secret_values_observed": False}, args.result_file)
return 1
print(json.dumps(result, indent=2, sort_keys=True))
emit_result(result, args.result_file)
return 0

54
scripts/audit_core_synthetic_sender_lane.py Normal file → Executable file
View file

@ -262,6 +262,24 @@ def settled_state(remote: Any, *, previous_uid: str | None, timeout: float = 90)
time.sleep(3)
class ContainedOperator(CREDS.Operator):
"""The -02 registry/kubectl helpers, authenticated only by the contained login.
No token file and no token variable: ``bao`` runs with the tunnel address
and Warden's isolated token helper (see ``LEASE.contained_bao_env``).
"""
def __init__(self, remote: str) -> None: # noqa: D107 - deliberately no super().__init__
self.remote = remote
self.bao_env = LEASE.contained_bao_env()
def contained_operator(args: argparse.Namespace) -> Any:
if not args.contained:
raise LaneError(f"{args.command} requires --contained (scripts/openbao-attended-exec.py)")
return ContainedOperator(args.remote)
def registry_metadata(operator: Any) -> dict[str, Any]:
raw = operator.bao(["kv", "metadata", "get", "-format=json", REGISTRY_PATH], label="read sender registry metadata").stdout
try:
@ -303,9 +321,16 @@ def preflight(args: argparse.Namespace) -> dict[str, Any]:
if args.offline:
result["live_checks"] = "skipped"
return result
if args.inspect_registry and not args.contained:
raise LaneError("--inspect-registry requires --contained")
remote = PREFLIGHT.Remote(args.remote)
result["audit_core"] = cluster_state(remote)
operator = CREDS.Operator(args.remote, args.token_file)
result["automated_checks_passed"] = result["audit_core"]["automated_checks_passed"]
if not args.contained:
# Outside the attended login there is no OpenBao credential at all.
result["sender_registry_metadata"] = "skipped-not-contained"
return result
operator = contained_operator(args)
result["sender_registry_metadata"] = registry_metadata(operator)
if args.inspect_registry:
# Attended only: values are held in memory and never emitted.
@ -316,7 +341,6 @@ def preflight(args: argparse.Namespace) -> dict[str, Any]:
)
result["registry_conflicts"] = registry_conflicts(registry, spec)
registry.clear()
result["automated_checks_passed"] = result["audit_core"]["automated_checks_passed"]
return result
@ -334,6 +358,7 @@ def project(args: argparse.Namespace) -> dict[str, Any]:
approval, spec = load_receipt(args.approval, now, require_open_window=True)
if args.confirm != confirm_token(approval):
raise LaneError(f"project requires --confirm {confirm_token(approval)}")
operator = contained_operator(args)
assert_projection_time(approval, spec, now)
if args.bearer_file is None:
raise LaneError("project requires --bearer-file outside Git")
@ -343,7 +368,6 @@ def project(args: argparse.Namespace) -> dict[str, Any]:
if not before["automated_checks_passed"]:
raise LaneError("audit-core recovery preflight is not green; refusing to project")
LEASE.attest_chain_state(remote)
operator = CREDS.Operator(args.remote, args.token_file)
registry = operator.registry()
conflicts = registry_conflicts(registry, spec)
if conflicts:
@ -384,8 +408,8 @@ def cleanup(args: argparse.Namespace) -> dict[str, Any]:
approval, spec = load_receipt(args.approval, utc_now(), require_open_window=False)
if args.confirm != confirm_token(approval):
raise LaneError(f"cleanup requires --confirm {confirm_token(approval)}")
operator = contained_operator(args)
gate = assert_evidence(args.exercise_evidence, args.abort_reason, approval)
operator = CREDS.Operator(args.remote, args.token_file)
removed = _cleanup_registry(operator, spec)
remaining = any(
isinstance(entry, dict) and entry.get("name") == spec["name"] for entry in operator.registry()
@ -412,26 +436,42 @@ def cleanup(args: argparse.Namespace) -> dict[str, Any]:
}
def emit_result(result: dict, result_file: "Path | None") -> None:
"""Under the contained login the child must stay silent: write, never print."""
if result_file is None:
print(json.dumps(result, indent=2, sort_keys=True))
return
fd = os.open(result_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(result, handle, indent=2, sort_keys=True)
handle.write("\n")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("command", choices=["preflight", "project", "cleanup"])
parser.add_argument("--approval", type=Path, required=True, help="approved T02 window receipt")
parser.add_argument("--bearer-file", type=Path, help="absolute path outside Git (mode-0700 directory)")
parser.add_argument("--remote", default="railiance01")
parser.add_argument("--token-file", type=Path, default=Path.home() / ".local/openbao/platform-admin.token")
parser.add_argument("--contained", action="store_true",
help="run inside the contained attended OpenBao login (required for project/cleanup)")
parser.add_argument("--confirm")
parser.add_argument("--offline", action="store_true", help="preflight: validate receipt/path only")
parser.add_argument("--inspect-registry", action="store_true", help="preflight: attended registry conflict check")
parser.add_argument("--exercise-evidence", type=Path, help="cleanup: recorded T02 exercise result JSON")
parser.add_argument("--abort-reason", help="cleanup: abort path when no exercise evidence exists")
parser.add_argument("--result-file", type=Path, help="write the JSON result here (0600, new) and print nothing")
args = parser.parse_args(argv)
try:
handler = {"preflight": preflight, "project": project, "cleanup": cleanup}[args.command]
result = handler(args)
except ERRORS as exc:
print(f"synthetic sender lane failed: {exc}", file=sys.stderr)
if args.result_file is None:
print(f"synthetic sender lane failed: {exc}", file=sys.stderr)
else:
emit_result({"status": "failed", "error": str(exc), "secret_values_observed": False}, args.result_file)
return 1
print(json.dumps(result, indent=2, sort_keys=True))
emit_result(result, args.result_file)
return 0

274
scripts/t02-attended-window.py Executable file
View file

@ -0,0 +1,274 @@
#!/usr/bin/env python3
"""One contained attended window for RAILIANCE-WP-0024-T02.
Run only as the single child of the contained attended login, so the whole
window costs one MFA login:
python3 scripts/openbao-attended-exec.py -- scripts/t02-attended-window.py ...
Order (each step a reviewed command; this file adds no OpenBao call of its own):
1. lane ``project`` -- one temporary sender, bearer to a 0600 file, audit-core restart
2. port-forward -- ``ssh -L`` to ``kubectl -n audit-core port-forward`` on the remote
3. harness ``exercise`` with the registered load driver
4. evidence -- exercise JSON written 0600 outside Git
5. lane ``cleanup`` -- with that evidence (restart #2)
Any failure after projection was attempted runs ``cleanup --abort-reason``.
The port-forward is always stopped. Children's stdout is captured and only
their value-safe JSON is re-emitted; nothing here reads the bearer.
"""
from __future__ import annotations
import argparse
import importlib.util
import json
import os
import re
import subprocess
import sys
import time
import urllib.error
import urllib.request
from datetime import UTC, datetime
from pathlib import Path
from typing import Any, Callable
ROOT = Path(__file__).resolve().parents[1]
LANE_SCRIPT = ROOT / "scripts" / "audit_core_synthetic_sender_lane.py"
HARNESS_SCRIPT = ROOT / "scripts" / "audit-core-database-lease-recovery.py"
HARNESS_CONFIRM = "RAILIANCE-WP-0024-T02:attended"
REMOTE_PF_TIMEOUT_SECONDS = 15 * 60
CONTAINER_PORT = 8080
class WindowError(RuntimeError):
pass
def _load_lane() -> Any:
spec = importlib.util.spec_from_file_location("audit_core_synthetic_sender_lane", LANE_SCRIPT)
if not spec or not spec.loader:
raise WindowError("cannot load synthetic sender lane")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
LANE = _load_lane()
def stamp(value: datetime) -> str:
return value.astimezone(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def run_json(command: list[str], *, label: str, env: dict[str, str] | None = None) -> dict[str, Any]:
"""Run a value-safe reviewed child; capture both streams (warden fails on any output)."""
completed = subprocess.run(
command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, check=False
)
if completed.returncode != 0:
# Reviewed lane/harness errors are fixed value-safe strings; keep one bounded line.
reason = (completed.stderr.strip().splitlines() or [""])[-1][:200]
raise WindowError(f"{label} failed (exit {completed.returncode}): {reason}")
try:
payload = json.loads(completed.stdout)
except json.JSONDecodeError as exc:
raise WindowError(f"{label} returned invalid JSON") from exc
if not isinstance(payload, dict) or payload.get("secret_values_observed") is not False:
raise WindowError(f"{label} did not attest value safety")
return payload
def private_dir(path: Path) -> Path:
"""An operator-owned 0700 directory outside every Git work tree."""
path = path.resolve()
LANE.check_bearer_path(path / "probe", must_exist=False)
return path
def write_private_json(path: Path, document: dict[str, Any]) -> None:
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(document, handle, indent=2, sort_keys=True)
handle.write("\n")
def slug(value: str) -> str:
cleaned = re.sub(r"[^a-z0-9-]+", "-", value.lower()).strip("-")
if not cleaned:
raise WindowError("approval id cannot form a fixture id")
return cleaned[:48]
def driver_env(spec: dict[str, Any], approval: dict[str, Any], bearer: Path, port: int) -> dict[str, str]:
env = dict(os.environ)
env.update({
"AUDIT_T02_BASE_URL": f"http://127.0.0.1:{port}",
"AUDIT_T02_TOKEN_FILE": str(bearer),
"AUDIT_T02_SOURCE": spec["source"],
"AUDIT_T02_TENANT": spec["tenant"],
"AUDIT_T02_FIXTURE_ID": f"t02-{slug(approval['approval_id'])}",
"AUDIT_T02_OCCURRED_AT": approval["window"]["start"],
})
return env
class PortForward:
"""``ssh -L`` to a remote, time-bounded ``kubectl port-forward`` on loopback.
The lane and harness reach the cluster only through ``ssh <remote> kubectl``;
the port-forward uses the same path so the window needs no local kubeconfig.
"""
def __init__(self, remote: str, port: int) -> None:
self.remote = remote
self.port = port
self.process: subprocess.Popen[bytes] | None = None
def command(self) -> list[str]:
remote_cmd = (
f"timeout {REMOTE_PF_TIMEOUT_SECONDS} kubectl -n audit-core port-forward "
f"--address 127.0.0.1 deploy/audit-core {self.port}:{CONTAINER_PORT}"
)
return [
"ssh", "-o", "BatchMode=yes", "-o", "ExitOnForwardFailure=yes",
"-L", f"127.0.0.1:{self.port}:127.0.0.1:{self.port}",
self.remote, remote_cmd,
]
def start(self, *, timeout: float = 30) -> None:
self.process = subprocess.Popen(
self.command(), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
if self.process.poll() is not None:
raise WindowError("audit-core port-forward exited early")
# A local ssh listener accepts before the remote forward is ready, so
# require a real /healthz answer through the whole chain.
try:
with urllib.request.urlopen(f"http://127.0.0.1:{self.port}/healthz", timeout=2) as response:
if response.status == 200:
return
except (OSError, urllib.error.URLError):
pass
time.sleep(1)
raise WindowError("audit-core port-forward did not become reachable")
def stop(self) -> None:
if self.process is None or self.process.poll() is not None:
return
self.process.terminate()
try:
self.process.wait(timeout=10)
except subprocess.TimeoutExpired:
self.process.kill()
self.process.wait(timeout=5)
def window(args: argparse.Namespace, *, runner: Callable[..., dict[str, Any]] = run_json,
forward_factory: Callable[[str, int], Any] = PortForward) -> dict[str, Any]:
if not args.bearer_file.is_absolute() or not args.evidence_dir.is_absolute():
raise WindowError("--bearer-file and --evidence-dir must be absolute")
LANE.LEASE.contained_bao_env() # refuse before any step outside the contained login
approval, spec = LANE.load_receipt(args.approval, datetime.now(UTC), require_open_window=True)
if args.confirm != LANE.confirm_token(approval):
raise WindowError(f"window requires --confirm {LANE.confirm_token(approval)}")
evidence_dir = private_dir(args.evidence_dir)
base = f"{slug(approval['approval_id'])}"
exercise_path = evidence_dir / f"{base}-exercise.json"
summary_path = evidence_dir / f"{base}-window.json"
for path in (exercise_path, summary_path):
if path.exists():
raise WindowError(f"evidence file already exists: {path.name}")
lane = [sys.executable, str(LANE_SCRIPT)]
common = ["--approval", str(args.approval), "--remote", args.remote, "--contained",
"--confirm", args.confirm]
summary: dict[str, Any] = {
"procedure": "t02-attended-window",
"approval_id": approval["approval_id"],
"identity": LANE.identity_view(spec),
"started_at": stamp(datetime.now(UTC)),
"steps": [],
"secret_values_observed": False,
}
projection_attempted = False
forward = forward_factory(args.remote, args.port)
try:
projection_attempted = True
summary["project"] = runner(
[*lane, "project", *common, "--bearer-file", str(args.bearer_file)], label="lane project")
summary["steps"].append("project")
forward.start()
summary["steps"].append("port-forward")
exercise = runner(
[sys.executable, str(HARNESS_SCRIPT), "exercise", "--approval", str(args.approval),
"--remote", args.remote, "--contained", "--load-driver", str(args.load_driver),
"--confirm", HARNESS_CONFIRM],
label="T02 exercise",
env=driver_env(spec, approval, args.bearer_file, args.port),
)
summary["steps"].append("exercise")
write_private_json(exercise_path, exercise)
summary["exercise_evidence"] = exercise_path.name
summary["steps"].append("evidence")
forward.stop()
summary["cleanup"] = runner(
[*lane, "cleanup", *common, "--bearer-file", str(args.bearer_file),
"--exercise-evidence", str(exercise_path)], label="lane cleanup")
summary["steps"].append("cleanup")
summary["outcome"] = "completed"
except BaseException as exc:
forward.stop()
summary["outcome"] = "aborted"
summary["abort_reason"] = str(exc)[:200] if isinstance(exc, WindowError) else type(exc).__name__
if projection_attempted and "cleanup" not in summary["steps"]:
try:
summary["cleanup"] = runner(
[*lane, "cleanup", *common, "--bearer-file", str(args.bearer_file),
"--abort-reason", f"t02 window aborted: {summary['abort_reason']}"],
label="lane abort cleanup")
summary["steps"].append("abort-cleanup")
except BaseException as cleanup_exc: # keep the first failure; flag cleanup loudly
summary["abort_cleanup_failed"] = str(cleanup_exc)[:200]
summary["finished_at"] = stamp(datetime.now(UTC))
write_private_json(summary_path, summary)
raise
finally:
forward.stop()
summary["finished_at"] = stamp(datetime.now(UTC))
write_private_json(summary_path, summary)
return summary
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--approval", type=Path, required=True)
parser.add_argument("--load-driver", type=Path, required=True)
parser.add_argument("--bearer-file", type=Path, required=True)
parser.add_argument("--evidence-dir", type=Path, required=True)
parser.add_argument("--confirm", required=True)
parser.add_argument("--remote", default="railiance01")
parser.add_argument("--port", type=int, default=18081)
args = parser.parse_args(argv)
# The contained login discards child output and fails closed on any byte,
# so this script never prints; its outcome lives only in the evidence dir.
try:
window(args)
except (OSError, KeyError, WindowError, LANE.LaneError, LANE.LEASE.ProcedureError) as exc:
try:
LANE.emit_result(
{"status": "failed", "error": str(exc), "secret_values_observed": False},
args.evidence_dir / f"window-error-{int(time.time())}.json",
)
except OSError:
pass
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())