"""Silent, attended, compare-before-write metadata child for CCR-2026-0032. Does not read/write KV values, change issuer custody, or deploy Kubernetes objects. """ import argparse from datetime import datetime, timezone import hashlib import importlib.util import json import os from pathlib import Path import subprocess import sys import uuid ROOT = Path(__file__).resolve().parents[1] CCR = ROOT / 'credential-change-requests/CCR-2026-0032.yaml' POLICY = 'workload-kv-read-informed-decision-sitting-requester-eso' ROLE = 'auth/kubernetes/role/informed-decision-sitting-requester-eso' LEGACY_POLICY = 'path "platform/data/workloads/informed-decision/sitting-requester" {\n capabilities = ["read"]\n}\n' EXPECTED_POLICY = 'path "platform/data/workloads/informed-decision/sitting-requester" {\n capabilities = ["read"]\n}\n\npath "auth/token/lookup-self" {\n capabilities = ["read"]\n}\n\npath "auth/token/revoke-self" {\n capabilities = ["update"]\n}\n' class Refused(Exception): def __init__(self, code): self.code = code def cli(arguments, payload=None, *, missing_ok=False): try: result = subprocess.run(['bao', *arguments], input=payload, capture_output=True, timeout=30, check=False) except (OSError, subprocess.TimeoutExpired): raise Refused(4) from None if result.returncode: # bao read turns an empty 404 into this exact path-specific diagnostic. # Do not treat arbitrary stderr containing 404, denial or timeout as absence. if (missing_ok and arguments[:2] == ['read', '-format=json'] and len(arguments) == 3 and result.returncode == 2 and not result.stdout and result.stderr.strip() == ('No value found at ' + arguments[2]).encode()): return None raise Refused(4) if arguments[0] == 'write' and not result.stdout.strip(): return {} try: return json.loads(result.stdout) except (ValueError, UnicodeError): raise Refused(5) from None def role_matches(value): expected = { 'bound_service_account_names': ['informed-decision-eso'], 'bound_service_account_namespaces': ['informed-decision'], 'audience': 'openbao', 'token_policies': [POLICY], 'token_ttl': 900, 'token_max_ttl': 900, 'token_explicit_max_ttl': 900, 'token_no_default_policy': True, } return (isinstance(value, dict) and all(type(value.get(k)) is type(v) and value[k] == v for k, v in expected.items()) and value.get('token_period', 0) == 0 and value.get('bound_service_account_namespace_selector', '') == '' and value.get('token_type', 'default') in ('default', 'service')) def source(expected_digest): if hashlib.sha256(CCR.read_bytes()).hexdigest() != expected_digest: raise Refused(2) spec = importlib.util.spec_from_file_location('credential_change', ROOT / 'scripts/credential-change.py') module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) ccr, errors, _ = module.validate_ccr(CCR) if errors or ccr['status'] != 'approved' or ccr['openbao']['auth'].get('bound_claims_confirmed') is not True: raise Refused(2) bao = ccr['openbao'] auth = bao['auth'] if (bao['policy_name'] != POLICY or bao['kv_path'] != 'platform/workloads/informed-decision/sitting-requester' or bao['fields'] != ['CLIENT_SECRET'] or bao.get('metadata_read') is not False or bao.get('token_self_lifecycle', False) is not True or auth['method'] != 'kubernetes' or auth['mount'] != 'kubernetes' or auth['role'] != ROLE.rsplit('/', 1)[1]): raise Refused(2) payload = module.auth_payload(ccr) if payload != {'bound_service_account_names': ['informed-decision-eso'], 'bound_service_account_namespaces': ['informed-decision'], 'policies': POLICY, 'ttl': '15m', 'audience': 'openbao', 'token_max_ttl': '15m', 'token_explicit_max_ttl': '15m', 'token_no_default_policy': True}: raise Refused(2) policy = module.generated_policy_hcl(ccr) if policy != EXPECTED_POLICY or (ROOT / bao['policy_file']).read_text() != policy: raise Refused(2) return policy, payload def apply(policy, payload, *, command=cli, phase=lambda value: None): phase("read_policy") current_policy = command(['read', '-format=json', 'sys/policies/acl/' + POLICY], missing_ok=True) phase('read_role') current_role = command(['read', '-format=json', ROLE], missing_ok=True) # Check BOTH before mutating either; differing objects require a fresh review. existing_policy = current_policy.get('data', {}).get('policy') if current_policy is not None else None allowed_predecessor = policy == EXPECTED_POLICY and existing_policy == LEGACY_POLICY if current_policy is not None and existing_policy != policy and not allowed_predecessor: raise Refused(3) if current_role is not None and not role_matches(current_role.get('data')): raise Refused(3) if current_policy is None or allowed_predecessor: phase('write_policy') command(['write', '-format=json', 'sys/policies/acl/' + POLICY, '-'], json.dumps({'policy': policy}).encode()) if current_role is None: phase('write_role') command(['write', '-format=json', ROLE, '-'], json.dumps(payload).encode()) phase('verify_policy') verified_policy = command(['read', '-format=json', 'sys/policies/acl/' + POLICY]) phase('verify_role') verified_role = command(['read', '-format=json', ROLE]) if (verified_policy.get('data', {}).get('policy') != policy or not role_matches(verified_role.get('data'))): raise Refused(5) def main(): receipt = {'schema': 'requester-metadata-attempt.v1', 'ccr': 'CCR-2026-0032', 'phase': 'arguments', 'exit_code': 2, 'native_delivery_verified': False} def phase(value): receipt['phase'] = value try: parser = argparse.ArgumentParser(add_help=False) parser.add_argument('--expected-ccr-sha256', required=True) # Parsing is deliberately silent even on malformed arguments. with open(os.devnull, 'w') as sink: original = sys.stderr try: sys.stderr = sink args = parser.parse_args() finally: sys.stderr = original phase('attended_boundary') if (Path.home().parent.name != '.warden-attended-login' or os.getenv('BAO_TOKEN') or os.getenv('VAULT_TOKEN')): raise Refused(2) phase('source') policy, payload = source(args.expected_ccr_sha256) phase('identity') identity = cli(['token', 'lookup', '-format=json']).get('data', {}) policies = identity.get('policies', []) if 'platform-admin' not in policies or 'root' in policies: raise Refused(2) apply(policy, payload, phase=phase) phase('metadata_verified') receipt['exit_code'] = 0 return 0 except Refused as exc: receipt['exit_code'] = exc.code return exc.code except BaseException: return 2 finally: # Fixed metadata only. Never persist CLI bytes, exception text or identity data. try: directory = ROOT / 'docs/evidence/requester-metadata' directory.mkdir(parents=True, exist_ok=True) receipt['at'] = datetime.now(timezone.utc).isoformat() path = directory / (uuid.uuid4().hex + '.json') fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) with os.fdopen(fd, 'w') as stream: json.dump(receipt, stream, sort_keys=True) stream.write('\n') except BaseException: return 6 # Receipt could not be retained; metadata may have changed. if __name__ == '__main__': sys.exit(main())