Build audit-core AppRole lane after founder approval
reuse_policy leaves external-secrets-audit-core intact so database leases keep working. AppRole delivered to Kubernetes; interim static ESO token retired.
This commit is contained in:
parent
93b9c1a027
commit
8e41f85121
3 changed files with 58 additions and 5 deletions
|
|
@ -3,9 +3,9 @@ id: audit-core-openbao-runtime-custody
|
|||
demand_source: audit-core/workplans/AUDIT-WP-0005-T02
|
||||
consumer_repo: audit-core
|
||||
credential_type: openbao-approle-kv
|
||||
status: reviewed
|
||||
approved_by: null
|
||||
approved_at: null
|
||||
status: built
|
||||
approved_by: "Bernd Worsch"
|
||||
approved_at: "2026-08-13"
|
||||
created: "2026-08-13"
|
||||
updated: "2026-08-13"
|
||||
---
|
||||
|
|
@ -118,3 +118,29 @@ is on this identity.
|
|||
**Decision:** approve, reject, or send back to phase 1.
|
||||
Approval authorizes Mason build of structure and in-cluster sender
|
||||
minting. It does not authorize printing any secret value.
|
||||
|
||||
## 6. Build result
|
||||
|
||||
Built 2026-08-13 after founder approve, via
|
||||
`ops_mason.executor.build_approle_kv_lane` with `reuse_policy=True` so
|
||||
the existing `external-secrets-audit-core` policy (database creds +
|
||||
senders read) was not rewritten.
|
||||
|
||||
- reused policy `external-secrets-audit-core`;
|
||||
- created AppRole `external-secrets-audit-core` (15m / 30m / 8 uses);
|
||||
- delivered role-id/secret-id into Secret
|
||||
`external-secrets/openbao-audit-core-approle`; local delivery dir shredded;
|
||||
- switched ClusterSecretStores `openbao-audit-core` and
|
||||
`openbao-audit-core-database` to AppRole auth; both `Valid`;
|
||||
- ExternalSecrets remain `SecretSynced`; receiver `/readyz` still
|
||||
`custody_class=archive`;
|
||||
- deleted interim Secret `external-secrets/openbao-audit-core-eso-token`;
|
||||
- catalog entry `audit-core-senders` already drafted in ops-warden
|
||||
(`warden route find "audit-core senders" --all`).
|
||||
|
||||
Empty KV path `platform/workloads/audit-core/senders` was not written:
|
||||
mason does not put values, and senders already live in-cluster. A later
|
||||
wrap-migrate can create that path without a founder paste.
|
||||
|
||||
No secret value was printed, committed, or logged. The 15-minute
|
||||
platform-admin child used for the build was revoked.
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ class AppRoleKVSpec:
|
|||
delivery_dir: Path | None = None
|
||||
bao_bin: str = "bao"
|
||||
audit_log_path: Path | None = None
|
||||
# When True the named policy already exists (and may grant more than one
|
||||
# KV path). Do not rewrite it — AppRole bind only.
|
||||
reuse_policy: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -111,8 +114,9 @@ def build_approle_kv_lane(plan: ConstructionPlan, spec: AppRoleKVSpec) -> dict[s
|
|||
f"approved_at={plan.approved_at!r}) — refusing to build"
|
||||
)
|
||||
|
||||
policy_hcl = _policy_hcl(spec.kv_path, spec.kv_capabilities)
|
||||
_run(spec.bao_bin, ["policy", "write", spec.policy_name, "-"], input_text=policy_hcl)
|
||||
if not spec.reuse_policy:
|
||||
policy_hcl = _policy_hcl(spec.kv_path, spec.kv_capabilities)
|
||||
_run(spec.bao_bin, ["policy", "write", spec.policy_name, "-"], input_text=policy_hcl)
|
||||
|
||||
_run(
|
||||
spec.bao_bin,
|
||||
|
|
|
|||
|
|
@ -117,6 +117,29 @@ def test_build_writes_policy_approle_and_delivers_credentials(tmp_path) -> None:
|
|||
assert "secret-id-value" not in audit_text
|
||||
|
||||
|
||||
def test_reuse_policy_does_not_rewrite_existing_policy(tmp_path) -> None:
|
||||
plan = _plan(tmp_path)
|
||||
spec = _spec(tmp_path)
|
||||
spec.reuse_policy = True
|
||||
|
||||
def fake_run(cmd, input=None, capture_output=True, text=True, timeout=30):
|
||||
result = MagicMock(returncode=0, stderr="")
|
||||
if cmd[1:3] == ["read", "-field=role_id"]:
|
||||
result.stdout = "role-id-value\n"
|
||||
elif "-field=secret_id" in cmd:
|
||||
result.stdout = "secret-id-value\n"
|
||||
else:
|
||||
result.stdout = ""
|
||||
return result
|
||||
|
||||
with patch("ops_mason.executor.subprocess.run", side_effect=fake_run) as run:
|
||||
build_approle_kv_lane(plan, spec)
|
||||
|
||||
bao_cmds = [c.args[0] for c in run.call_args_list]
|
||||
assert not any(cmd[:2] == ["bao", "policy"] for cmd in bao_cmds)
|
||||
assert any(cmd[1:3] == ["write", "auth/approle/role/test-lane"] for cmd in bao_cmds)
|
||||
|
||||
|
||||
def test_build_appends_audit_record(tmp_path) -> None:
|
||||
plan = _plan(tmp_path)
|
||||
spec = _spec(tmp_path)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue