railiance-platform/scripts/informed_decision_requester_metadata.py
codex 7713a45923 Prepare informed-decision workload reader and contained metadata child
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1046b-5629-7f52-badd-f718eadb6042
2026-10-04 08:59:02 +02:00

138 lines
5.7 KiB
Python

"""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
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import subprocess
import sys
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'
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:
# Only a structured API 404 is absence. Denial/transport failure is not.
if missing_ok and result.returncode == 2 and b'Code: 404' in result.stderr:
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 False
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 (ROOT / bao['policy_file']).read_text() != policy:
raise Refused(2)
return policy, payload
def apply(policy, payload, *, command=cli):
current_policy = command(['read', '-format=json', 'sys/policies/acl/' + POLICY], missing_ok=True)
current_role = command(['read', '-format=json', ROLE], missing_ok=True)
# Check BOTH before mutating either; differing objects require a fresh review.
if current_policy is not None and current_policy.get('data', {}).get('policy') != policy:
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:
command(['write', '-format=json', 'sys/policies/acl/' + POLICY, '-'],
json.dumps({'policy': policy}).encode())
if current_role is None:
command(['write', '-format=json', ROLE, '-'], json.dumps(payload).encode())
verified_policy = command(['read', '-format=json', 'sys/policies/acl/' + POLICY])
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():
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
if (Path.home().parent.name != '.warden-attended-login'
or os.getenv('BAO_TOKEN') or os.getenv('VAULT_TOKEN')):
raise Refused(2)
policy, payload = source(args.expected_ccr_sha256)
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)
return 0
except Refused as exc:
return exc.code
except BaseException:
return 2
if __name__ == '__main__':
sys.exit(main())