RPF-WP-0035-T02: CCR-2026-0038 service-JWT login lane for secrets-engine-openbao
Package the reviewed keycape-services JWT mount, secrets-engine-openbao role and secrets-engine-login-self policy as CCR-2026-0038 and teach the delegated applier a narrow service-jwt-login request type: fixed mount, generated login-self policy, allow-listed role/config keys, bounded TTL/uses, refusal on a foreign mount type and a value-free readback compare. The policy now denies the client-secret data/metadata paths explicitly (MASON-WP-0009 invariant ii). Proposed only; founder approval and delegated apply pending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 327203@bnt-lap001 Assistant-Session: 55028ae3-4914-4bd1-8f5c-815f3527aef0
This commit is contained in:
parent
2e7701f155
commit
0b2e7ddb5f
8 changed files with 680 additions and 15 deletions
|
|
@ -516,6 +516,8 @@ def validate_ccr(path: Path) -> tuple[dict[str, Any], list[str], list[str]]:
|
|||
owner = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(owner)
|
||||
errors.extend(owner.validate_contract(ccr))
|
||||
elif request_type == "service-jwt-login":
|
||||
errors.extend(service_jwt_lane().validate(ccr, REPO_DIR))
|
||||
else:
|
||||
errors.append("unsupported request_type")
|
||||
return ccr, errors, warnings
|
||||
|
|
@ -527,14 +529,14 @@ def render_summary(ccr: dict[str, Any], warnings: list[str]) -> str:
|
|||
frontdoor = ccr["access_frontdoor"]
|
||||
risk = ccr["risk"]
|
||||
verification = ccr["verification"]
|
||||
fields = ", ".join(openbao["fields"])
|
||||
fields = ", ".join(openbao.get("fields", []))
|
||||
lines = [
|
||||
f"Request: {ccr['title']}",
|
||||
f"CCR: {ccr['id']} ({ccr['status']})",
|
||||
f"Type: {ccr['request_type']}",
|
||||
f"Target: {ccr['target']['tenant']}/{ccr['target']['workload']} ({ccr['target']['environment']})",
|
||||
"Mount/path/field:",
|
||||
f" {openbao['kv_path']}",
|
||||
f" {openbao.get('kv_path', '-')}",
|
||||
f" {fields}",
|
||||
"Policy:",
|
||||
f" {openbao.get('policy_name', 'none (attended host delivery)')}",
|
||||
|
|
@ -804,6 +806,19 @@ def string_values(value: Any) -> list[str]:
|
|||
return []
|
||||
|
||||
|
||||
def service_jwt_lane():
|
||||
"""Lazy-load scripts/service_jwt_lane.py (request_type service-jwt-login)."""
|
||||
import importlib.util
|
||||
spec = importlib.util.spec_from_file_location("service_jwt_lane", REPO_DIR / "scripts/service_jwt_lane.py")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def is_service_jwt(ccr: dict[str, Any]) -> bool:
|
||||
return ccr.get("request_type") == "service-jwt-login"
|
||||
|
||||
|
||||
def applier_policy_violations(ccr: dict[str, Any]) -> list[str]:
|
||||
violations: list[str] = []
|
||||
if ccr.get("request_type") != "workload-kv-read":
|
||||
|
|
@ -921,6 +936,8 @@ def applier_policy_violations(ccr: dict[str, Any]) -> list[str]:
|
|||
|
||||
|
||||
def applier_readiness_blockers(ccr: dict[str, Any]) -> list[str]:
|
||||
if is_service_jwt(ccr):
|
||||
return service_jwt_lane().readiness_blockers(ccr, REPO_DIR, APPLIER_DRY_RUN_ALLOWED_STATUSES)
|
||||
blockers: list[str] = []
|
||||
status = ccr.get("status")
|
||||
if status not in APPLIER_DRY_RUN_ALLOWED_STATUSES:
|
||||
|
|
@ -935,6 +952,8 @@ def applier_readiness_blockers(ccr: dict[str, Any]) -> list[str]:
|
|||
|
||||
|
||||
def applier_dry_run_payload(ccr: dict[str, Any], warnings: list[str]) -> dict[str, Any]:
|
||||
if is_service_jwt(ccr):
|
||||
return service_jwt_lane().dry_run_payload(ccr, REPO_DIR, warnings)
|
||||
openbao = ccr["openbao"]
|
||||
auth = openbao["auth"]
|
||||
auth_path = f"auth/{auth['mount']}/role/{auth['role']}"
|
||||
|
|
@ -1023,6 +1042,8 @@ def applier_confirmation_phrase(ccr: dict[str, Any]) -> str:
|
|||
|
||||
|
||||
def delegated_apply_details(ccr: dict[str, Any], actor: str) -> list[str]:
|
||||
if is_service_jwt(ccr):
|
||||
return service_jwt_lane().apply_details(ccr, actor)
|
||||
openbao = ccr["openbao"]
|
||||
auth = openbao["auth"]
|
||||
return [
|
||||
|
|
@ -1183,6 +1204,13 @@ def render_runbook(payload: dict[str, Any]) -> str:
|
|||
|
||||
|
||||
def run_bao_metadata_apply(ccr: dict[str, Any], bao_bin: str, quiet: bool = False) -> None:
|
||||
if is_service_jwt(ccr):
|
||||
lane = service_jwt_lane()
|
||||
try:
|
||||
lane.run_apply(ccr, REPO_DIR, bao_bin, quiet=quiet)
|
||||
except (lane.BaoError, subprocess.CalledProcessError) as exc:
|
||||
fail(str(exc))
|
||||
return
|
||||
openbao = ccr["openbao"]
|
||||
auth = openbao["auth"]
|
||||
auth_path = f"auth/{auth['mount']}/role/{auth['role']}"
|
||||
|
|
@ -1272,7 +1300,7 @@ def record_evidence_state_hub(
|
|||
openbao = ccr["openbao"]
|
||||
summary = (
|
||||
f"CCR {ccr['id']} evidence {kind}/{result} by {actor}: "
|
||||
f"status={ccr['status']} path={openbao['kv_path']} "
|
||||
f"status={ccr['status']} path={openbao.get('kv_path') or 'auth/' + openbao['auth']['mount']} "
|
||||
f"policy={openbao['policy_name']}; "
|
||||
+ "; ".join(details)
|
||||
)
|
||||
|
|
@ -1451,7 +1479,7 @@ def record_lifecycle_state_hub(
|
|||
summary = (
|
||||
f"CCR {ccr['id']} lifecycle {action} by {actor}: "
|
||||
f"status={ccr['status']} readiness={frontdoor.get('readiness')} "
|
||||
f"resolvable={frontdoor.get('resolvable') is True} path={openbao['kv_path']} "
|
||||
f"resolvable={frontdoor.get('resolvable') is True} path={openbao.get('kv_path', '-')} "
|
||||
f"policy={openbao['policy_name']}; reason={reason}; "
|
||||
+ "; ".join(details)
|
||||
)
|
||||
|
|
@ -1687,9 +1715,9 @@ def status_payload(ccr: dict[str, Any], warnings: list[str]) -> dict[str, Any]:
|
|||
"frontdoor_blockers": frontdoor_blocked_by,
|
||||
"warnings": warnings,
|
||||
"openbao": {
|
||||
"mount": openbao["mount"],
|
||||
"kv_path": openbao["kv_path"],
|
||||
"fields": openbao["fields"],
|
||||
"mount": openbao.get("mount"),
|
||||
"kv_path": openbao.get("kv_path"),
|
||||
"fields": openbao.get("fields", []),
|
||||
"policy_name": openbao.get("policy_name"),
|
||||
"auth_mount": auth.get("mount"),
|
||||
"auth_method": auth.get("method"),
|
||||
|
|
@ -1845,7 +1873,7 @@ def decision_template_context(ccr: dict[str, Any]) -> dict[str, str]:
|
|||
state_hub = ccr.get("state_hub", {})
|
||||
return {
|
||||
"id": ccr["id"],
|
||||
"kv_path": openbao["kv_path"],
|
||||
"kv_path": openbao.get("kv_path") or f"auth/{auth['mount']}",
|
||||
"policy_name": openbao.get("policy_name"),
|
||||
"auth_role_path": f"auth/{auth['mount']}/role/{auth['role']}",
|
||||
"decision_link": state_hub.get("decision_api_url")
|
||||
|
|
@ -2159,7 +2187,7 @@ def command_decision(args: argparse.Namespace, status: str) -> int:
|
|||
{
|
||||
"summary": (
|
||||
f"CCR {ccr['id']} decision {status} by {args.reviewer}: "
|
||||
f"path={openbao['kv_path']} policy={openbao['policy_name']} "
|
||||
f"path={openbao.get('kv_path', '-')} policy={openbao['policy_name']} "
|
||||
f"fields={','.join(openbao['fields'])} "
|
||||
f"auth_role=auth/{auth['mount']}/role/{auth['role']}; "
|
||||
f"{args.comment}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue