Reconcile T03 verification and complete guarded native execution
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 6s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
codex 2026-09-16 02:12:45 +02:00
parent 5b3041cfb5
commit 9e658be8e3
5 changed files with 118 additions and 15 deletions

View file

@ -0,0 +1,9 @@
{
"observed_at": "2026-09-16T00:04:53.406447+00:00",
"status": "failed",
"phase": "scoped_reader_delivered",
"credential_values_emitted": false,
"reader_effective_policy_verified": true,
"existing_kv_version": 1,
"failure_code": "attended_admin_failed"
}

View file

@ -0,0 +1,10 @@
{
"observed_at": "2026-09-16T00:08:16.013097+00:00",
"status": "passed",
"phase": "native_actions_completed",
"credential_values_emitted": false,
"reader_effective_policy_verified": true,
"existing_kv_version": 1,
"admin_warden_exited_0": true,
"private_runtime_removed": true
}

View file

@ -1,5 +1,5 @@
"""Scoped reader -> contained attended administration; never exports credentials.""" """Scoped reader -> contained attended administration; never exports credentials."""
import importlib.util,json,os,signal,stat,subprocess,tempfile import importlib.util,json,os,signal,stat,subprocess,tempfile,sys
from pathlib import Path from pathlib import Path
from datetime import datetime,timezone from datetime import datetime,timezone
from urllib.request import Request,build_opener,ProxyHandler,HTTPRedirectHandler from urllib.request import Request,build_opener,ProxyHandler,HTTPRedirectHandler
@ -13,9 +13,13 @@ def require(value,code):
if not value:raise ValueError(code) if not value:raise ValueError(code)
def private(path,directory=False): def private(path,directory=False):
st=path.lstat();require(st.st_uid==os.getuid() and stat.S_IMODE(st.st_mode)==(0o700 if directory else 0o600) and (stat.S_ISDIR(st.st_mode) if directory else stat.S_ISREG(st.st_mode)),'private_path_required') st=path.lstat();require(st.st_uid==os.getuid() and stat.S_IMODE(st.st_mode)==(0o700 if directory else 0o600) and (stat.S_ISDIR(st.st_mode) if directory else stat.S_ISREG(st.st_mode)),'private_path_required')
RESUME=sys.argv[1:]==['--resume-exec']
if sys.argv[1:] and not RESUME:raise SystemExit('unsupported mode')
if RESUME:RECEIPT=ROOT/'docs/evidence/2026-09-16-t03-resume-attended-delivery.json'
def main(receipt): def main(receipt):
require(Path.home().parent.name=='.warden-attended-login' and not os.getenv('BAO_TOKEN') and not os.getenv('VAULT_TOKEN'),'attended_reader_required') require(Path.home().parent.name=='.warden-attended-login' and not os.getenv('BAO_TOKEN') and not os.getenv('VAULT_TOKEN'),'attended_reader_required')
require(not Path('/home/worsch/secrets-engine/docs/evidence/2026-09-15-t03-native-execution.json').exists(),'execution_receipt_requires_reconciliation') require(not Path('/home/worsch/secrets-engine/docs/evidence/'+('2026-09-16-t03-resume-exec.json' if RESUME else '2026-09-15-t03-native-execution.json')).exists(),'execution_receipt_requires_reconciliation')
identity=pre.bao('token','lookup','-format=json')['data'];pre.validate_identity(identity) identity=pre.bao('token','lookup','-format=json')['data'];pre.validate_identity(identity)
for path,expected in pre.EXPECTED.items():require(sorted(pre.bao('token','capabilities','-format=json',path))==expected,'reader_scope_failed') for path,expected in pre.EXPECTED.items():require(sorted(pre.bao('token','capabilities','-format=json',path))==expected,'reader_scope_failed')
runtime=Path('/run/user')/str(os.getuid());private(runtime,True) runtime=Path('/run/user')/str(os.getuid());private(runtime,True)
@ -34,7 +38,7 @@ def main(receipt):
del value,data,req del value,data,req
receipt.update(phase='scoped_reader_delivered',reader_effective_policy_verified=True,existing_kv_version=1) receipt.update(phase='scoped_reader_delivered',reader_effective_policy_verified=True,existing_kv_version=1)
RECEIPT.write_text(json.dumps(receipt,indent=2)+'\n') RECEIPT.write_text(json.dumps(receipt,indent=2)+'\n')
p=subprocess.run(['python3',str(ROOT/'scripts/openbao-attended-exec.py'),'--','/home/worsch/secrets-engine/.venv/bin/python','-B',str(ROOT/'scripts/t03-native-execution.py'),'admin','--directory',str(directory),'--negative-token-file',str(helper)],capture_output=True,timeout=780) p=subprocess.run(['python3',str(ROOT/'scripts/openbao-attended-exec.py'),'--','/home/worsch/secrets-engine/.venv/bin/python','-B',str(ROOT/'scripts/t03-native-execution.py'),'resume-exec' if RESUME else 'admin','--directory',str(directory),'--negative-token-file',str(helper)],capture_output=True,timeout=780)
# Warden's output remains inside this envelope, never forwarded or stored. # Warden's output remains inside this envelope, never forwarded or stored.
require(p.returncode==0,'attended_admin_failed') require(p.returncode==0,'attended_admin_failed')
receipt.update(status='passed',phase='native_actions_completed',admin_warden_exited_0=True) receipt.update(status='passed',phase='native_actions_completed',admin_warden_exited_0=True)

