net-kingdom/tools/bounded-execution-admission/generate_examples.py
tegwick d1cf754222 NK-WP-0056-T11 done: bounded executor core runner
Consumes one admission once through the approval-engine carrier after an
effect-free preflight (validity, source pins, trust, window vs budget,
binding digest, actor, human aal2 approvers), emits run.started, runs steps
with settle-timed checks, reverts on failure with per-revert checks, retries
within attempts, stops on unknown outcomes, honours budget and kill switch,
and emits one value-free receipt (PDP not-evaluated). 22 tests with fakes.
BEA-9 extended to revert steps; examples regenerated.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 994874@bnt-lap001
Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
2026-10-31 16:00:52 +01:00

144 lines
13 KiB
Python

"""Regenerate examples/bounded-execution-admission/ and the human-intrinsic invalid fixture (NK-WP-0056-T01).
Pins the named files at the current commit and computes plan_digest. Run after changing those files or the schema.
"""
import hashlib, json, pathlib, subprocess, sys, yaml
ROOT = pathlib.Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "tools/bounded-execution-admission"))
import validate as V
commit = subprocess.run(["git", "-C", str(ROOT), "rev-parse", "--short=12", "HEAD"], capture_output=True, text=True).stdout.strip()
def pin(*paths): return [{"path": p, "sha256": hashlib.sha256((ROOT / p).read_bytes()).hexdigest()} for p in paths]
def sha(p): return hashlib.sha256((ROOT / p).read_bytes()).hexdigest()
JOURNEY = "sso-mfa/k8s/synthetic-journey/journey.py"
LANE = "tools/service-binds/bind-lane.py"
SPLIT = "sso-mfa/k8s/lldap/bind-split.py"
COMMON = {
"approval": {"approvers": ["admin"], "principal_type": "HUMAN", "assurance": "aal2",
"carriers": ["approval-engine", "founder-ssh-signature"]},
"consumption": {"single_use": True, "consume_before_first_step": True},
"receipt": {"schema": "netkingdom.playbook-capability-contract/v0.2#execution-attribution-receipt",
"emit_to": "audit-core", "retain": "by-hash-no-secret-values",
"independent_verifier": "tools/bounded-execution-admission/validate.py check-approval + receipt source hashes"},
}
EXECUTOR = {"identity": "service:nk-bind-executor", "principal_type": "WORKLOAD",
"runtime": "k8s sso/serviceaccount nk-bind-executor (planned, NK-WP-0056-T03)"}
P = ["PRODUCTION"]
def check(cid, desc, ref, timeout, settle=0):
c = {"id": cid, "description": desc, "ref": ref, "sha256": sha(ref), "timeout_seconds": timeout, "expect": "pass"}
if settle: c["settle_seconds"] = settle
return c
def doc(id_, title, purpose, risk, window, ceiling, radius, files, steps, reverts, checks, policy, budget, not_auth, trust=("runtime_secret_trust", "runtime_identity_trust")):
spec = {"id": id_, "title": title, "purpose": purpose, "work_ref": "NK-WP-0056-T01 example", "risk_class": risk,
"window": window, **{"approval": COMMON["approval"]}, "executor": EXECUTOR, "scope_ceiling": ceiling,
"blast_radius": radius, "trust_requires": list(trust),
"plan": {"source": {"repo": "net-kingdom", "commit": commit, "files": pin(*files)}, "steps": steps,
**({"revert_steps": reverts} if reverts else {})},
"plan_digest": "", "checks": checks, "stop_and_revert": policy, "budget": budget,
"consumption": COMMON["consumption"], "receipt": COMMON["receipt"], "not_authorized": not_auth}
d = {"apiVersion": "netkingdom.io/bounded-execution-admission/v0.1", "kind": "BoundedExecutionAdmission", "spec": spec}
spec["plan_digest"] = V.plan_digest(d)
return d
def step(id_, op, target, consumer, cls, mutates, mode, scope, checks=None, revert=None, irreversible=None):
s = {"id": id_, "operation": op, "target": target, "consumer": consumer, "class": cls, "mutates": mutates,
"requires": {"mode": mode, "scope": scope}}
if checks: s["checks_after"] = checks
if revert: s["revert"] = revert
if irreversible: s["irreversible"] = True
return s
KV = "openbao/platform/workloads/net-kingdom/directory-binds/svc-authelia"
rotation = doc(
"BEA-2026-0001", "Rotate the svc-authelia directory bind (bind-split steps 5-8, one consumer)",
"Pilot shape for NK-WP-0056-T06: generate a new bind password into OpenBao, set it in LLDAP, let ESO deliver it, restart Authelia, prove sign-in with the synthetic journey; revert on any failed check.",
"medium", {"not_before": "2026-11-03T09:00:00Z", "not_after": "2026-11-03T11:00:00Z"},
[{"mode": "OPERATE", "scope": KV, "environments": P},
{"mode": "OPERATE", "scope": "lldap/people/svc-authelia", "environments": P},
{"mode": "OPERATE", "scope": "k8s/sso/externalsecret/authelia-secrets", "environments": P},
{"mode": "OPERATE", "scope": "k8s/sso/deployment/authelia", "environments": P}],
{"unit": "consumer", "consumers": ["authelia"], "max_concurrent": 1},
[LANE, JOURNEY],
[step("generate", "openbao.kv.write-next-version", "openbao:" + KV[len("openbao/"):], "authelia", "delegable", True, "OPERATE", KV, ["kv-advanced"], "restore-kv"),
step("set-directory", "lldap.user.change-own-password-from-kv", "lldap:svc-authelia", "authelia", "delegable", True, "OPERATE", "lldap/people/svc-authelia", ["bind-signs-in"], "restore-directory"),
step("deliver", "eso.force-sync", "k8s:sso/externalsecret/authelia-secrets", "authelia", "delegable", True, "OPERATE", "k8s/sso/externalsecret/authelia-secrets", ["eso-synced"], "redeliver"),
step("restart", "k8s.rollout-restart", "k8s:sso/deployment/authelia", "authelia", "delegable", True, "OPERATE", "k8s/sso/deployment/authelia", ["synthetic-journey"], "restart-again")],
[step("restore-kv", "openbao.kv.write-previous-value-as-next-version", "openbao:" + KV[len("openbao/"):], "authelia", "delegable", True, "OPERATE", KV, ["kv-advanced"]),
step("restore-directory", "lldap.user.change-own-password-from-kv", "lldap:svc-authelia", "authelia", "delegable", True, "OPERATE", "lldap/people/svc-authelia", ["bind-signs-in"]),
step("redeliver", "eso.force-sync", "k8s:sso/externalsecret/authelia-secrets", "authelia", "delegable", True, "OPERATE", "k8s/sso/externalsecret/authelia-secrets", ["eso-synced"]),
step("restart-again", "k8s.rollout-restart", "k8s:sso/deployment/authelia", "authelia", "delegable", True, "OPERATE", "k8s/sso/deployment/authelia", ["synthetic-journey"])],
[check("kv-advanced", "KV svc-authelia has exactly one new version, CAS on the previous one", LANE, 30),
check("bind-signs-in", "the new KV value signs in to LLDAP as svc-authelia", LANE, 30),
check("eso-synced", "the ExternalSecret is Ready with a refresh after the step started; the executor never reads the Secret", LANE, 120),
check("synthetic-journey", "syn-journey signs in through KeyCape, Authelia and privacyIDEA at aal2", JOURNEY, 90)],
{"on_check_failure": "revert", "on_budget_exhausted": "revert", "on_unknown": "stop",
"on_revert_failure": "stop-and-alert", "alert": "founder via State Hub message and audit-core"},
{"wall_time_seconds": 1200, "attempts": 2, "cost": {"compute_cpu_seconds": 120, "egress_bytes": 5000000, "currency": "EUR", "amount": 0.05}},
["any bind other than svc-authelia", "any LLDAP group change", "changing LLDAP's configured super-admin",
"reading or changing any human account", "any change outside the sso namespace"])
create = doc(
"BEA-2026-0002", "Create the service bind accounts (bind-split step 3, root-class)",
"Create svc-authelia, svc-keycape and svc-privacyidea in LLDAP as read-only binds. Needs lldap_admin: LLDAP has no narrower grant that can create users (SI-X6), so the founder admits this run and the executor runs it.",
"high", {"not_before": "2026-11-03T09:00:00Z", "not_after": "2026-11-03T12:00:00Z"},
[{"mode": "ADMINISTER", "scope": "lldap", "environments": P},
{"mode": "OPERATE", "scope": "openbao/platform/workloads/net-kingdom/directory-binds", "environments": P}],
{"unit": "account", "consumers": ["lldap"], "max_concurrent": 1},
[SPLIT, LANE],
[step("create-accounts", "lldap.user.create-read-only-binds", "lldap:svc-authelia,svc-keycape,svc-privacyidea", "lldap", "root-class", True, "ADMINISTER", "lldap", ["accounts-read-only"], "delete-accounts"),
step("hold-passwords", "openbao.kv.write-cas0", "openbao:platform/workloads/net-kingdom/directory-binds", "lldap", "root-class", True, "OPERATE", "openbao/platform/workloads/net-kingdom/directory-binds", ["accounts-sign-in"], "destroy-kv")],
[step("delete-accounts", "lldap.user.delete-created", "lldap:svc-authelia,svc-keycape,svc-privacyidea", "lldap", "root-class", True, "ADMINISTER", "lldap", ["accounts-read-only"]),
step("destroy-kv", "openbao.kv.destroy-created", "openbao:platform/workloads/net-kingdom/directory-binds", "lldap", "root-class", True, "OPERATE", "openbao/platform/workloads/net-kingdom/directory-binds", ["accounts-sign-in"])],
[check("accounts-read-only", "each account exists in lldap_strict_readonly only and cannot write", SPLIT, 60),
check("accounts-sign-in", "each KV value signs in to LLDAP as its account", LANE, 60)],
{"on_check_failure": "revert", "on_budget_exhausted": "revert", "on_unknown": "stop",
"on_revert_failure": "stop-and-alert", "alert": "founder via State Hub message"},
{"wall_time_seconds": 900, "attempts": 1, "cost": {"compute_cpu_seconds": 60, "egress_bytes": 1000000, "currency": "EUR", "amount": 0.02}},
["switching any consumer to the new accounts", "any human account", "changing LLDAP's configured super-admin"])
superadmin = doc(
"BEA-2026-0003", "Switch LLDAP's configured super-admin to ld-admin (bind-split step 9, root-class)",
"Change LLDAP_LDAP_USER_DN from admin to ld-admin and restart LLDAP; admin keeps its rights through lldap_admin membership. Changes who administers the directory, so the founder admits the run.",
"high", {"not_before": "2026-11-03T09:00:00Z", "not_after": "2026-11-03T11:00:00Z"},
[{"mode": "ADMINISTER", "scope": "lldap", "environments": P},
{"mode": "OPERATE", "scope": "k8s/sso/deployment/lldap", "environments": P}],
{"unit": "consumer", "consumers": ["lldap"], "max_concurrent": 1},
[SPLIT, JOURNEY],
[step("switch-user-dn", "lldap.config.set-ldap-user-dn", "k8s:sso/secret/lldap-secrets#LLDAP_LDAP_USER_DN", "lldap", "root-class", True, "ADMINISTER", "lldap", ["ld-admin-administers"], "switch-back"),
step("restart-lldap", "k8s.rollout-restart", "k8s:sso/deployment/lldap", "lldap", "root-class", True, "OPERATE", "k8s/sso/deployment/lldap", ["synthetic-journey"], "restart-lldap-again")],
[step("switch-back", "lldap.config.set-ldap-user-dn", "k8s:sso/secret/lldap-secrets#LLDAP_LDAP_USER_DN", "lldap", "root-class", True, "ADMINISTER", "lldap", ["ld-admin-administers"]),
step("restart-lldap-again", "k8s.rollout-restart", "k8s:sso/deployment/lldap", "lldap", "root-class", True, "OPERATE", "k8s/sso/deployment/lldap", ["synthetic-journey"])],
[check("ld-admin-administers", "ld-admin signs in and administers; admin keeps lldap_admin membership", SPLIT, 60),
check("synthetic-journey", "all three binds still serve sign-in: syn-journey reaches aal2", JOURNEY, 90, settle=125)],
{"on_check_failure": "revert", "on_budget_exhausted": "revert", "on_unknown": "stop",
"on_revert_failure": "stop-and-alert", "alert": "founder via State Hub message; crisis list item for ld-admin"},
{"wall_time_seconds": 900, "attempts": 1, "cost": {"compute_cpu_seconds": 60, "egress_bytes": 1000000, "currency": "EUR", "amount": 0.02}},
["changing admin's own password", "sealing ld-admin's password (human-intrinsic, step 10)", "any service bind change"])
verify = doc(
"BEA-2026-0004", "Read-only verification of the directory split (bind-split steps 2, 4 and 10 verify)",
"Plan and verify reads that the founder used to do signed in as admin: account inventory, group membership, bind identities of the consumers. No mutation, so nothing to revert.",
"low", {"not_before": "2026-11-03T09:00:00Z", "not_after": "2026-11-04T09:00:00Z"},
[{"mode": "OBSERVE", "scope": "lldap/people", "environments": P},
{"mode": "OBSERVE", "scope": "k8s/sso", "environments": P}],
{"unit": "consumer", "consumers": ["lldap", "authelia", "keycape", "privacyidea"], "max_concurrent": 4},
[SPLIT],
[step("inventory", "lldap.read.users-and-groups", "lldap:people", "lldap", "delegable", False, "OBSERVE", "lldap/people", ["split-shape"]),
step("consumer-binds", "k8s.read.bind-identities", "k8s:sso", "authelia", "delegable", False, "OBSERVE", "k8s/sso", ["split-shape"])],
[],
[check("split-shape", "every consumer binds as its own svc- account; no service binds as admin", SPLIT, 60)],
{"on_check_failure": "stop", "on_budget_exhausted": "stop", "on_unknown": "stop", "on_revert_failure": "stop-and-alert"},
{"wall_time_seconds": 300, "attempts": 3, "cost": {"compute_cpu_seconds": 30, "egress_bytes": 500000, "currency": "EUR", "amount": 0.01}},
["any write to LLDAP, Kubernetes or OpenBao"])
out = ROOT / "examples/bounded-execution-admission"
for name, d in [("bind-rotation-svc-authelia", rotation), ("bind-split-create-service-accounts", create),
("bind-split-switch-super-admin", superadmin), ("bind-split-read-only-verify", verify)]:
(out / f"{name}.yaml").write_text("# NK-WP-0056-T01 example: the bind split (NK-WP-0054) as admissions. Illustrative: the executor\n"
"# (T03) and its operations do not exist yet; files are pinned at the commit named in plan.source.\n"
+ yaml.safe_dump(d, sort_keys=False, width=120))
# Invalid: step 10, the founder's own password, is human-intrinsic.
bad = json.loads(json.dumps(superadmin)); s = bad["spec"]; s["id"] = "BEA-2026-0099"
s["plan"]["steps"].append(step("change-admin-password", "lldap.user.set-password", "lldap:admin", "lldap", "human-intrinsic", True, "ADMINISTER", "lldap", ["ld-admin-administers"], "switch-back"))
s["plan_digest"] = V.plan_digest(bad)
fx = ROOT / "tools/bounded-execution-admission/fixtures"; (fx / "invalid").mkdir(parents=True, exist_ok=True)
(fx / "invalid/human-intrinsic-step.yaml").write_text(yaml.safe_dump(bad, sort_keys=False, width=120))
print("commit", commit)