feat: admit existing OpenBao catalog lanes
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

This commit is contained in:
tegwick 2026-08-21 08:20:33 +02:00
parent 9d383442c8
commit 784be978bf
29 changed files with 1490 additions and 79 deletions

View file

@ -63,6 +63,19 @@ def _writer(cfg: Config) -> EvidenceWriter:
return EvidenceWriter(evidence_dir=cfg.evidence_dir, hub_url=cfg.hub_url, topic_id=cfg.topic_id)
def _require_lane_approval(cfg: Config, entry):
"""Resolve and enforce the lane approval for a privileged live action."""
if not entry.approval_required():
return None
decision = resolve_decision(
hub_url=cfg.hub_url,
repo_root=repo_root(),
decision_ref=entry.approval.get("decision_ref", entry.id),
)
require_approved(entry, decision)
return decision
# -- command handlers ------------------------------------------------------
@ -89,13 +102,23 @@ def cmd_catalog_show(cfg: Config, args) -> int:
print(f"openbao: mount={e.mount} allowed={sorted(e.auth_allowed_paths)}")
print(f"approle: {e.role_name} policy={e.policy_name}")
else:
print(f"openbao: {e.mount}/{e.path} fields={e.fields}")
print(
f"openbao: {e.mount}/{e.path} fields={e.fields} "
f"mount_management={e.mount_management}"
)
print(
f"delivery auth: {e.delivery_auth_method}/"
f"{e.delivery_auth_management} role={e.role_name if e.has_delivery_auth else '-'}"
)
print(f"workload: {e.workload_delivery}")
print(f"consumers: {[c['name'] for c in e.consumers]}")
print(f"delivery: {e.delivery_modes}")
print(f"approval: {e.approval.get('model')} ref={e.approval.get('decision_ref','')}")
print(f"verification: {e.verification}")
print(f"rotation: {e.rotation}")
print(f"deactivation: {e.deactivation}")
if e.risk:
print(f"risk: {e.risk}")
print(f"description: {e.description.strip()}")
return 0
@ -172,6 +195,7 @@ def cmd_provision(cfg: Config, args) -> int:
if args.stage != entry.stage:
from secrets_engine.errors import ProvisioningError
raise ProvisioningError(f"lane '{entry.id}' is stage '{entry.stage}', not '{args.stage}'")
decision = _require_lane_approval(cfg, entry)
client = OpenBaoClient.resolve(cfg.bao_addr, bootstrap_token_file=args.bootstrap_token_file)
field = args.field or (entry.fields[0] if entry.fields else "")
if args.generate:
@ -182,12 +206,13 @@ def cmd_provision(cfg: Config, args) -> int:
mode = "from-file"
print(f"provisioned lane '{entry.id}' field '{f}' ({mode}) — value not displayed")
_writer(cfg).record("provision", result=mode, catalog_id=entry.id, stage=entry.stage,
detail={"field": f})
decision_id=decision.id if decision else "", detail={"field": f})
return 0
def cmd_verify(cfg: Config, args) -> int:
entry = get_entry(cfg.catalog_dir, args.catalog_id)
decision = _require_lane_approval(cfg, entry)
client = OpenBaoClient.resolve(cfg.bao_addr, bootstrap_token_file=args.bootstrap_token_file)
field = args.field or (entry.fields[0] if entry.fields else "")
if entry.stores_kv_value() and not field:
@ -202,7 +227,8 @@ def cmd_verify(cfg: Config, args) -> int:
if not r.passed:
rc = 7
_writer(cfg).record("verify", result=f"{r.check}:{'pass' if r.passed else 'fail'}",
catalog_id=entry.id, stage=entry.stage, detail=r.detail)
catalog_id=entry.id, stage=entry.stage,
decision_id=decision.id if decision else "", detail=r.detail)
return rc
@ -215,13 +241,7 @@ def cmd_handoff(cfg: Config, args) -> int:
raise ProvisioningError(f"lane '{entry.id}' is stage '{entry.stage}', not '{args.stage}'")
if entry.kind != "auth-capability":
raise ProvisioningError(f"lane '{entry.id}' is {entry.kind}; handoff needs auth-capability")
decision = None
if entry.approval_required():
decision = resolve_decision(
hub_url=cfg.hub_url, repo_root=repo_root(),
decision_ref=entry.approval.get("decision_ref", entry.id),
)
require_approved(entry, decision)
decision = _require_lane_approval(cfg, entry)
client = OpenBaoClient.resolve(cfg.bao_addr, bootstrap_token_file=args.bootstrap_token_file)
result = write_approle_handoff(
client,
@ -257,12 +277,7 @@ def cmd_exec(cfg: Config, args) -> int:
from secrets_engine.exec_delivery import exec_with_secret
entry = get_entry(cfg.catalog_dir, args.catalog)
# require approval + readiness before running.
if entry.approval_required():
decision = resolve_decision(
hub_url=cfg.hub_url, repo_root=repo_root(),
decision_ref=entry.approval.get("decision_ref", entry.id),
)
require_approved(entry, decision)
decision = _require_lane_approval(cfg, entry)
if not args.command:
from secrets_engine.errors import DeliveryError
raise DeliveryError("no command after '--'")
@ -270,9 +285,11 @@ def cmd_exec(cfg: Config, args) -> int:
field = args.field or (entry.fields[0] if entry.fields else "")
w = _writer(cfg)
w.record("exec", result="attempt", catalog_id=entry.id, stage=entry.stage,
decision_id=decision.id if decision else "",
detail={"command": args.command[0], "mode": args.mode})
rc = exec_with_secret(client, entry, field, args.command, mode=args.mode)
w.record("exec", result=f"exit-{rc}", catalog_id=entry.id, stage=entry.stage,
decision_id=decision.id if decision else "",
detail={"command": args.command[0]})
return rc
@ -322,6 +339,7 @@ def cmd_route(cfg: Config, args) -> int:
def cmd_revoke(cfg: Config, args) -> int:
entry = get_entry(cfg.catalog_dir, args.catalog_id)
decision = None if args.dry_run else _require_lane_approval(cfg, entry)
client = OpenBaoClient.resolve(cfg.bao_addr, bootstrap_token_file=args.bootstrap_token_file)
if entry.kind == "auth-capability":
if args.dry_run:
@ -336,7 +354,10 @@ def cmd_revoke(cfg: Config, args) -> int:
f"revoked lane '{entry.id}': deleted approle {entry.role_name} "
f"and policy {entry.policy_name}"
)
_writer(cfg).record("revoke", result="auth-capability-deactivated", catalog_id=entry.id, stage=entry.stage)
_writer(cfg).record(
"revoke", result="auth-capability-deactivated", catalog_id=entry.id,
stage=entry.stage, decision_id=decision.id if decision else ""
)
return 0
if args.dry_run:
print(f"(dry-run) would delete KV metadata {entry.mount}/{entry.path} "
@ -344,7 +365,10 @@ def cmd_revoke(cfg: Config, args) -> int:
return 0
client.kv_delete_metadata(entry.mount, entry.path)
print(f"revoked lane '{entry.id}': KV metadata deleted at {entry.mount}/{entry.path}")
_writer(cfg).record("revoke", result="deactivated", catalog_id=entry.id, stage=entry.stage)
_writer(cfg).record(
"revoke", result="deactivated", catalog_id=entry.id, stage=entry.stage,
decision_id=decision.id if decision else ""
)
return 0