#!/usr/bin/env python3 """Attended OpenBao setup and proof for the bounded executor's two lanes (NK-WP-0056-T10, T06). write CCR-2026-0058 policy only. Its Kubernetes role is window-scoped: created per admitted run, never here. Also sets cas_required on the svc-authelia key. journey CCR-2026-0059 policy and the standing read role for sso/nk-bind-executor. setup: under both approved, sha256-bound CCRs (binding confirmed) and an attended platform-admin session: check that the coding-agent fence denies both paths, apply both policies, set cas_required, create the journey role, prove that the write role does not exist, and prove that the fence beats both policies. verify: read back policies, cas_required and the role; log in as sso/nk-bind-executor (audience openbao) and prove that the token reads only the journey path, with exactly its policy and at most 15m. Prove that a wrong ServiceAccount, a wrong audience and a login to the write role are all denied. Receipts hold closed booleans only. The journey values are read into memory to check their field names, and never printed or written. """ import argparse import hashlib import importlib.util import json import os import subprocess import sys from pathlib import Path HERE = Path(__file__).resolve().parent REPO = HERE.parents[1] PLATFORM = Path(os.environ.get('NK_PLATFORM_ROOT', REPO.parent / 'railiance-platform')) sys.path.insert(0, str(PLATFORM / 'scripts')) from state_hub_preflight_lane import bao, data, capabilities, revoke # noqa: E402 from repair_eso_kubernetes_auth import role_payload, check_role # noqa: E402 from telemetry_smtp_delivery import ensure # noqa: E402 SA, NS = 'nk-bind-executor', 'sso' APPROVERS = {'platform-owner', 'net-kingdom-owner'} BOUNDARY = 'sys/policies/acl/agent-high-risk-boundary' BINDS = 'platform/data/workloads/net-kingdom/directory-binds/' LANES = { 'write': { 'ccr': 'CCR-2026-0058-net-kingdom-bind-executor-svc-authelia-write.yaml', 'request_type': 'workload-kv-write-next-version', 'kv': 'platform/workloads/net-kingdom/directory-binds/svc-authelia', 'fields': ['LDAP_BIND_PASSWORD'], 'policy': 'net-kingdom-bind-executor-svc-authelia-write', 'policy_text': ('path "platform/data/workloads/net-kingdom/directory-binds/svc-authelia" {\n' ' capabilities = ["read", "update"]\n allowed_parameters = {\n "data" = []\n' ' "options" = []\n }\n}\n\npath "auth/token/lookup-self" {\n capabilities = ["read"]\n}\n\n' 'path "auth/token/revoke-self" {\n capabilities = ["update"]\n}\n'), }, 'journey': { 'ccr': 'CCR-2026-0059-net-kingdom-bind-executor-syn-journey-read.yaml', 'request_type': 'workload-kv-read', 'kv': 'platform/workloads/net-kingdom/synthetic-journey/syn-journey', 'fields': ['PASSWORD', 'TOTP_SEED'], 'policy': 'net-kingdom-bind-executor-syn-journey-read', 'policy_text': ('path "platform/data/workloads/net-kingdom/synthetic-journey/syn-journey" ' '{ capabilities = ["read"] }\n' 'path "auth/token/lookup-self" { capabilities = ["read"] }\n' 'path "auth/token/revoke-self" { capabilities = ["update"] }\n'), }, } for _lane in LANES.values(): _lane.update(role=_lane['policy'], service_account=SA, namespace=NS, data_path=_lane['kv'].replace('platform/', 'platform/data/', 1), metadata_path=_lane['kv'].replace('platform/', 'platform/metadata/', 1)) CAS_KEY = ['-mount=platform', 'workloads/net-kingdom/directory-binds/svc-authelia'] class Refused(Exception): pass def load(name, path): spec = importlib.util.spec_from_file_location(name, path) module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module) return module def contract(name, expected): lane = LANES[name] path = PLATFORM / 'credential-change-requests' / lane['ccr'] if hashlib.sha256(path.read_bytes()).hexdigest() != expected: raise Refused(name + ':ccr_digest') ccr, errors, _ = load('credential_change', PLATFORM / 'scripts/credential-change.py').validate_ccr(path) if errors or ccr['status'] not in {'approved', 'applied', 'verified', 'active'}: raise Refused(name + ':ccr_status') approved = {r['reviewer'] for r in ccr['review'].get('comments', []) if r.get('decision') == 'approved'} if not APPROVERS <= approved: raise Refused(name + ':ccr_approval') a = ccr['openbao']; auth = a['auth'] if (ccr['request_type'] != lane['request_type'] or a['kv_path'] != lane['kv'] or a['fields'] != lane['fields'] or a['policy_name'] != lane['policy'] or auth['role'] != lane['role'] or auth['policies'] != [lane['policy']] or auth.get('method') != 'kubernetes' or auth.get('audience') != 'openbao' or auth['bound_claims'] != {'service_account_names': [SA], 'service_account_namespaces': [NS]} or auth.get('token_no_default_policy') is not True or any(auth.get(k) != '15m' for k in ('ttl', 'token_max_ttl', 'token_explicit_max_ttl'))): raise Refused(name + ':ccr_shape') if auth.get('bound_claims_confirmed') is not True: raise Refused(name + ':binding_unconfirmed') if (PLATFORM / a['policy_file']).read_text() != lane['policy_text']: raise Refused(name + ':policy_file') return ccr def attended_platform_admin(): ident = data(bao(['token', 'lookup', '-format=json']))['data'] if ('platform-admin' not in ident.get('policies', []) or 'root' in ident.get('policies', []) or not ident.get('path', '').startswith('auth/netkingdom/')): raise Refused('attended_platform_admin_required') def fence_present(): current = data(bao(['read', '-format=json', BOUNDARY]))['data']['policy'] return all('path "' + p + '" { capabilities = ["deny"] }' in current for lane in LANES.values() for p in (lane['data_path'], lane['metadata_path'])) def policy_check(lane): def check(actual): if actual.get('policy') != lane['policy_text']: raise Refused(lane['policy'] + ':policy_drift') return check def cas_required(): return data(bao(['kv', 'metadata', 'get', '-format=json', *CAS_KEY]))['data'].get('cas_required') is True def write_role_absent(): result = bao(['read', '-format=json', 'auth/kubernetes/role/' + LANES['write']['role']], allow_failure=True) return result.returncode != 0 and b'No value found' in result.stdout + result.stderr def contracts(digests): return {name: contract(name, digests[name]) for name in LANES} def setup(digests, receipt): receipt['phase'] = 'contract'; ccrs = contracts(digests) receipt['phase'] = 'identity'; attended_platform_admin() receipt['phase'] = 'coding_agent_fence' if not fence_present(): raise Refused('agent_fence_missing') # established by CCR-2026-0053 and -0057 receipt['phase'] = 'policies' for lane in LANES.values(): ensure('sys/policies/acl/' + lane['policy'], {'policy': lane['policy_text']}, policy_check(lane)) receipt['phase'] = 'cas_required' if not cas_required(): bao(['kv', 'metadata', 'patch', '-cas-required=true', *CAS_KEY]) if not cas_required(): raise Refused('cas_required_not_set') receipt['phase'] = 'journey_role' journey = LANES['journey'] ensure('auth/kubernetes/role/' + journey['role'], role_payload(journey), lambda actual: check_role(actual, journey)) receipt['phase'] = 'write_role_absent' if not write_role_absent(): raise Refused('write_role_exists_outside_a_window') receipt['phase'] = 'fence_effective' for lane in LANES.values(): child = data(bao(['token', 'create', '-format=json', '-policy=' + lane['policy'], '-policy=agent-high-risk-boundary', '-no-default-policy', '-ttl=60s']))['auth']['client_token'] try: caps = capabilities(child, [lane['data_path'], lane['metadata_path']]) if any(v != ['deny'] for v in caps.values()): raise Refused(lane['policy'] + ':fence_ineffective') finally: revoke(child) receipt.update(phase='done', status='lanes_prepared', policies_applied=True, cas_required=True, journey_role_created=True, write_role_absent=True, coding_agents_denied=True, source_ccrs={n: c['id'] for n, c in ccrs.items()}, ccr_sha256=digests) def kube(kubeconfig, args): return subprocess.run(['kubectl', '--kubeconfig', kubeconfig, *args], check=True, capture_output=True).stdout def verify(digests, kubeconfig, receipt): receipt['phase'] = 'contract'; ccrs = contracts(digests) receipt['phase'] = 'identity'; attended_platform_admin() receipt['phase'] = 'read_back' for lane in LANES.values(): policy_check(lane)(data(bao(['read', '-format=json', 'sys/policies/acl/' + lane['policy']]))['data']) journey, write = LANES['journey'], LANES['write'] check_role(data(bao(['read', '-format=json', 'auth/kubernetes/role/' + journey['role']]))['data'], journey) if not cas_required(): raise Refused('cas_required_not_set') if not write_role_absent(): raise Refused('write_role_exists_outside_a_window') if not fence_present(): raise Refused('agent_fence_missing') def login(sa, audience, role, negative=False): jwt = kube(kubeconfig, ['-n', NS, 'create', 'token', sa, '--audience=' + audience, '--duration=10m']).decode().strip() result = bao(['write', '-format=json', 'auth/kubernetes/login', '-'], payload={'role': role, 'jwt': jwt}, allow_failure=negative) if negative: if result.returncode == 0: revoke(data(result)['auth']['client_token']); raise Refused('negative_login_accepted') if b'403' not in result.stderr and b'400' not in result.stderr: raise Refused('negative_login_unclear') return None return data(result)['auth'] receipt['phase'] = 'journey_login' auth = login(SA, 'openbao', journey['role']); token = auth['client_token'] try: if (auth['token_policies'] != [journey['policy']] or auth.get('identity_policies') or not 0 < auth['lease_duration'] <= 900): raise Refused('journey_policy_or_ttl') receipt['phase'] = 'journey_capabilities' others = [BINDS + n for n in ('svc-authelia', 'svc-keycape', 'svc-privacyidea', 'svc-provisioner')] paths = [journey['data_path'], journey['metadata_path'], write['metadata_path'], *others, 'auth/token/create'] caps = capabilities(token, paths) if caps[paths[0]] != ['read'] or any(caps[p] != ['deny'] for p in paths[1:]): raise Refused('journey_capabilities') receipt['phase'] = 'journey_read' native = data(bao(['read', '-format=json', journey['data_path']], token=token))['data'] if sorted(native['data']) != sorted(journey['fields']): raise Refused('journey_fields') receipt['journey_kv_version'] = native['metadata']['version'] finally: revoke(token) receipt['phase'] = 'negative_logins' login('default', 'openbao', journey['role'], True) login(SA, 'wrong-audience', journey['role'], True) login(SA, 'openbao', write['role'], True) receipt.update(phase='done', status='lanes_verified', exact_policies_and_role=True, cas_required=True, journey_reads_only_its_path=True, journey_lease_at_most_15m=True, write_role_absent=True, wrong_service_account_denied=True, wrong_audience_denied=True, write_login_denied=True, coding_agents_denied=True, temporary_token_revoked=True, source_ccrs={n: c['id'] for n, c in ccrs.items()}, ccr_sha256=digests) def main(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument('action', choices=['setup', 'verify']) p.add_argument('--write-ccr-sha256', required=True, help='sha256 of the CCR-2026-0058 file') p.add_argument('--journey-ccr-sha256', required=True, help='sha256 of the CCR-2026-0059 file') p.add_argument('--kubeconfig', help='admin kubeconfig; required for verify') p.add_argument('--receipt', required=True) a = p.parse_args() if a.action == 'verify' and not a.kubeconfig: p.error('--kubeconfig is required for verify') digests = {'write': a.write_ccr_sha256, 'journey': a.journey_ccr_sha256} fd = os.open(a.receipt, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) r = {'status': 'failed', 'action': a.action, 'values_recorded': False, 'source_sha256': hashlib.sha256(Path(__file__).read_bytes()).hexdigest()} try: if a.action == 'setup': setup(digests, r) else: verify(digests, a.kubeconfig, r) except Exception as error: r.update(status='failed', reason=str(error) if isinstance(error, Refused) else type(error).__name__) finally: with os.fdopen(fd, 'w') as out: json.dump(r, out, indent=2, sort_keys=True); out.write('\n') print(json.dumps({k: r[k] for k in ('status', 'phase', 'reason') if k in r})) return int(r['status'] == 'failed') if __name__ == '__main__': raise SystemExit(main())