the-custodian/docs/changes/netkingdom-target-2026-10-16/native-preflight-r1/preflight.py
codex cf59a5f9ab
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Reject generated-only commits / generated-commit-guard (push) Successful in 7s
Retain native preflight and independent signer rollout reviews
2026-10-16 21:28:42 +02:00

121 lines
10 KiB
Python

"""Stdin-only public metadata and exact server-dry-run; never persist raw output.
Local coordinator supplies frozen JSON manifests. No SSH credential inspection.
"""
import datetime, hashlib, json, os, subprocess
def digest(x):
return hashlib.sha256(json.dumps(x,sort_keys=True,separators=(',',':')).encode()).hexdigest()
def invoke(argv, stdin=None):
try:
r=subprocess.run(argv,input=stdin,capture_output=True,text=True,timeout=35,env={**os.environ,'KUBECONFIG':'/etc/rancher/k3s/k3s.yaml'})
return r.returncode,r.stdout,bool(r.stderr)
except (OSError,subprocess.TimeoutExpired):
return -1,'',True
def query(resource, fields, namespace=None, name=None, allns=False):
argv=['kubectl','get',resource]
if namespace: argv+=['-n',namespace]
if name: argv+=[name]
if allns: argv+=['-A']
template=('{range .items[*]}' if not name else '')+'{"\\u001e"}'.join('{.'+f+'}' for f in fields)+'{"\\n"}'+('{end}' if not name else '')
rc,out,err=invoke(argv+['-o','jsonpath='+template])
rows=[]
if rc==0:
for line in out.split('\n'):
if not line: continue
parts=line.split('\x1e')
if len(parts)!=len(fields):return {'ok':False,'failure':'metadata-shape','stderr_present':err}
row={}
for key,value in zip(fields,parts):
try: row[key]=json.loads(value) if value else None
except ValueError: row[key]=value
rows.append(row)
return {'ok':rc==0,'exit_code':rc,'stderr_present':err,'rows':rows}
def require(condition,message):
if not condition:raise ValueError(message)
# Exact AST source extracted from pinned Platform module SHA256
# 4fb9ce6608ad04c4f5dc98a95796dc88a08a2453dc15c0d40c5c688fc02f573f
def deployment_projection(deployment):
spec=deployment['spec']['template']['spec'];containers=spec.get('containers',[])
require(type(containers) is list and len(containers)==1 and not spec.get('initContainers'),'single fixed signer container required')
item=containers[0]
# Never hash arbitrary env values or an entire Secret/spec response.
return {'service_account':spec.get('serviceAccountName'),'image':item.get('image'),
'args':item.get('args'),'volume_mounts':item.get('volumeMounts',[]),
'volumes':spec.get('volumes',[]),'security_context':spec.get('securityContext')}
def run(payload):
receipt={'schema':'netkingdom.native-public-preflight.v1','observed_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'source_pins':payload['source_pins'],'persistent_mutations':False,'held_fence':False,'observations':{}}
obs=receipt['observations']
obs['nodes']=query('nodes',['metadata.name','metadata.uid','status.addresses','status.allocatable','status.conditions[*].type','status.conditions[*].status','status.conditions[*].reason'])
obs['cluster']=query('namespace',['metadata.name','metadata.uid'],name='kube-system')
nodes=obs['nodes'].get('rows',[]);cluster=obs['cluster'].get('rows',[])
valid_node=any(type(row.get('status.addresses')) is list and any(type(address) is dict and address.get('address')=='92.205.62.239' and address.get('type') in {'InternalIP','ExternalIP'} for address in row['status.addresses']) for row in nodes if type(row) is dict)
if not obs['nodes'].get('ok') or not obs['cluster'].get('ok') or not valid_node or len(cluster)!=1 or type(cluster[0]) is not dict or cluster[0].get('metadata.uid')!='a553c742-0115-43d4-99a4-a5ca56fe0786':
receipt['refused']='target-identity';return receipt
# conditions contain only public node health, no annotations/secret/env/config.
common=['metadata.name','metadata.namespace','metadata.uid','metadata.generation','metadata.ownerReferences','metadata.deletionTimestamp']
obs['deployment']=query('deployment',common+['spec.replicas','spec.strategy','status.observedGeneration','status.replicas','status.readyReplicas','spec.template.spec.serviceAccountName','spec.template.spec.containers[*].image','spec.template.spec.containers[*].resources'],namespace='flex-auth',name='flex-auth-secrets-engine')
obs['replicasets']=query('replicasets',common+['spec.replicas','status.replicas'],namespace='flex-auth')
obs['pods']=query('pods',common+['status.phase','spec.nodeName','spec.serviceAccountName','spec.volumes[*].secret.secretName','spec.volumes[*].persistentVolumeClaim.claimName','spec.containers[*].image'],namespace='flex-auth')
obs['hpa']=query('hpa',common+['spec.scaleTargetRef','spec.minReplicas','spec.maxReplicas'],namespace='flex-auth')
# Only identity/owner/SA/volume references from potential workload writers.
for resource in ['jobs','cronjobs','statefulsets','daemonsets']:
obs[resource]=query(resource,common,namespace='flex-auth')
obs['quota']=query('resourcequota',['metadata.name','spec.hard','status.hard','status.used'],namespace='flex-auth')
obs['limits']=query('limitrange',['metadata.name','spec.limits'],namespace='flex-auth')
obs['pvc']=query('pvc',common+['spec.storageClassName','spec.accessModes','spec.resources.requests.storage','spec.volumeName','status.phase','status.capacity'],namespace='flex-auth')
obs['storageclass']=query('storageclass',['metadata.name','metadata.uid','provisioner','reclaimPolicy','volumeBindingMode','allowVolumeExpansion'],name='local-path')
obs['applications']=query('applications.argoproj.io',common+['spec.destination','spec.syncPolicy.automated','status.operationState.phase','status.sync.status','status.resources[*].kind','status.resources[*].namespace','status.resources[*].name'],namespace='argocd')
# Metrics and allocation JSONPath select only resource fields of all pods.
obs['resource_requests']=query('pods',['metadata.namespace','metadata.name','spec.nodeName','status.phase','spec.containers[*].resources','spec.initContainers[*].resources','spec.overhead'],allns=True)
rc,out,err=invoke(['kubectl','top','nodes','--no-headers']);obs['node_usage']={'ok':rc==0,'exit_code':rc,'stderr_present':err,'public_metrics':out.strip() if rc==0 else None}
rc,out,err=invoke(['df','-B1','--output=size,used,avail,pcent,target','/var/lib/rancher/k3s/storage']);obs['storage_free']={'ok':rc==0,'exit_code':rc,'stderr_present':err,'public_filesystem_metrics':out.strip() if rc==0 else None}
# helm history returns release metadata, no values/manifests/Secret reads by wrapper.
rc,out,err=invoke(['helm','history','flex-auth-secrets-engine','-n','flex-auth','--output','json','--max','10'])
history={'ok':rc==0,'exit_code':rc,'stderr_present':err}
try: history['revisions']=[{k:x.get(k) for k in ('revision','updated','status','chart','app_version')} for x in json.loads(out)] if rc==0 else []
except (ValueError,TypeError):history={'ok':False,'failure':'history-shape'}
obs['helm_history']=history
# Sanitized client comparison: no arbitrary live args or projection fields emitted.
mapping={'service_account':'spec.template.spec.serviceAccountName','image':'spec.template.spec.containers[0].image','args':'spec.template.spec.containers[0].args','volume_mounts':'spec.template.spec.containers[0].volumeMounts','volumes':'spec.template.spec.volumes','security_context':'spec.template.spec.securityContext'}
live=query('deployment',list(mapping.values()),namespace='flex-auth',name='flex-auth-secrets-engine')
if live['ok'] and len(live['rows'])==1:
current={k:live['rows'][0][path] for k,path in mapping.items()}
for key in ['volume_mounts','volumes']:current[key]=current[key] or []
obs['client_projection_diff']={'ok':True,'live_sha256':digest(current),'candidate_sha256':digest(payload['expected_projection']),'differing_fields':[k for k in current if current[k]!=payload['expected_projection'][k]],'field_digests':{k:{'live':digest(current[k]),'candidate':digest(payload['expected_projection'][k])} for k in current}}
else:obs['client_projection_diff']={'ok':False}
receipt['dry_runs']=[]
for obj in payload['resources']:
kind=obj['kind'];name=obj['metadata']['name']
if kind not in {'ServiceAccount','Role','RoleBinding','ClusterRole','ClusterRoleBinding','Service','Deployment','NetworkPolicy','PersistentVolumeClaim'} or obj['metadata'].get('namespace','flex-auth')!='flex-auth':
receipt['refused']='resource-allowlist';return receipt
fields=list(mapping.values())+['spec.replicas']
selected='jsonpath='+('{"\\u001e"}'.join('{.'+f+'}' for f in fields)) if kind=='Deployment' else 'name'
rc,out,err=invoke(['kubectl','apply','--dry-run=server','--validate=strict','-n','flex-auth','-f','-','-o',selected],json.dumps(obj))
row={'kind':kind,'name':name,'exit_code':rc,'stderr_present':err,'admitted':rc==0}
if rc==0:
try:
if kind=='Deployment':
raw=out.strip().split('\x1e')
if len(raw)!=len(fields):raise ValueError('projection-shape')
values=[]
for value in raw:
try:values.append(json.loads(value) if value else None)
except ValueError:values.append(value)
p=dict(zip(mapping,values[:-1]))
row.update(replicas=values[-1],projection_sha256=digest(p),expected_projection_sha256=digest(payload['expected_projection']),projection_matches=p==payload['expected_projection'],differing_projection_fields=[k for k in p if p[k]!=payload['expected_projection'].get(k)])
except (ValueError,TypeError,KeyError):row.update(admitted=False,failure='admission-shape')
receipt['dry_runs'].append(row)
receipt['completed_at']=datetime.datetime.now(datetime.timezone.utc).isoformat()
receipt['limitations']=['Dated metadata and dry-run only; no held writer fence or native owner disposition.','Helm values/manifests/config/env/annotations/Secrets not fetched. Live args only compared and hashed.','Application workload refs and current writer hints do not authenticate manual/CI writers.','No actual PVC binding/PV Retain, stop/create/start/custody or token request.','kubectl apply dry-run is admission evidence, not Helm upgrade execution equivalence.']
return receipt
if __name__=='__main__':
try: result=run(PAYLOAD)
except Exception: result={'schema':'netkingdom.native-public-preflight.v1','refused':'sanitized-wrapper-failure','persistent_mutations':False,'held_fence':False}
print(json.dumps(result,sort_keys=True))