railiance-platform/scripts/factory_audit_delivery.py
codex ea028c1de6
Some checks failed
CCR intake gate / gate (push) Has been cancelled
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled
ADHOC-2026-11-03-T06: repair the last five drifted tests; suite green.
factory_audit_delivery re-pins the reviewed manifest after the CUST-WP-0073
metadata-only template fix and accepts only that exact template (data, type
and dataFrom still refused). Flex-auth signer tests assert the reviewed
state of CCR-2026-0050 and its pinned native plan instead of the
pre-review snapshot. Full suite: 1547 passed, 18 skipped, 0 failed.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 509845@bnt-lap001
Assistant-Session: ed7a7d95-d8eb-455c-b594-963fb1830f76
2026-11-04 06:26:27 +01:00

197 lines
13 KiB
Python

#!/usr/bin/env python3
"""Attended delivery and native scope checks for the approved factory audit senders.
No token values leave captured subprocess memory. The receipt is metadata only.
This does not deploy producers or claim their outbox/recovery acceptance.
"""
from __future__ import annotations
import argparse
import base64
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path
import time
import yaml
from factory_audit_custody import contracts, desired, receiver_check, snapshot, require, REGISTRY
from keycape_approval_custody import require_denied, read_optional, role_matches
from state_hub_preflight_lane import ROOT, LaneError, command, bao, data, revoke
CONFIRM='DELIVER CCR-2026-0021 CCR-2026-0022'
# Re-pinned 2026-11-04 after reviewing 80e0539 (CUST-WP-0073): metadata-only target templates.
MANIFEST_SHA256='a28aecfeb4afa5821b67bd555b45dfd590d1ecb481e673b6eb005365ca1d0cfe'
def custody_value(lane, raw):
require(raw['metadata']['version']==1 and set(raw['data'])=={'AUDIT_TOKEN','CUSTODY_REQUEST'}
and raw['data']['CUSTODY_REQUEST']==lane['ccr'], 'custody_provenance_mismatch')
token=raw['data']['AUDIT_TOKEN']
import re
require(isinstance(token,str) and re.fullmatch(r'[A-Za-z0-9_-]{64}',token), 'custody_token_shape')
return token
def verify_readers(kube, lanes, receipt, save):
values={}
for lane,row in zip(lanes,receipt['lanes']):
receipt['check']=lane['name']+':reader_metadata';save()
require(read_optional('sys/policies/acl/'+lane['policy'])['policy']==lane['hcl']
and role_matches(read_optional('auth/kubernetes/role/'+lane['role']),lane),'native_metadata_drift')
receipt['check']=lane['name']+':reader_login';save()
jwt=command(kube+['-n','external-secrets','create','token','external-secrets','--duration=10m']).stdout.decode().strip()
auth=data(bao(['write','-format=json','auth/kubernetes/login','-'],payload={'role':lane['role'],'jwt':jwt}))['auth']
token=auth['client_token']
try:
require(set(auth['token_policies'])=={lane['policy'],'default'} and 0<auth['lease_duration']<=900,'reader_scope_or_ttl_mismatch')
raw=data(bao(['read','-format=json',lane['kv']],token=token))['data']
values[lane['name']]=custody_value(lane,raw)
for path in [REGISTRY,lane['metadata'],next(x['kv'] for x in lanes if x!=lane)]:
require_denied(bao(['read','-format=json',path],token=token,allow_failure=True))
require_denied(bao(['list','-format=json','platform/metadata/workloads'],token=token,allow_failure=True))
row.update(reader_exact_path=True,registry_and_sibling_denied=True,metadata_and_listing_denied=True,reader_ttl=auth['lease_duration'])
finally: revoke(token)
require_denied(bao(['token','lookup','-format=json'],token=token,allow_failure=True))
row['reader_revocation_verified']=True
receipt['check']=lane['name']+':wrong_service_account';save()
bad_jwt=command(kube+['-n','external-secrets','create','token','default','--duration=10m']).stdout.decode().strip()
bad=bao(['write','-format=json','auth/kubernetes/login','-'],payload={'role':lane['role'],'jwt':bad_jwt},allow_failure=True)
if bad.returncode==0:
revoke(data(bad)['auth']['client_token']);raise LaneError('wrong_service_account_authenticated')
require(b'403' in bad.stderr and b'service account' in bad.stderr.lower(),'service_account_denial_inconclusive')
row['wrong_service_account_denied']=True
receipt['check']=lane['name']+':coding_agent_boundary';save()
child=data(bao(['token','create','-format=json','-policy='+lane['policy'],'-policy=agent-high-risk-boundary','-no-default-policy','-ttl=60s']))['auth']['client_token']
try:
for path in [lane['kv'],lane['metadata']]:require_denied(bao(['read','-format=json',path],token=child,allow_failure=True))
finally:revoke(child)
row['coding_agent_deny_wins']=True;save()
require(len(set(values.values()))==len(lanes),'sender_token_collision')
return values
def projection_matches(secret, external, lane, value):
require(set(secret['data'])=={lane['secret_key']}
and base64.b64decode(secret['data'][lane['secret_key']],validate=True).decode()==value,'projection_value_mismatch')
require(any(x['uid']==external['metadata']['uid'] and x.get('controller') is True
for x in secret['metadata'].get('ownerReferences',[])),'projection_owner_mismatch')
def guarded_manifest():
path=ROOT/'manifests/factory-audit-senders.yaml'
require(hashlib.sha256(path.read_bytes()).hexdigest()==MANIFEST_SHA256,'reviewed_manifest_changed')
return path,list(yaml.safe_load_all(path.read_text()))
def preflight_projection(kube, objects):
for obj in objects:
meta=obj['metadata'];cmd=kube+(['-n',meta['namespace']] if 'namespace' in meta else [])
result=command(cmd+['get',obj['kind'],meta['name'],'-o','json'],allow_failure=True)
if result.returncode:
require(b'NotFound' in result.stderr,'projection_presence_unknown');continue
live=data(result)
# Ignore API default fields, but never redirect an existing delivery lane.
require(all(live['spec'].get(k)==v for k,v in obj['spec'].items()),'existing_projection_drift')
# A target template may only carry the reviewed Secret metadata (CUST-WP-0073);
# anything that could transform or inject data is refused.
template=live['spec'].get('target',{}).get('template')
expected=obj['spec'].get('target',{}).get('template')
require(not live['spec'].get('dataFrom') and (template is None or (set(template)<={'metadata'} and template==expected)),'unexpected_projection_template')
def deliver(kube, lanes, values, receipt, save):
path,objects=guarded_manifest();preflight_projection(kube,objects)
receipt['check']='apply_reviewed_projection';save()
command(kube+['apply','-f',str(path)])
for lane,row in zip(lanes,receipt['lanes']):
receipt['check']=lane['name']+':projection_readiness';save()
command(kube+['wait','--for=condition=Ready','clustersecretstore/'+lane['store'],'--timeout=45s'])
command(kube+['-n',lane['name'],'wait','--for=condition=Ready','externalsecret/'+lane['secret'],'--timeout=45s'])
es=data(command(kube+['-n',lane['name'],'get','externalsecret',lane['secret'],'-o','json']))
secret=data(command(kube+['-n',lane['name'],'get','secret',lane['secret'],'-o','json']))
projection_matches(secret,es,lane,values[lane['name']])
row.update(external_secret_ready=True,projection_matches=True,secret_uid=secret['metadata']['uid'],secret_resource_version=secret['metadata']['resourceVersion']);save()
receipt['check']='receiver_registry_delivery';save()
version,body,rows,_=snapshot()
require(all(desired(l,values[l['name']]) in rows for l in lanes),'registry_scope_changed')
command(kube+['-n','audit-core','annotate','externalsecret','audit-core-senders','force-sync='+str(time.time_ns()),'--overwrite'])
for _ in range(25):
secret=data(command(kube+['-n','audit-core','get','secret','audit-core-senders','-o','json']))
if json.loads(base64.b64decode(secret['data']['senders.json']))==json.loads(body['senders.json']):break
time.sleep(2)
else:raise LaneError('receiver_registry_delivery_mismatch')
receipt.update(registry_version=version,receiver_registry_matches=True,receiver_secret_resource_version=secret['metadata']['resourceVersion']);save()
receipt['check']='receiver_reload';save()
command(kube+['-n','audit-core','rollout','restart','deployment/audit-core'])
command(kube+['-n','audit-core','rollout','status','deployment/audit-core','--timeout=50s'])
for _ in range(20):
try:
receipt['receiver_after']=receiver_check(kube,receipt['receiver_before']['image']);break
except LaneError as e:
if str(e)!='receiver_rollout_in_progress':raise
time.sleep(2)
else:raise LaneError('receiver_rollout_in_progress')
receipt['receiver_reloaded']=True;save()
def namespace_checks(kube, lanes, receipt, save):
import secrets
ns='factory-audit-check-'+secrets.token_hex(5)
created=data(command(kube+['create','namespace',ns,'-o','json']))
try:
receipt['temporary_namespace']={'name':ns,'uid':created['metadata']['uid']};save()
command(kube+['-n',ns,'create','serviceaccount','external-secrets'])
jwt=command(kube+['-n',ns,'create','token','external-secrets','--duration=10m']).stdout.decode().strip()
for lane,row in zip(lanes,receipt['lanes']):
receipt['check']=lane['name']+':wrong_namespace';save()
result=bao(['write','-format=json','auth/kubernetes/login','-'],payload={'role':lane['role'],'jwt':jwt},allow_failure=True)
if result.returncode==0:
revoke(data(result)['auth']['client_token']);raise LaneError('wrong_namespace_authenticated')
require(b'403' in result.stderr and b'namespace' in result.stderr.lower(),'namespace_denial_inconclusive')
obj={'apiVersion':'external-secrets.io/v1','kind':'ExternalSecret','metadata':{'name':lane['secret'],'namespace':ns},'spec':{'refreshInterval':'15s','secretStoreRef':{'kind':'ClusterSecretStore','name':lane['store']},'target':{'name':lane['secret'],'creationPolicy':'Owner'},'data':[{'secretKey':lane['secret_key'],'remoteRef':{'key':lane['kv'].removeprefix('platform/data/'),'property':'AUDIT_TOKEN'}}]}}
command(kube+['create','-f','-'],payload=obj)
for _ in range(20):
live=data(command(kube+['-n',ns,'get','externalsecret',lane['secret'],'-o','json']))
events=data(command(kube+['-n',ns,'get','events','--field-selector=involvedObject.uid='+live['metadata']['uid'],'-o','json']))
if any('not allowed' in e.get('message','').lower() and 'namespace' in e.get('message','').lower() for e in events['items']):break
time.sleep(2)
else:raise LaneError('store_namespace_denial_inconclusive')
absent=command(kube+['-n',ns,'get','secret',lane['secret'],'-o','json'],allow_failure=True)
require(absent.returncode!=0 and b'NotFound' in absent.stderr,'outside_namespace_secret_not_absent')
row.update(wrong_namespace_denied=True,store_namespace_denied=True);save()
finally:
command(kube+['delete','--raw=/api/v1/namespaces/'+ns,'-f','/dev/stdin'],payload={'apiVersion':'v1','kind':'DeleteOptions','preconditions':{'uid':created['metadata']['uid']}})
receipt['namespace_cleanup_requested']=True;save()
def main():
p=argparse.ArgumentParser(description=__doc__)
p.add_argument('--kubeconfig',required=True);p.add_argument('--server',required=True)
p.add_argument('--expected-receiver-image',required=True);p.add_argument('--confirm',required=True)
p.add_argument('--receipt',type=Path,required=True)
a=p.parse_args();receipt={'schema':'platform.factory-audit-delivery.v1','status':'refused','started_at':datetime.now(timezone.utc).isoformat(),'credential_values_emitted':False};fd=None
try:
lanes=contracts(approved=True);guarded_manifest();require(a.confirm==CONFIRM,'exact_confirmation_required')
require(Path.home().parent.name=='.warden-attended-login' and not os.getenv('BAO_TOKEN') and not os.getenv('VAULT_TOKEN'),'attended_warden_envelope_required')
fd=os.open(a.receipt,os.O_RDWR|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
def save():
os.lseek(fd,0,os.SEEK_SET);os.ftruncate(fd,0);os.write(fd,(json.dumps(receipt,indent=2)+'\n').encode());os.fsync(fd)
kube=['kubectl','--kubeconfig',a.kubeconfig,'--server',a.server,'--request-timeout=20s']
receipt['receiver_before']=receiver_check(kube,a.expected_receiver_image)
identity=data(bao(['token','lookup','-format=json']))['data']
require('platform-admin' in identity['policies'] and 'root' not in identity['policies'],'attended_platform_admin_required')
receipt['lanes']=[{k:x[k] for k in ['ccr','name','kv','store','secret','secret_key']} for x in lanes]
receipt['phase']='reader_verification';save();values=verify_readers(kube,lanes,receipt,save)
receipt['phase']='delivery';save();deliver(kube,lanes,values,receipt,save)
receipt['phase']='namespace_verification';save();namespace_checks(kube,lanes,receipt,save)
receipt.update(status='delivered_and_scope_verified_pending_producer_acceptance',completed_at=datetime.now(timezone.utc).isoformat());save();return 0
except Exception as exc:
receipt.update(status='refused',error=str(exc) if isinstance(exc,LaneError) else 'contained_delivery_failed')
if fd is not None:save()
return 1
finally:
if fd is not None:os.close(fd)
if __name__=='__main__':raise SystemExit(main())