Package protected review runtime and prepare deployment admission
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
parent
bb5b607bbd
commit
bda9381f07
20 changed files with 3534 additions and 7 deletions
131
tools/render_deployment.py
Normal file
131
tools/render_deployment.py
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
"""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}
|
||||
|
||||
|
||||
def render(inputs):
|
||||
if not isinstance(inputs,dict) or set(inputs)!={'image','policy','keycape_egress_ips','storage_class'}:
|
||||
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']
|
||||
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')
|
||||
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'}}
|
||||
config['policy']['caller_token_file']='/var/run/secrets/informed-decision/policy/token'
|
||||
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'),
|
||||
'resources':{'requests':{'cpu':'50m','memory':'64Mi'},'limits':{'cpu':'500m','memory':'256Mi'}},
|
||||
'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'),
|
||||
peer('flex-auth',policy['pod_name'])],'ports':http},
|
||||
# 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}]}]})]
|
||||
# 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.
|
||||
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')]:
|
||||
item=obj('networking.k8s.io/v1','NetworkPolicy',NAME+'-review-ingress',spec={
|
||||
'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()
|
||||
150
tools/smoke_container.py
Normal file
150
tools/smoke_container.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
"""Exercise a local image, private volumes and restore without external network.
|
||||
|
||||
Only synthetic records/tokens are created. No native issuer, policy, audit or
|
||||
Approval Engine is contacted. Every container and volume is created by this
|
||||
process, labeled, and removed in finally; the image is retained for inspection.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import uuid
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--image',required=True)
|
||||
parser.add_argument('--receipt',type=Path,required=True)
|
||||
args=parser.parse_args()
|
||||
prefix='infd-proof-'+uuid.uuid4().hex[:12]
|
||||
volumes=[];containers=[];checks=[]
|
||||
def docker(*words,check=True):
|
||||
return subprocess.run(['docker',*words],capture_output=True,text=True,check=check,timeout=60)
|
||||
def command(name,code):
|
||||
return docker('exec',name,'python','-c',code).stdout.strip()
|
||||
def admin(name,*args):
|
||||
return json.loads(docker('exec',name,'informed-decision-admin',*args).stdout)
|
||||
def stop(name):
|
||||
docker('stop','--time','15',name)
|
||||
state=json.loads(docker('inspect',name).stdout)[0]['State']
|
||||
assert state['ExitCode']==0, 'service did not stop gracefully: '+str(state['ExitCode'])
|
||||
def wait_health(name):
|
||||
for _ in range(40):
|
||||
result=docker('exec',name,'python','-c',
|
||||
"import urllib.request; assert urllib.request.urlopen('http://127.0.0.1:8080/healthz',timeout=1).status==200",check=False)
|
||||
if result.returncode==0:return
|
||||
state=json.loads(docker('inspect',name).stdout)[0]['State']
|
||||
if not state['Running']:raise AssertionError('container exited before health; '+docker('logs',name).stdout)
|
||||
time.sleep(.2)
|
||||
raise AssertionError('container did not become healthy')
|
||||
def volume(suffix):
|
||||
name=prefix+'-'+suffix
|
||||
docker('volume','create','--label','informed-decision.fixture='+prefix,name);volumes.append(name)
|
||||
docker('run','--rm','--network','none','--read-only','--user','0:0','--cap-drop','ALL','--cap-add','CHOWN',
|
||||
'--mount','type=volume,src='+name+',dst=/data','--entrypoint','python',args.image,'-c',
|
||||
"import os; os.chown('/data',0,10001); os.chmod('/data',0o2770)")
|
||||
return name
|
||||
def start(suffix,data,fixture,backup=None):
|
||||
name=prefix+'-'+suffix;containers.append(name)
|
||||
words=['run','-d','--name',name,'--label','informed-decision.fixture='+prefix,
|
||||
'--network','none','--read-only','--cap-drop','ALL','--security-opt','no-new-privileges',
|
||||
'--tmpfs','/run/informed-decision:rw,nosuid,nodev,noexec,uid=10001,gid=10001,mode=0700',
|
||||
'--tmpfs','/tmp:rw,nosuid,nodev,noexec,mode=1777',
|
||||
'--mount','type=volume,src='+data+',dst=/data',
|
||||
'--mount','type=bind,src='+str(fixture)+',dst=/configuration,readonly',
|
||||
'--env','INFD_KEYCAPE_ISSUER=https://kc.coulomb.social']
|
||||
if backup:words+=['--mount','type=volume,src='+backup+',dst=/backup']
|
||||
docker(*words,args.image);return name
|
||||
snapshot="""import hashlib,json
|
||||
from informed_decision.store import Store
|
||||
s=Store('/data/private/review.sqlite')
|
||||
with s._connection() as db:
|
||||
rows={t:[list(r) for r in db.execute('SELECT * FROM '+t+' ORDER BY rowid')] for t in ['memos','presentations','dispositions','submissions','evidence','documents']}
|
||||
rows['documents']=[[r[0],r[1],hashlib.sha256(r[2]).hexdigest()] for r in rows['documents']]
|
||||
print(hashlib.sha256(json.dumps(rows,sort_keys=True).encode()).hexdigest())
|
||||
"""
|
||||
seed="""from informed_decision.store import Store
|
||||
from informed_decision.memo import Memo,BindingSlice,Principal,Scope,BindingLevel,StepKind,PacketItem
|
||||
from informed_decision.provenance import Claim,Route
|
||||
from informed_decision.disposition import Actor,ActorKind,Verb
|
||||
s=Store('/data/private/review.sqlite');digest=s.put_document(b'Synthetic retained content; no real approval.')
|
||||
m=Memo(id='container-fixture',version=1,question='Synthetic custody exercise?',requested_act='deliver',binding_level=BindingLevel.ORGANIZATIONAL,brief='Fixture only',binding=BindingSlice(Principal('fixture-human','person','Fixture'),Scope('resource','fixture','Fixture')),step_kind=StepKind.APPROVE,packet=(PacketItem('doc','Fixture',digest),),approval_id='fixture',approval_binding_digest='sha256:'+'1'*64)
|
||||
s.save_memo(m)
|
||||
p=s.present(m.id,principal_sub='fixture-human',tenant=Claim('tenant:platform',Route.REGISTRATION),principal_type=Claim('human',Route.AUTHENTICATION))
|
||||
d=s.record_disposition(p.id,Actor('fixture-human',ActorKind.PERSON),Verb.ACCEPT,operation_id='container-fixture-op')
|
||||
attempt=s.begin_submission(d.id);s.finish_submission(d.id,attempt)
|
||||
assert s.submission(d.id)['state']=='unresolved'
|
||||
"""
|
||||
try:
|
||||
with tempfile.TemporaryDirectory(prefix=prefix+'-') as temp:
|
||||
fixture=Path(temp);fixture.chmod(0o755)
|
||||
(fixture/'audit-token').write_text('synthetic-audit-token');(fixture/'caller-token').write_text('synthetic-caller-token')
|
||||
config={'schema':'informed-decision.review-runtime.v1','evidence_db':'/data/private/review.sqlite',
|
||||
'approval_origin':'http://127.0.0.1:18082','policy':{'origin':'http://127.0.0.1:18083',
|
||||
'package':'container.fixture','version':'v1','package_digest':'sha256:'+'a'*64,
|
||||
'caller_token_file':'/configuration/caller-token'},
|
||||
'audit':{'origin':'http://127.0.0.1:18084','sender_token_file':'/configuration/audit-token'}}
|
||||
(fixture/'runtime.json').write_text(json.dumps(config))
|
||||
for file in fixture.iterdir():file.chmod(0o444)
|
||||
data=volume('data');backup=volume('backup')
|
||||
first=start('first',data,fixture,backup);wait_health(first)
|
||||
checks.append('installed container entrypoint serves with projected configuration')
|
||||
info=json.loads(docker('inspect',first).stdout)[0]
|
||||
assert info['Config']['User']=='10001:10001' and info['HostConfig']['ReadonlyRootfs']
|
||||
assert info['HostConfig']['NetworkMode']=='none' and not info['NetworkSettings']['Ports'].get('8080/tcp')
|
||||
checks.append('non-root read-only runtime has no external network or published port')
|
||||
command(first,"import os,stat,importlib.util; assert os.getuid()==10001; assert importlib.util.find_spec('pip') is None; assert stat.S_IMODE(os.stat('/data/private').st_mode)==0o700; assert stat.S_IMODE(os.stat('/data/private/review.sqlite').st_mode)==0o600; assert stat.S_IMODE(os.stat('/run/informed-decision/private/runtime.json').st_mode)==0o600")
|
||||
checks.append('private config/database modes enforced; package installer absent')
|
||||
command(first,"import urllib.request,urllib.error\nfor path,status in [('/readyz',503),('/review?memo_id=missing',401)]:\n try: urllib.request.urlopen('http://127.0.0.1:8080'+path)\n except urllib.error.HTTPError as e: assert e.code==status\n else: raise AssertionError(path)")
|
||||
checks.append('configured review refuses anonymous access and reports audit-unready status')
|
||||
command(first,"from informed_decision.container import single_writer\ntry:\n with single_writer('/data/private'): pass\nexcept ValueError: pass\nelse: raise AssertionError('second writer admitted')")
|
||||
checks.append('a second writer cannot acquire the active evidence volume')
|
||||
command(first,seed)
|
||||
state=admin(first,'inspect','--db','/data/private/review.sqlite')
|
||||
assert state['submissions']=={'unresolved':1} and state['outbox'].get('pending',0)>0
|
||||
original=command(first,snapshot)
|
||||
checks.append('synthetic uncertain intent and undelivered evidence remain explicit')
|
||||
command(first,"from informed_decision.container import private_directory; private_directory('/backup/private')")
|
||||
result=admin(first,'backup','--db','/data/private/review.sqlite','--output','/backup/private/review.sqlite')
|
||||
assert result['consistent_snapshot'] is True
|
||||
repeat=docker('exec',first,'informed-decision-admin','backup','--db','/data/private/review.sqlite','--output','/backup/private/review.sqlite',check=False)
|
||||
assert repeat.returncode!=0
|
||||
checks.append('consistent backup created while serving; overwrite refused')
|
||||
stop(first)
|
||||
checks.append('SIGTERM closes the serving writer without forced kill')
|
||||
docker('start',first);wait_health(first)
|
||||
assert command(first,snapshot)==original
|
||||
assert admin(first,'inspect','--db','/data/private/review.sqlite')['submissions']=={'unresolved':1}
|
||||
checks.append('restart preserves immutable content and unresolved submission without retry')
|
||||
stop(first)
|
||||
restored=start('restored',backup,fixture);wait_health(restored)
|
||||
assert command(restored,snapshot)==original
|
||||
assert admin(restored,'inspect','--db','/data/private/review.sqlite')['submissions']=={'unresolved':1}
|
||||
checks.append('separate restored volume preserves exact content and unresolved state')
|
||||
stop(restored)
|
||||
(fixture/'runtime.json').chmod(0o644);(fixture/'runtime.json').write_text('{}');(fixture/'runtime.json').chmod(0o444)
|
||||
invalid=start('invalid',data,fixture)
|
||||
code=int(docker('wait',invalid).stdout)
|
||||
assert code!=0
|
||||
checks.append('incomplete owner configuration exits before serving')
|
||||
image=json.loads(docker('image','inspect',args.image).stdout)[0]
|
||||
receipt={'status':'passed','image_id':image['Id'],'repo_digests':image['RepoDigests'],
|
||||
'checks_passed':len(checks),'checks':checks,'fixture_only':True,'external_network':'none',
|
||||
'content_snapshot_sha256':original,'native_identity_policy_audit_proven':False,
|
||||
'published':False,'deployed':False,'factory_attempts':0,'paid_model_calls':0}
|
||||
finally:
|
||||
for name in reversed(containers):docker('rm','-f',name,check=False)
|
||||
for name in reversed(volumes):docker('volume','rm',name,check=False)
|
||||
receipt['cleanup_complete']=all(docker('inspect',name,check=False).returncode!=0 for name in containers)
|
||||
receipt['cleanup_complete'] &= all(docker('volume','inspect',name,check=False).returncode!=0 for name in volumes)
|
||||
assert receipt['cleanup_complete']
|
||||
args.receipt.write_text(json.dumps(receipt,indent=2)+'\n')
|
||||
print(json.dumps({'status':'passed','checks_passed':len(checks),'cleanup_complete':True,'image_id':receipt['image_id']}))
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue