2026-09-11 01:21:37 +02:00
|
|
|
"""Render a review-only Kubernetes candidate. Never apply or provision custody."""
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
import ipaddress
|
|
|
|
|
import json
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
import re
|
|
|
|
|
import hashlib
|
|
|
|
|
|
|
|
|
|
NAME = 'informed-decision'
|
|
|
|
|
SHA = re.compile(r'sha256:[0-9a-f]{64}')
|
|
|
|
|
LABEL = {'app.kubernetes.io/name': NAME}
|
|
|
|
|
|
|
|
|
|
|
2026-10-04 20:17:24 +02:00
|
|
|
def validate_policy(policy):
|
2026-09-11 01:21:37 +02:00
|
|
|
if not isinstance(policy,dict) or set(policy)!={'origin','package','version','package_digest','pod_name'}:
|
|
|
|
|
raise ValueError('exact policy endpoint, package pins and pod selector required')
|
|
|
|
|
if not re.fullmatch(r'http://[a-z0-9-]+\.flex-auth\.svc\.cluster\.local:8080',policy['origin']):
|
|
|
|
|
raise ValueError('exact in-cluster Flex Auth endpoint required')
|
|
|
|
|
if not re.fullmatch(r'[a-z0-9][a-z0-9.-]{0,62}',policy['pod_name']):
|
|
|
|
|
raise ValueError('exact Flex Auth pod label required')
|
|
|
|
|
if not SHA.fullmatch(policy['package_digest']) or any(
|
|
|
|
|
not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9._-]{0,127}',policy[key]) for key in ['package','version']):
|
|
|
|
|
raise ValueError('exact policy package/version/digest required')
|
2026-10-04 20:17:24 +02:00
|
|
|
|
|
|
|
|
|
2026-10-04 21:40:19 +02:00
|
|
|
def validate_workload(value):
|
|
|
|
|
if (not isinstance(value,dict) or set(value)!={'registries_configmap','peers','api_egress','intake','lifecycle'}
|
|
|
|
|
or any(type(value[k]) is not bool for k in ('intake','lifecycle'))
|
|
|
|
|
or not (value['intake'] or value['lifecycle'])
|
|
|
|
|
or not re.fullmatch(r'[a-z0-9][a-z0-9.-]{0,62}',value['registries_configmap'])):
|
|
|
|
|
raise ValueError('exact workload listener configuration required')
|
|
|
|
|
for key in ('peers','api_egress'):
|
|
|
|
|
if not isinstance(value[key],list) or not 1<=len(value[key])<=16:
|
|
|
|
|
raise ValueError('bounded exact workload peers required')
|
|
|
|
|
for item in value['peers']:
|
|
|
|
|
if (not isinstance(item,dict) or set(item)!={'namespace','app'}
|
|
|
|
|
or any(not re.fullmatch(r'[a-z0-9][a-z0-9.-]{0,62}',x) for x in item.values())):
|
|
|
|
|
raise ValueError('exact workload namespace and pod label required')
|
|
|
|
|
for item in value['api_egress']:
|
|
|
|
|
if not isinstance(item,dict) or set(item)!={'ip','port'} or type(item['port']) is not int or item['port'] not in (443,6443):
|
|
|
|
|
raise ValueError('exact Kubernetes API endpoint required')
|
|
|
|
|
address=ipaddress.ip_address(item['ip'])
|
|
|
|
|
if address.is_loopback or address.is_unspecified or address.is_multicast:
|
|
|
|
|
raise ValueError('routable API address required')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_workload_objects(objects,pod,value,obj,readonly,peer):
|
|
|
|
|
container=pod['containers'][0]
|
|
|
|
|
container['ports'].append({'name':'workload','containerPort':8081})
|
|
|
|
|
container['volumeMounts'].extend([
|
|
|
|
|
readonly('kubernetes-api','/var/run/secrets/informed-decision/kubernetes'),
|
|
|
|
|
readonly('workload-registry','/workload-registry')])
|
|
|
|
|
pod['volumes'].extend([
|
|
|
|
|
{'name':'kubernetes-api','projected':{'defaultMode':0o440,'sources':[
|
|
|
|
|
{'serviceAccountToken':{'path':'token','expirationSeconds':600}},
|
|
|
|
|
{'configMap':{'name':'kube-root-ca.crt','items':[{'key':'ca.crt','path':'ca.crt'}]}}]}},
|
|
|
|
|
{'name':'workload-registry','configMap':{'name':value['registries_configmap'],'defaultMode':0o444}}])
|
|
|
|
|
if value['intake']:
|
|
|
|
|
container['volumeMounts'].append(readonly('sitting-requester','/var/run/secrets/informed-decision/requester'))
|
|
|
|
|
pod['volumes'].append({'name':'sitting-requester','secret':{
|
|
|
|
|
'secretName':'informed-decision-sitting-requester','defaultMode':0o440,'optional':False,
|
|
|
|
|
'items':[{'key':'client-secret','path':'client-secret'}]}})
|
|
|
|
|
objects.append(obj('v1','Service',NAME+'-workload',spec={'type':'ClusterIP',
|
|
|
|
|
'selector':{**LABEL,'app.kubernetes.io/component':'review'},
|
|
|
|
|
'ports':[{'name':'workload','port':8081,'targetPort':'workload','protocol':'TCP'}]}))
|
|
|
|
|
objects[5]['spec']['ingress'].append({'from':[peer(p['namespace'],p['app']) for p in value['peers']],
|
|
|
|
|
'ports':[{'protocol':'TCP','port':8081}]})
|
|
|
|
|
for endpoint in value['api_egress']:
|
|
|
|
|
address=ipaddress.ip_address(endpoint['ip'])
|
|
|
|
|
objects[5]['spec']['egress'].append({'to':[{'ipBlock':{'cidr':str(address)+'/'+str(address.max_prefixlen)}}],
|
|
|
|
|
'ports':[{'protocol':'TCP','port':endpoint['port']}]})
|
|
|
|
|
role=obj('rbac.authorization.k8s.io/v1','ClusterRole',NAME+'-tokenreview',rules=[
|
|
|
|
|
{'apiGroups':['authentication.k8s.io'],'resources':['tokenreviews'],'verbs':['create']}])
|
|
|
|
|
binding=obj('rbac.authorization.k8s.io/v1','ClusterRoleBinding',NAME+'-tokenreview',
|
|
|
|
|
roleRef={'apiGroup':'rbac.authorization.k8s.io','kind':'ClusterRole','name':NAME+'-tokenreview'},
|
|
|
|
|
subjects=[{'kind':'ServiceAccount','name':'review','namespace':NAME}])
|
|
|
|
|
for item in (role,binding):
|
|
|
|
|
item['metadata'].pop('namespace')
|
|
|
|
|
item['metadata']['annotations']['informed-decision.coulomb.social/admission']='proposal; INFD-WP-0007/INFD-WP-0005 owner review required'
|
|
|
|
|
objects.append(item)
|
|
|
|
|
|
|
|
|
|
|
2026-10-04 20:17:24 +02:00
|
|
|
def render(inputs):
|
2026-10-04 21:40:19 +02:00
|
|
|
if not isinstance(inputs,dict) or (set(inputs)-{'ordinary_approval_ids','lifecycle_policy','lifecycle_delivery','workload_api','intake_attributes','intake_delivery'})!={'image','policy','keycape_egress_ips','storage_class'}:
|
2026-10-04 20:17:24 +02:00
|
|
|
raise ValueError('exact image, policy, KeyCape egress IPs and storage class inputs required')
|
|
|
|
|
if not re.fullmatch(r'forgejo\.coulomb\.social/coulomb/informed-decision@sha256:[0-9a-f]{64}',inputs['image']):
|
|
|
|
|
raise ValueError('immutable Informed Decision registry digest required')
|
|
|
|
|
policy=inputs['policy']
|
|
|
|
|
validate_policy(policy)
|
2026-09-11 01:21:37 +02:00
|
|
|
if not re.fullmatch(r'[a-z0-9][a-z0-9.-]{0,62}',inputs['storage_class']):
|
|
|
|
|
raise ValueError('explicit storage class required')
|
|
|
|
|
ips=inputs['keycape_egress_ips']
|
|
|
|
|
if not isinstance(ips,list) or not 1<=len(ips)<=4:
|
|
|
|
|
raise ValueError('one to four exact admitted KeyCape egress addresses required')
|
|
|
|
|
cidrs=[]
|
|
|
|
|
for ip in ips:
|
|
|
|
|
address=ipaddress.ip_address(ip)
|
|
|
|
|
if not address.is_global:
|
|
|
|
|
raise ValueError('KeyCape public origin requires exact global addresses')
|
|
|
|
|
cidrs.append(str(address)+'/'+str(address.max_prefixlen))
|
|
|
|
|
config={'schema':'informed-decision.review-runtime.v1','evidence_db':'/data/private/review.sqlite',
|
|
|
|
|
'approval_origin':'http://approval-engine.approval-engine.svc.cluster.local:8080',
|
|
|
|
|
'policy':{key:policy[key] for key in ['origin','package','version','package_digest']},
|
|
|
|
|
'audit':{'origin':'http://audit-core.audit-core.svc.cluster.local:8080',
|
|
|
|
|
'sender_token_file':'/var/run/secrets/informed-decision/audit/token'}}
|
2026-09-27 20:45:28 +02:00
|
|
|
if 'ordinary_approval_ids' in inputs:
|
|
|
|
|
ids=inputs['ordinary_approval_ids']
|
|
|
|
|
if (not isinstance(ids,list) or len(ids)>32
|
|
|
|
|
or any(not isinstance(i,str) or not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9_-]{0,127}',i) for i in ids)
|
|
|
|
|
or len(set(ids))!=len(ids)):
|
|
|
|
|
raise ValueError('exact ordinary approval ids required')
|
|
|
|
|
config['ordinary_approval_ids']=sorted(ids)
|
2026-09-11 01:21:37 +02:00
|
|
|
config['policy']['caller_token_file']='/var/run/secrets/informed-decision/policy/token'
|
2026-10-04 20:17:24 +02:00
|
|
|
lifecycle=inputs.get('lifecycle_policy')
|
|
|
|
|
if 'lifecycle_policy' in inputs:
|
|
|
|
|
validate_policy(lifecycle)
|
|
|
|
|
if lifecycle['package']==policy['package'] or inputs.get('lifecycle_delivery') is not True:
|
|
|
|
|
raise ValueError('separate lifecycle policy and explicit lifecycle delivery required')
|
|
|
|
|
config['lifecycle_policy']={key:lifecycle[key] for key in ['origin','package','version','package_digest']}
|
|
|
|
|
config['lifecycle_policy']['caller_token_file']=config['policy']['caller_token_file']
|
|
|
|
|
if 'lifecycle_delivery' in inputs:
|
|
|
|
|
if type(inputs['lifecycle_delivery']) is not bool:
|
|
|
|
|
raise ValueError('lifecycle delivery must be boolean')
|
|
|
|
|
config['lifecycle_delivery']=inputs['lifecycle_delivery']
|
|
|
|
|
|
2026-10-04 21:40:19 +02:00
|
|
|
if 'intake_attributes' in inputs:
|
|
|
|
|
if type(inputs['intake_attributes']) is not bool:
|
|
|
|
|
raise ValueError('intake attributes must be boolean')
|
|
|
|
|
config['intake_attributes']=inputs['intake_attributes']
|
|
|
|
|
if 'intake_delivery' in inputs:
|
|
|
|
|
if inputs['intake_delivery'] != {'hub_origin':'http://state-hub.state-hub.svc.cluster.local:8000'}:
|
|
|
|
|
raise ValueError('exact State Hub delivery origin required')
|
|
|
|
|
config['intake_delivery']=inputs['intake_delivery']
|
|
|
|
|
if 'workload_api' in inputs:
|
|
|
|
|
workload=inputs['workload_api']
|
|
|
|
|
validate_workload(workload)
|
|
|
|
|
if workload['lifecycle'] and (not lifecycle or inputs.get('lifecycle_delivery') is not True):
|
|
|
|
|
raise ValueError('workload lifecycle needs policy and evidence delivery')
|
|
|
|
|
if workload['intake'] and (inputs.get('intake_attributes') is not True or 'intake_delivery' not in inputs):
|
|
|
|
|
raise ValueError('private intake needs attribute and delivery composition')
|
|
|
|
|
config['workload_api']={'token_review':{'origin':'https://kubernetes.default.svc:443',
|
|
|
|
|
'ca_file':'/var/run/secrets/informed-decision/kubernetes/ca.crt',
|
|
|
|
|
'token_file':'/var/run/secrets/informed-decision/kubernetes/token'}}
|
|
|
|
|
if workload['lifecycle']:
|
|
|
|
|
config['workload_api']['lifecycle_callers_file']='/workload-registry/lifecycle-callers.json'
|
|
|
|
|
if workload['intake']:
|
|
|
|
|
config['workload_api']['intake']={
|
|
|
|
|
'secret_file':'/var/run/secrets/informed-decision/requester/client-secret',
|
|
|
|
|
'submitters_file':'/workload-registry/intake-submitters.json',
|
|
|
|
|
'groups_file':'/workload-registry/review-groups.json'}
|
|
|
|
|
|
2026-09-11 01:21:37 +02:00
|
|
|
raw=json.dumps(config,sort_keys=True,separators=(',',':'))
|
|
|
|
|
config_name=NAME+'-runtime-'+hashlib.sha256(raw.encode()).hexdigest()[:12]
|
|
|
|
|
def obj(api,kind,name,**values):
|
|
|
|
|
return {'apiVersion':api,'kind':kind,'metadata':{'name':name,'namespace':NAME,
|
|
|
|
|
'annotations':{'informed-decision.coulomb.social/admission':'review-only; INFD-WP-0001-T08'}},**values}
|
|
|
|
|
container_security={'allowPrivilegeEscalation':False,'readOnlyRootFilesystem':True,'capabilities':{'drop':['ALL']}}
|
|
|
|
|
mount=lambda name,path:{'name':name,'mountPath':path}
|
|
|
|
|
readonly=lambda name,path:{**mount(name,path),'readOnly':True}
|
|
|
|
|
probe=lambda path:{'httpGet':{'path':path,'port':'http'},'periodSeconds':10,'timeoutSeconds':2}
|
|
|
|
|
pod={'automountServiceAccountToken':False,'serviceAccountName':'review','terminationGracePeriodSeconds':45,
|
|
|
|
|
'securityContext':{'runAsNonRoot':True,'runAsUser':10001,'runAsGroup':10001,'fsGroup':10001,
|
|
|
|
|
'fsGroupChangePolicy':'OnRootMismatch','seccompProfile':{'type':'RuntimeDefault'}},
|
|
|
|
|
'containers':[{'name':NAME,'image':inputs['image'],'securityContext':container_security,
|
|
|
|
|
'env':[{'name':'INFD_KEYCAPE_ISSUER','value':'https://kc.coulomb.social'},
|
|
|
|
|
{'name':'INFD_CONTAINER_CONFIG','value':'/configuration/runtime.json'}],
|
|
|
|
|
'ports':[{'name':'http','containerPort':8080}],
|
|
|
|
|
'startupProbe':{**probe('/healthz'),'failureThreshold':12},
|
|
|
|
|
# Keep read/recovery pages reachable during an audit outage. The
|
|
|
|
|
# application gates acceptance on /readyz internally; monitoring
|
|
|
|
|
# must scrape it separately. An outage must not restart the writer.
|
|
|
|
|
'readinessProbe':probe('/healthz'),'livenessProbe':probe('/healthz'),
|
2026-09-14 03:46:27 +02:00
|
|
|
'resources':{'requests':{'cpu':'5m','memory':'64Mi'},'limits':{'cpu':'500m','memory':'256Mi'}},
|
2026-09-11 01:21:37 +02:00
|
|
|
'volumeMounts':[mount('data','/data'),mount('runtime','/run/informed-decision'),mount('tmp','/tmp'),
|
|
|
|
|
readonly('configuration','/configuration'),readonly('caller','/var/run/secrets/informed-decision/policy'),
|
|
|
|
|
readonly('audit','/var/run/secrets/informed-decision/audit')]}],
|
|
|
|
|
'volumes':[{'name':'data','persistentVolumeClaim':{'claimName':NAME+'-data'}},
|
|
|
|
|
{'name':'runtime','emptyDir':{'medium':'Memory','sizeLimit':'16Mi'}},
|
|
|
|
|
{'name':'tmp','emptyDir':{'medium':'Memory','sizeLimit':'16Mi'}},
|
|
|
|
|
{'name':'configuration','configMap':{'name':config_name,'defaultMode':0o444}},
|
|
|
|
|
{'name':'caller','projected':{'defaultMode':0o440,'sources':[{'serviceAccountToken':{
|
|
|
|
|
'path':'token','audience':'flex-auth','expirationSeconds':3600}}]}},
|
|
|
|
|
{'name':'audit','secret':{'secretName':NAME+'-audit','defaultMode':0o440,
|
|
|
|
|
'items':[{'key':'token','path':'token'}]}}]}
|
|
|
|
|
peer=lambda namespace,name:{'namespaceSelector':{'matchLabels':{'kubernetes.io/metadata.name':namespace}},
|
|
|
|
|
'podSelector':{'matchLabels':{'app.kubernetes.io/name':name}}}
|
|
|
|
|
http=[{'protocol':'TCP','port':8080}]
|
|
|
|
|
objects=[
|
|
|
|
|
obj('v1','ServiceAccount','review',automountServiceAccountToken=False),
|
|
|
|
|
obj('v1','PersistentVolumeClaim',NAME+'-data',spec={'accessModes':['ReadWriteOnce'],
|
|
|
|
|
'storageClassName':inputs['storage_class'],'resources':{'requests':{'storage':'1Gi'}}}),
|
|
|
|
|
obj('v1','ConfigMap',config_name,immutable=True,data={'runtime.json':raw}),
|
|
|
|
|
obj('apps/v1','Deployment',NAME,spec={'replicas':1,'strategy':{'type':'Recreate'},'progressDeadlineSeconds':600,
|
|
|
|
|
'selector':{'matchLabels':LABEL},'template':{'metadata':{'labels':{**LABEL,'app.kubernetes.io/component':'review'}},'spec':pod}}),
|
|
|
|
|
obj('v1','Service',NAME,spec={'type':'ClusterIP','selector':{**LABEL,'app.kubernetes.io/component':'review'},
|
|
|
|
|
'ports':[{'name':'http','port':80,'targetPort':'http','protocol':'TCP'}]}),
|
|
|
|
|
obj('networking.k8s.io/v1','NetworkPolicy',NAME+'-review',spec={'podSelector':{'matchLabels':{**LABEL,
|
|
|
|
|
'app.kubernetes.io/component':'review'}},'policyTypes':['Ingress','Egress'],
|
|
|
|
|
'ingress':[{'from':[peer('kube-system','traefik')],'ports':http}],
|
|
|
|
|
'egress':[{'to':[{'namespaceSelector':{'matchLabels':{'kubernetes.io/metadata.name':'kube-system'}},
|
|
|
|
|
'podSelector':{'matchLabels':{'k8s-app':'kube-dns'}}}],
|
|
|
|
|
'ports':[{'protocol':'UDP','port':53},{'protocol':'TCP','port':53}]},
|
|
|
|
|
{'to':[peer('approval-engine','approval-engine'),peer('audit-core','audit-core'),
|
2026-10-04 20:17:24 +02:00
|
|
|
peer('flex-auth',policy['pod_name'])]+([peer('flex-auth',lifecycle['pod_name'])] if lifecycle else []),'ports':http},
|
2026-09-11 01:21:37 +02:00
|
|
|
# Traefik's observed websecure target is 8443. Include
|
|
|
|
|
# its exact peer for CNI enforcement after Service DNAT.
|
|
|
|
|
{'to':[peer('kube-system','traefik')],'ports':[{'protocol':'TCP','port':8443}]},
|
|
|
|
|
{'to':[{'ipBlock':{'cidr':cidr}} for cidr in cidrs],'ports':[{'protocol':'TCP','port':443}]}]})]
|
2026-10-04 21:40:19 +02:00
|
|
|
if 'intake_delivery' in inputs:
|
|
|
|
|
objects[5]['spec']['egress'].append({'to':[peer('state-hub','state-hub')],
|
|
|
|
|
'ports':[{'protocol':'TCP','port':8000}]})
|
|
|
|
|
if 'workload_api' in inputs:
|
|
|
|
|
add_workload_objects(objects, pod, inputs['workload_api'], obj, readonly, peer)
|
2026-09-11 01:21:37 +02:00
|
|
|
# Counterparty proposals travel in the review packet. Neither the existing
|
|
|
|
|
# Approval Engine namespace gate nor other Flex callers admits this pod.
|
|
|
|
|
# Audit Core already owns its prepared exact informed-decision ingress.
|
2026-10-04 20:17:24 +02:00
|
|
|
for namespace,name,task in ([('approval-engine','approval-engine','APPROVAL-WP-0002-T01'),
|
|
|
|
|
('flex-auth',policy['pod_name'],'INFD-WP-0001-T08 policy-owner return')]
|
|
|
|
|
+ ([('flex-auth',lifecycle['pod_name'],'INFD-WP-0007 lifecycle policy-owner return')] if lifecycle else [])):
|
|
|
|
|
item=obj('networking.k8s.io/v1','NetworkPolicy',NAME+('-lifecycle-ingress' if task.startswith('INFD-WP-0007') else '-review-ingress'),spec={
|
2026-09-11 01:21:37 +02:00
|
|
|
'podSelector':{'matchLabels':{'app.kubernetes.io/name':name}},'policyTypes':['Ingress'],
|
|
|
|
|
'ingress':[{'from':[{'namespaceSelector':{'matchLabels':{'kubernetes.io/metadata.name':NAME}},
|
|
|
|
|
'podSelector':{'matchLabels':{**LABEL,'app.kubernetes.io/component':'review'}}}],'ports':http}]})
|
|
|
|
|
item['metadata']['namespace']=namespace
|
|
|
|
|
item['metadata']['annotations']['informed-decision.coulomb.social/owner-review']=task
|
|
|
|
|
objects.append(item)
|
|
|
|
|
return {'apiVersion':'v1','kind':'List','items':objects}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
|
|
|
parser.add_argument('--input',type=Path,required=True)
|
|
|
|
|
parser.add_argument('--output',type=Path)
|
|
|
|
|
args=parser.parse_args()
|
|
|
|
|
try: result=render(json.loads(args.input.read_text()))
|
|
|
|
|
except (ValueError,TypeError,KeyError,OSError) as exc:parser.error(str(exc))
|
|
|
|
|
raw=json.dumps(result,indent=2)+'\n'
|
|
|
|
|
if args.output:args.output.write_text(raw)
|
|
|
|
|
else:print(raw,end='')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__=='__main__':main()
|