net-kingdom/tools/bootstrap-recovery/verify-identity-delivery.py
tegwick 17a8fa1495
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
feat(recovery): verify scoped delivery and create first full offsite bundle
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
2026-10-16 09:21:55 +02:00

93 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""Attended native reader/ESO verification; only closed booleans leave custody."""
import argparse
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import sys
HERE=Path(__file__).resolve().parent
def load(name):
spec=importlib.util.spec_from_file_location(name,HERE/(name+'.py'))
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m);return m
setup=load('identity-credential-setup');runtime=load('identity-runtime')
from state_hub_preflight_lane import bao,data,capabilities,revoke
def verify(action,expected,receipt,kubeconfig):
receipt['phase']='validate_approved_contract'
ccr,lane=setup.contract(action,expected)
receipt['phase']='verify_attended_identity'
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 setup.Refused()
capture=runtime.capture;capture.KUBE=['kubectl','--kubeconfig',kubeconfig]
receipt['phase']='verify_cluster_identity'
if capture.get(None,'namespace','kube-system')['metadata']['uid']!=capture.prepare.CLUSTER_UID:raise setup.Refused()
def login(sa,audience,negative=False,namespace='sso'):
jwt=capture.command(['-n',namespace,'create','token',sa,'--audience='+audience,'--duration=10m']).decode().strip()
result=bao(['write','-format=json','auth/kubernetes/login','-'],payload={'role':lane['role'],'jwt':jwt},allow_failure=negative)
if negative:
if result.returncode==0:
revoke(data(result)['auth']['client_token']);raise setup.Refused()
if b'403' not in result.stderr and b'400' not in result.stderr:raise setup.Refused()
return None
return data(result)['auth']
receipt['phase']='native_reader_login'
auth=login(lane['service_account'],'openbao');token=auth['client_token']
receipt['native_reader_metadata']={k:auth.get(k) for k in ['token_policies','identity_policies','policies','lease_duration']}
try:
receipt['phase']='effective_reader_policy_ttl'
if (auth['token_policies']!=[lane['policy']] or auth.get('identity_policies')
or auth.get('policies',auth['token_policies'])!=[lane['policy']] or not 0<auth['lease_duration']<=900):raise setup.Refused()
paths=[lane['kv_path'],lane['kv_path'].replace('/data/','/metadata/',1),lane['kv_path']+'-sibling',
'platform/data/workloads/railiance/backup/offsite-lane','auth/token/create']
receipt['phase']='reader_capabilities'
cap=capabilities(token,paths)
receipt['capabilities_checked']=cap
if cap[paths[0]]!=['read'] or any(cap[p]!=['deny'] for p in paths[1:]):raise setup.Refused()
receipt['phase']='native_kv_eso_equality'
native=data(bao(['read','-format=json',lane['kv_path']],token=token))['data']
if set(native['data'])!={'NC_WEBDAV_TOKEN'} or native['data']['NC_WEBDAV_TOKEN']!=runtime.token(action):raise setup.Refused()
receipt['kv_version']=native['metadata']['version']
finally:revoke(token)
receipt['phase']='negative_reader_logins'
login('default','openbao',True);login(lane['service_account'],'wrong-audience',True)
proposed={'apiVersion':'v1','kind':'ServiceAccount','metadata':{'name':lane['service_account'],'namespace':'default'},'automountServiceAccountToken':False}
receipt['wrong_namespace_probe_attempted']=proposed['metadata']
test=json.loads(capture.command(['create','-f','-','-o','json'],json.dumps(proposed).encode()))
if test['metadata']['name']!=lane['service_account'] or test['metadata']['namespace']!='default':raise setup.Refused()
try:login(lane['service_account'],'openbao',True,'default')
finally:
path='/api/v1/namespaces/default/serviceaccounts/'+lane['service_account']
capture.command(['delete','--raw',path,'-f','-'],json.dumps({'apiVersion':'v1','kind':'DeleteOptions',
'preconditions':{'uid':test['metadata']['uid']}}).encode())
receipt['wrong_namespace_probe_cleanup_verified']=True
row=data(bao(['read','-format=json','auth/kubernetes/role/'+lane['role']]))['data']
setup.check_role(row,lane)
receipt.update(status='reader_and_delivery_verified',exact_namespace_binding_verified=True,
exact_policy_and_ttl_verified=True,wrong_service_account_denied=True,wrong_audience_denied=True,
wrong_namespace_denied=True,
metadata_sibling_escrow_and_token_create_denied=True,kv_write_denied=True,
eso_delivery_equality_verified=True,temporary_reader_revoked=True,secret_values_exported=False,
source_ccr=ccr['id'],ccr_sha256=expected)
def main():
p=argparse.ArgumentParser(description=__doc__);p.add_argument('action',choices=setup.PROFILES)
p.add_argument('--ccr-sha256',required=True);p.add_argument('--kubeconfig',required=True);p.add_argument('--receipt',required=True)
a=p.parse_args();fd=os.open(a.receipt,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
r={'status':'failed','source_sha256':hashlib.sha256(Path(__file__).read_bytes()).hexdigest()}
try:verify(a.action,a.ccr_sha256,r,a.kubeconfig)
except Exception:r.update(status='failed',error='native_delivery_verification_failed_diagnostics_suppressed')
finally:
with os.fdopen(fd,'w') as out:json.dump(r,out,indent=2);out.write('\n')
return int(r['status']=='failed')
if __name__=='__main__':raise SystemExit(main())