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
103
tests/test_container_runtime.py
Normal file
103
tests/test_container_runtime.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import stat
|
||||
|
||||
import pytest
|
||||
|
||||
from informed_decision.container import prepare_configuration, private_directory, single_writer
|
||||
from informed_decision.runtime import Runtime
|
||||
from informed_decision.store import Store
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def projected(tmp_path):
|
||||
data=tmp_path/'data';data.mkdir()
|
||||
run=tmp_path/'run';run.mkdir()
|
||||
config={'schema':'informed-decision.review-runtime.v1','evidence_db':str(data/'private/review.sqlite'),
|
||||
'approval_origin':'http://approval-engine.approval-engine.svc.cluster.local:8080',
|
||||
'policy':{'origin':'http://flex-auth-review.flex-auth.svc.cluster.local:8080',
|
||||
'package':'fixture','version':'v1','package_digest':'sha256:'+'a'*64,
|
||||
'caller_token_file':str(tmp_path/'not-provisioned-caller')},
|
||||
'audit':{'origin':'http://audit-core.audit-core.svc.cluster.local:8080',
|
||||
'sender_token_file':str(tmp_path/'not-provisioned-sender')}}
|
||||
payload=tmp_path/'projected';payload.write_text(json.dumps(config));payload.chmod(0o444)
|
||||
link=tmp_path/'runtime.json';link.symlink_to(payload)
|
||||
return link,data,run,config
|
||||
|
||||
|
||||
def test_projected_config_becomes_private_without_reading_or_copying_credentials(projected):
|
||||
source,data,run,expected=projected
|
||||
result,evidence=prepare_configuration(source,data_root=data,run_root=run)
|
||||
assert not result.is_symlink() and stat.S_IMODE(result.stat().st_mode)==0o600
|
||||
assert result.stat().st_uid==os.getuid() and json.loads(result.read_text())==expected
|
||||
assert stat.S_IMODE(evidence.stat().st_mode)==0o700
|
||||
runtime=Runtime.from_file(result)
|
||||
assert not runtime.pump.ready()
|
||||
assert not Path(expected['policy']['caller_token_file']).exists()
|
||||
assert not Path(expected['audit']['sender_token_file']).exists()
|
||||
assert sorted(p.name for p in result.parent.iterdir())==['runtime.json']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fault',['oversize','outside_db','unsafe_directory','symlink_directory'])
|
||||
def test_unsafe_container_storage_and_config_refused(projected,tmp_path,fault):
|
||||
source,data,run,config=projected
|
||||
source.resolve().chmod(0o644)
|
||||
if fault=='oversize':source.write_text('x'*16385)
|
||||
if fault=='outside_db':
|
||||
config['evidence_db']=str(tmp_path/'outside.sqlite');source.write_text(json.dumps(config))
|
||||
if fault=='unsafe_directory':(data/'private').mkdir(mode=0o755)
|
||||
if fault=='symlink_directory':(data/'private').symlink_to(run)
|
||||
with pytest.raises((ValueError,FileExistsError)):
|
||||
prepare_configuration(source,data_root=data,run_root=run)
|
||||
assert not (data/'private/review.sqlite').exists()
|
||||
|
||||
|
||||
def test_second_service_cannot_open_the_same_evidence_volume(tmp_path):
|
||||
private=private_directory(tmp_path/'private')
|
||||
with single_writer(private):
|
||||
with pytest.raises(ValueError,match='another review service'):
|
||||
with single_writer(private):pass
|
||||
with single_writer(private):pass # Process/holder release permits restart.
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fault',['symlink','hardlink','mode'])
|
||||
def test_unsafe_service_lock_refused(tmp_path,fault):
|
||||
private=private_directory(tmp_path/'private');lock=private/'serve.lock'
|
||||
if fault=='symlink':lock.symlink_to(tmp_path/'outside')
|
||||
else:
|
||||
lock.touch(mode=0o600)
|
||||
if fault=='hardlink':os.link(lock,private/'other')
|
||||
else:lock.chmod(0o644)
|
||||
with pytest.raises((ValueError,OSError)):
|
||||
with single_writer(private):pass
|
||||
|
||||
|
||||
def test_reconfiguration_preserves_persisted_evidence(projected):
|
||||
source,data,run,config=projected
|
||||
result,_=prepare_configuration(source,data_root=data,run_root=run)
|
||||
first=Runtime.from_file(result);digest=first.controller.store.put_document(b'private fixture content')
|
||||
source.resolve().chmod(0o644)
|
||||
config['policy']['version']='v2';source.write_text(json.dumps(config))
|
||||
result,_=prepare_configuration(source,data_root=data,run_root=run)
|
||||
second=Runtime.from_file(result)
|
||||
assert second.controller.policy.version=='v2'
|
||||
with second.controller.store._connection() as db:
|
||||
assert db.execute('SELECT content FROM documents WHERE digest=?',(digest,)).fetchone()[0]==b'private fixture content'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('host',[None,'0.0.0.0','localhost'])
|
||||
def test_listener_exposure_requires_explicit_supported_setting(monkeypatch,host):
|
||||
import waitress
|
||||
from informed_decision import web
|
||||
monkeypatch.delenv('INFD_REVIEW_CONFIG',raising=False)
|
||||
monkeypatch.setenv('INFD_KEYCAPE_ISSUER','https://keycape.test')
|
||||
monkeypatch.delenv('INFD_LISTEN_HOST',raising=False)
|
||||
if host:monkeypatch.setenv('INFD_LISTEN_HOST',host)
|
||||
observed=[]
|
||||
monkeypatch.setattr(waitress,'serve',lambda app,**kwargs:observed.append(kwargs))
|
||||
if host=='localhost':
|
||||
with pytest.raises(ValueError):web.main()
|
||||
assert not observed
|
||||
else:
|
||||
web.main();assert observed[0]['host']==(host or '127.0.0.1')
|
||||
77
tests/test_deployment_candidate.py
Normal file
77
tests/test_deployment_candidate.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import copy
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
spec=importlib.util.spec_from_file_location('render_deployment',Path(__file__).parents[1]/'tools/render_deployment.py')
|
||||
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
||||
|
||||
|
||||
def inputs():
|
||||
return {'image':'forgejo.coulomb.social/coulomb/informed-decision@sha256:'+'a'*64,
|
||||
'policy':{'origin':'http://flex-auth-review.flex-auth.svc.cluster.local:8080','package':'review.example',
|
||||
'version':'v1','package_digest':'sha256:'+'b'*64,'pod_name':'flex-auth-review'},
|
||||
'keycape_egress_ips':['92.205.62.239'],'storage_class':'local-path'}
|
||||
|
||||
|
||||
def test_candidate_does_not_create_custody_and_uses_one_private_writer():
|
||||
result=module.render(inputs());objects=result['items']
|
||||
assert not any(o['kind'] in {'Secret','Role','RoleBinding','ClusterRoleBinding','Ingress','Namespace'} for o in objects)
|
||||
deploy=next(o for o in objects if o['kind']=='Deployment')['spec']
|
||||
assert deploy['replicas']==1 and deploy['strategy']=={'type':'Recreate'}
|
||||
pod=deploy['template']['spec'];container=pod['containers'][0]
|
||||
assert pod['automountServiceAccountToken'] is False and pod['serviceAccountName']=='review'
|
||||
assert pod['securityContext']['runAsUser']==pod['securityContext']['fsGroup']==10001
|
||||
assert container['securityContext']['readOnlyRootFilesystem'] is True
|
||||
assert container['securityContext']['capabilities']['drop']==['ALL']
|
||||
caller=next(v for v in pod['volumes'] if v['name']=='caller')
|
||||
assert caller['projected']['sources']==[{'serviceAccountToken':{'path':'token','audience':'flex-auth','expirationSeconds':3600}}]
|
||||
# Healthy serving remains available for refusal/recovery; application itself
|
||||
# gates accept on audit /readyz, without a dependency-driven restart cycle.
|
||||
assert container['readinessProbe']['httpGet']['path']=='/healthz'
|
||||
assert container['livenessProbe']['httpGet']['path']=='/healthz'
|
||||
|
||||
|
||||
def test_policies_and_service_selectors_do_not_include_placeholder_or_whole_namespaces():
|
||||
objects=module.render(inputs())['items']
|
||||
service=next(o for o in objects if o['kind']=='Service')
|
||||
assert service['spec']['selector']['app.kubernetes.io/component']=='review'
|
||||
policy=next(o for o in objects if o['kind']=='NetworkPolicy')['spec']
|
||||
for rule in policy['ingress']+policy['egress']:
|
||||
for peer in rule.get('from',rule.get('to',[])):
|
||||
if 'namespaceSelector' in peer:assert 'podSelector' in peer
|
||||
assert policy['egress'][-1]['to']==[{'ipBlock':{'cidr':'92.205.62.239/32'}}]
|
||||
assert policy['egress'][-1]['ports']==[{'protocol':'TCP','port':443}]
|
||||
assert policy['egress'][-2]['to'][0]['podSelector']['matchLabels']=={'app.kubernetes.io/name':'traefik'}
|
||||
assert policy['egress'][-2]['ports']==[{'protocol':'TCP','port':8443}]
|
||||
peers=[o for o in objects if o['kind']=='NetworkPolicy' and o['metadata']['namespace']!='informed-decision']
|
||||
assert {o['metadata']['namespace'] for o in peers}=={'approval-engine','flex-auth'}
|
||||
for obj in peers:
|
||||
caller=obj['spec']['ingress'][0]['from'][0]
|
||||
assert caller['namespaceSelector']['matchLabels']=={'kubernetes.io/metadata.name':'informed-decision'}
|
||||
assert caller['podSelector']['matchLabels']['app.kubernetes.io/component']=='review'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fault',['tag','other_image','missing_pin','external_pdp','wildcard_pod','unknown_field',
|
||||
'broad_egress','loopback','empty_ips','empty_storage','empty_package'])
|
||||
def test_incomplete_or_broad_deployment_inputs_refuse(fault):
|
||||
data=inputs()
|
||||
if fault=='tag':data['image']='forgejo.coulomb.social/coulomb/informed-decision:latest'
|
||||
if fault=='other_image':data['image']=data['image'].replace('informed-decision','other')
|
||||
if fault=='missing_pin':del data['policy']['package_digest']
|
||||
if fault=='external_pdp':data['policy']['origin']='https://elsewhere.test'
|
||||
if fault=='wildcard_pod':data['policy']['pod_name']='*'
|
||||
if fault=='unknown_field':data['token']='not-permitted-inline'
|
||||
if fault=='broad_egress':data['keycape_egress_ips']=['0.0.0.0/0']
|
||||
if fault=='loopback':data['keycape_egress_ips']=['127.0.0.1']
|
||||
if fault=='empty_ips':data['keycape_egress_ips']=[]
|
||||
if fault=='empty_storage':data['storage_class']=''
|
||||
if fault=='empty_package':data['policy']['package']=''
|
||||
with pytest.raises(ValueError):module.render(data)
|
||||
|
||||
|
||||
def test_policy_revision_changes_immutable_configuration_name():
|
||||
first=inputs();second=copy.deepcopy(first);second['policy']['version']='v2'
|
||||
names=lambda data:[o['metadata']['name'] for o in module.render(data)['items'] if o['kind']=='ConfigMap']
|
||||
assert names(first)!=names(second)
|
||||
Loading…
Add table
Add a link
Reference in a new issue