View file

@ -63,7 +63,36 @@ def health():
require(rows,'llm_connect_health_target_missing');result[kind]=rows require(rows,'llm_connect_health_target_missing');result[kind]=rows
return result return result
def admin(directory,negative): def verify_session_cleanup(client):
# Keep the explicit scoped token in memory: close() clears session.client.token,
# which otherwise makes a subsequent CLI call use the attended admin helper.
from urllib.request import Request,build_opener,ProxyHandler,HTTPRedirectHandler
from urllib.error import HTTPError
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self,*a,**k):return None
with client.approle_session(ROLE) as session:
probe_token=session.client.token
probes=['platform/data/workloads/secrets-engine/approval-client','platform/metadata/workloads/activity-core']
require(all(session.client.token_capabilities(p,token=probe_token)==['deny'] for p in probes),'unrelated_path_authority')
try:
require(session.revocation_succeeded,'session_revocation_failed')
req=Request(client.addr+'/v1/auth/token/lookup-self',headers={'X-Vault-Token':probe_token})
try:
with build_opener(ProxyHandler({}),NoRedirect()).open(req,timeout=20) as response:
raise ValueError('revoked_token_still_usable')
except HTTPError as error:
require(error.code==403,'revocation_probe_not_definitive')
finally:
del probe_token
return {'unrelated_path_denied':True,'session_revocation_verified':True,'revoked_token_lookup_status':403}
def resume_receipt():
prior=json.loads((ROOT/'docs/evidence/2026-09-15-t03-native-execution.json').read_text())
require(prior.get('phase')=='verify_attempt_started' and prior.get('failure_code')=='revoked_token_still_usable','unexpected_prior_failure')
require(prior.get('actions')==[{'action':'apply','approval_id':IDS['apply'],'exit_code':0,'limits':{'token_ttl':900,'token_max_ttl':1800,'secret_id_ttl':900,'secret_id_num_uses':1,'token_num_uses':8}}],'prior_apply_not_verified')
return prior
def admin(directory,negative,resume=False):
require(not RECEIPT.exists(),'existing_execution_receipt_requires_reconciliation') require(not RECEIPT.exists(),'existing_execution_receipt_requires_reconciliation')
receipt={'status':'failed','phase':'preflight','observed_at':datetime.now(timezone.utc).isoformat(),'actions':[]} receipt={'status':'failed','phase':'preflight','observed_at':datetime.now(timezone.utc).isoformat(),'actions':[]}
forwards=[] forwards=[]
@ -93,14 +122,28 @@ def admin(directory,negative):
fd=os.open(directory/'pdp-caller',os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600) fd=os.open(directory/'pdp-caller',os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
with os.fdopen(fd,'w') as out:out.write(run(*KUBE,'-n','secrets-engine','create','token','secrets-engine','--audience=flex-auth','--duration=10m').strip()) with os.fdopen(fd,'w') as out:out.write(run(*KUBE,'-n','secrets-engine','create','token','secrets-engine','--audience=flex-auth','--duration=10m').strip())
# Validate real claims and exact current decisions before any consumption. # Validate real claims and exact current decisions before any consumption.
for action in IDS: actions=('exec',) if resume else tuple(IDS)
for action in actions:
auth=authorize_action(configs[action],entries[action],action,fields=() if action=='apply' else tuple(entries[action].fields),policy_targets=(ROLE,),auth_targets=(ROLE,)) auth=authorize_action(configs[action],entries[action],action,fields=() if action=='apply' else tuple(entries[action].fields),policy_targets=(ROLE,),auth_targets=(ROLE,))
require(auth is not None,'native_authorization_missing') require(auth is not None,'native_authorization_missing')
receipt['phase']='all_claims_and_pdp_checks_passed';save(receipt) receipt['phase']='all_claims_and_pdp_checks_passed';save(receipt)
client=OpenBaoClient.resolve('http://127.0.0.1:18200') client=OpenBaoClient.resolve('http://127.0.0.1:18200')
existing=client.read_policy(ROLE) existing=client.read_policy(ROLE)
require(existing is None and not client.approle_exists(ROLE),'existing_native_objects_require_reconciliation') if resume:
for action in IDS: prior=resume_receipt()
plan=build_plan(entries['apply'],'prod')
require(existing is not None and existing.strip()==plan.policy_hcl.strip(),'applied_policy_drift')
role=bao('read','-format=json','auth/approle/role/'+ROLE)['data']
require(all(role.get(k)==v for k,v in prior['actions'][0]['limits'].items()) and role.get('token_policies')==[ROLE],'applied_role_drift')
code="import json;from approval_engine.store import Engine;e=Engine('/data/approvals.sqlite');print(json.dumps({k:e.claim(v) for k,v in "+repr(IDS)+".items()}))"
claims=json.loads(run(*KUBE,'-n','approval-engine','exec','statefulset/approval-engine','--','python','-c',code))
require(all(claims[a]['consumed'] and claims[a]['approval_id']==IDS[a] for a in ('apply','verify')) and claims['exec']['valid_now'] and not claims['exec']['consumed'],'resume_approval_state_mismatch')
receipt['prior_receipt']='2026-09-15-t03-native-execution.json'
receipt['completed_actions_not_replayed']=['apply','verify']
receipt['supplemental_cleanup']=verify_session_cleanup(client);save(receipt)
else:
require(existing is None and not client.approle_exists(ROLE),'existing_native_objects_require_reconciliation')
for action in actions:
receipt['phase']=action+'_attempt_started';save(receipt) receipt['phase']=action+'_attempt_started';save(receipt)
if action=='apply':argv=['apply',LANE,'--stage','prod','--auth','env'] if action=='apply':argv=['apply',LANE,'--stage','prod','--auth','env']
elif action=='verify':argv=['verify',LANE,'--auth','env','--negative-token-file',str(negative)] elif action=='verify':argv=['verify',LANE,'--auth','env','--negative-token-file',str(negative)]
@ -116,12 +159,7 @@ def admin(directory,negative):
limits={'token_ttl':900,'token_max_ttl':1800,'secret_id_ttl':900,'secret_id_num_uses':1,'token_num_uses':8} limits={'token_ttl':900,'token_max_ttl':1800,'secret_id_ttl':900,'secret_id_num_uses':1,'token_num_uses':8}
require(all(role.get(k)==v for k,v in limits.items()) and role.get('token_policies')==[ROLE],'role_limits_mismatch');row['limits']=limits require(all(role.get(k)==v for k,v in limits.items()) and role.get('token_policies')==[ROLE],'role_limits_mismatch');row['limits']=limits
elif action=='verify': elif action=='verify':
with client.approle_session(ROLE) as session: row.update(verify_session_cleanup(client))
probes=['platform/data/workloads/secrets-engine/approval-client','platform/metadata/workloads/activity-core']
require(all(session.client.token_capabilities(p,token=session.client.token)==['deny'] for p in probes),'unrelated_path_authority')
require(session.revocation_succeeded,'session_revocation_failed')
require(session.client._run(['token','lookup','-format=json']).returncode!=0,'revoked_token_still_usable')
row.update(unrelated_path_denied=True,session_revocation_verified=True)
else: else:
result=json.loads(output.getvalue());require(result=={'result':'authenticated','http_status':200},'key_check_result_invalid');row['key_check']=result result=json.loads(output.getvalue());require(result=={'result':'authenticated','http_status':200},'key_check_result_invalid');row['key_check']=result
receipt['actions'].append(row);receipt['phase']=action+'_completed';save(receipt) receipt['actions'].append(row);receipt['phase']=action+'_completed';save(receipt)
@ -142,12 +180,16 @@ def admin(directory,negative):
receipt['owner_forwards_closed']=True;save(receipt) receipt['owner_forwards_closed']=True;save(receipt)
if __name__=='__main__': if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('mode',choices=['preflight','admin']);p.add_argument('--directory',type=Path);p.add_argument('--negative-token-file',type=Path);a=p.parse_args() p=argparse.ArgumentParser();p.add_argument('mode',choices=['preflight','admin','resume-exec']);p.add_argument('--directory',type=Path);p.add_argument('--negative-token-file',type=Path);a=p.parse_args()
try: try:
if a.mode=='preflight': if a.mode=='preflight':
with tempfile.TemporaryDirectory() as t:prepare(Path(t)) with tempfile.TemporaryDirectory() as t:prepare(Path(t))
print('Three frozen requests and pinned recipient passed; native claims checked at execution.') print('Three frozen requests and pinned recipient passed; native claims checked at execution.')
else:admin(a.directory,a.negative_token_file) else:
if a.mode=='resume-exec':
resume_receipt()
RECEIPT=ROOT/'docs/evidence/2026-09-16-t03-resume-exec.json'
admin(a.directory,a.negative_token_file,resume=a.mode=='resume-exec')
except Exception: except Exception:
if a.mode=='preflight':raise if a.mode=='preflight':raise
raise SystemExit(1) from None raise SystemExit(1) from None

View file

@ -0,0 +1,38 @@
import importlib.util,unittest,json
from pathlib import Path
from unittest.mock import patch
from contextlib import contextmanager
from types import SimpleNamespace
from urllib.error import HTTPError,URLError
spec=importlib.util.spec_from_file_location('native',Path(__file__).resolve().parents[1]/'scripts/t03-native-execution.py');m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
class TestProbe(unittest.TestCase):
def check(self,status):
scoped=SimpleNamespace(token='synthetic-revoked-token',token_capabilities=lambda *a,**k:['deny'])
session=SimpleNamespace(client=scoped,revocation_succeeded=False)
@contextmanager
def session_context(role):
yield session
session.revocation_succeeded=True;scoped.token=''
client=SimpleNamespace(addr='http://127.0.0.1:18200',approle_session=session_context)
class Opener:
def open(self,req,timeout):
self_test.assertEqual(req.get_header('X-vault-token'),'synthetic-revoked-token')
self_test.assertEqual(scoped.token,'')
if status:raise HTTPError(req.full_url,status,'synthetic',{},None)
return unittest.mock.MagicMock()
self_test=self
with patch('urllib.request.build_opener',return_value=Opener()):return m.verify_session_cleanup(client)
def test_revoked_explicit_token_refused(self):self.assertEqual(self.check(403)['revoked_token_lookup_status'],403)
def test_live_token_refused(self):
with self.assertRaisesRegex(ValueError,'revoked_token_still_usable'):self.check(0)
def test_server_failure_is_not_revocation_proof(self):
with self.assertRaisesRegex(ValueError,'revocation_probe_not_definitive'):self.check(500)
def test_reconciled_prior_phase(self):
prior={'phase':'verify_attempt_started','failure_code':'revoked_token_still_usable','actions':[{'action':'apply','approval_id':m.IDS['apply'],'exit_code':0,'limits':{'token_ttl':900,'token_max_ttl':1800,'secret_id_ttl':900,'secret_id_num_uses':1,'token_num_uses':8}}]}
with patch.object(Path,'read_text',return_value=json.dumps(prior)):
self.assertEqual(m.resume_receipt(),prior)
prior['actions'][0]['approval_id']='other'
with patch.object(Path,'read_text',return_value=json.dumps(prior)):
with self.assertRaisesRegex(ValueError,'prior_apply_not_verified'):m.resume_receipt()
if __name__=='__main__':unittest.main()