Renew T03 requests using Railiance time and bind native execution
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
parent
3bd3a2e87b
commit
5b3041cfb5
7 changed files with 219 additions and 14 deletions
|
|
@ -1,5 +1,5 @@
|
|||
"""Silent, single-attempt creation of the three operator-reviewed T03 requests."""
|
||||
import base64,json,os,stat,subprocess,time
|
||||
import argparse,base64,json,os,stat,subprocess,time,sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime,timezone,timedelta
|
||||
from urllib.request import Request,build_opener,ProxyHandler,HTTPRedirectHandler
|
||||
|
|
@ -7,7 +7,7 @@ from urllib.parse import urlencode
|
|||
from urllib.error import HTTPError
|
||||
import jwt
|
||||
ROOT=Path('/home/worsch/railiance-platform')
|
||||
RECEIPT=ROOT/'docs/evidence/2026-09-14-t03-native-approval-requests.json'
|
||||
RECEIPT=ROOT/'docs/evidence/2026-09-15-t03-renewed-approval-requests.json'
|
||||
POLICY='workload-kv-read-secrets-engine-requester-client'
|
||||
KV='platform/data/workloads/secrets-engine/approval-requester'
|
||||
ISSUER='https://kc.coulomb.social'
|
||||
|
|
@ -55,7 +55,7 @@ def main(receipt):
|
|||
if bao('token','capabilities','-format=json',path)!=expected:raise ValueError('reader_scope_failed')
|
||||
helper=Path.home()/'.vault-token';info=helper.lstat()
|
||||
if not stat.S_ISREG(info.st_mode) or stat.S_IMODE(info.st_mode)!=0o600 or info.st_uid!=os.getuid():raise ValueError('private_helper_required')
|
||||
status,data=http('https://bao.coulomb.social/v1/'+KV+'?version=1',headers={'X-Vault-Token':helper.read_text().strip()})
|
||||
status,data=http('http://127.0.0.1:18200/v1/'+KV+'?version=1',headers={'X-Vault-Token':helper.read_text().strip()})
|
||||
if status!=200 or data['data']['metadata']['version']!=1:raise ValueError('requester_delivery_failed')
|
||||
secret=data['data']['data']['CLIENT_SECRET'];del data
|
||||
def exchange(scope,credential=secret):
|
||||
|
|
@ -68,7 +68,9 @@ def main(receipt):
|
|||
header=jwt.get_unverified_header(token)
|
||||
keys=[key for key in jwks['keys'] if key['kid']==header.get('kid')]
|
||||
if header.get('alg')!='RS256' or len(keys)!=1:raise ValueError('signing_key_failed')
|
||||
claims=jwt.decode(token,jwt.PyJWK.from_dict(keys[0]).key,algorithms=['RS256'],issuer=ISSUER,audience='approval-engine',options={'strict_aud':True,'require':['sub','iat','exp','iss','aud']})
|
||||
claims=jwt.decode(token,jwt.PyJWK.from_dict(keys[0]).key,algorithms=['RS256'],issuer=ISSUER,audience='approval-engine',options={'strict_aud':True,'require':['sub','iat','exp','iss','aud'],'verify_iat':False,'verify_exp':False,'verify_nbf':False})
|
||||
from t03_request_time import validate_token_time
|
||||
validate_token_time(claims, CLOCK.read())
|
||||
expected={'sub':'secrets-engine','tenant':'tenant:platform','principal_type':'service','scope':'approval:create','roles':['secrets-engine-requester'],'groups':[]}
|
||||
if any(claims.get(k)!=v for k,v in expected.items()) or claims['exp']-claims['iat']!=900:raise ValueError('requester_claims_failed')
|
||||
for scope in ('approval:approve','approval:consume','approval:read'):
|
||||
|
|
@ -76,16 +78,20 @@ def main(receipt):
|
|||
if exchange('approval:create','invalid-synthetic-credential')[0]!=401:raise ValueError('wrong_secret_not_refused')
|
||||
receipt.update(phase='requester_verified',signature_verified=True,excess_scopes_refused=True,wrong_secret_refused=True,reader_scope_verified=True)
|
||||
for memo,record,pdp_digest in prepared:
|
||||
now=datetime.now(timezone.utc)
|
||||
now=datetime.fromtimestamp(CLOCK.read().lower_ns/1e9,timezone.utc)
|
||||
body={'id':record['approval_id'],'binding':record['binding'],'validity':{'not_before':now.isoformat(),'expires_at':(now+timedelta(hours=24)).isoformat()},'required_count':1,'human_control':True,'pdp_path':True,'pdp_digest':pdp_digest}
|
||||
receipt['phase']='create_attempt:'+record['action'];RECEIPT.write_text(json.dumps(receipt,indent=2)+'\n')
|
||||
status,result=http('http://127.0.0.1:18281/v1/approvals',body=json.dumps(body).encode(),headers={'Authorization':'Bearer '+token,'Content-Type':'application/json'})
|
||||
if status!=201 or result['status']!='requested' or result['entries'] or result['binding']['digest']!=record['binding_digest'] or result['binding']['human_control'] is not True or result['binding']['pdp_digest']!=pdp_digest:raise ValueError('request_creation_requires_reconciliation')
|
||||
receipt['requests'].append({'memo_id':memo.removeprefix('memo:'),'action':record['action'],'approval':result})
|
||||
receipt['requests'].append({'memo_id':memo.removeprefix('memo:'),'memo_version':record['memo_version'],'action':record['action'],'approval':result})
|
||||
RECEIPT.write_text(json.dumps(receipt,indent=2)+'\n')
|
||||
receipt.update(status='created',phase='three_unapproved_requests_created',human_entries_created=False,approvals_consumed=False)
|
||||
|
||||
if __name__=='__main__':
|
||||
p=argparse.ArgumentParser();p.add_argument('--clock-trust-file',type=Path,required=True);a=p.parse_args()
|
||||
sys.path.insert(0,'/home/worsch/railiance-clock/src')
|
||||
from railiance_clock.client import Clock,FileTrust
|
||||
CLOCK=Clock(FileTrust(a.clock_trust_file));CLOCK.read()
|
||||
if RECEIPT.exists():raise SystemExit(1)
|
||||
receipt={'observed_at':datetime.now(timezone.utc).isoformat(),'status':'failed','phase':'preflight','requests':[],'credential_values_emitted':False}
|
||||
try:main(receipt)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue