239 lines
13 KiB
Python
239 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""Exact install-only action; execute remotely only after founder approval."""
|
|
import argparse
|
|
import hashlib
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path, PurePosixPath
|
|
import pwd
|
|
import shutil
|
|
import signal
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import tarfile
|
|
import time
|
|
|
|
DIGEST = 'f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d'
|
|
ARCHIVE = '4c55b2740e759175d645705518fc33db30b26b4611f68e052a4ca909173084c7'
|
|
RUNTIME_MODULE = '19b5fb687288bbc836978b0251599dc1e4d1593e8b5efee85a6abb6a7f388412'
|
|
STORE_MODULE = 'c3d6ec09d08881611d3d02b8f55d68deed6ecfedaa1a809b6b11c57cc4ca97cf'
|
|
REHEARSAL_MODULE = '192f654cad4a5f065c7faa5e35b03493297a472e8d7fe641be2c42c5fb00dcc3'
|
|
STAGE = Path('/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017')
|
|
STORE = Path('/home/tegwick/.helixforge-factory/runtimes')
|
|
DESTINATION = STORE/DIGEST
|
|
LOGICAL_BYTES = 260668241
|
|
ARCHIVE_BYTES = 109931109
|
|
MEMBERS = 1448
|
|
ENV = {'HOME':'/home/tegwick','PATH':'/usr/bin:/bin','LANG':'C.UTF-8','PYTHONDONTWRITEBYTECODE':'1'}
|
|
|
|
|
|
def require(value, code):
|
|
if not value: raise ValueError(code)
|
|
|
|
|
|
def file_digest(path):
|
|
with path.open('rb') as source:return hashlib.file_digest(source,'sha256').hexdigest()
|
|
|
|
|
|
def protected(path, *, directory=False, mode=None):
|
|
s=path.lstat()
|
|
require(not stat.S_ISLNK(s.st_mode) and s.st_uid==1000 and s.st_gid==1000,'path_ownership')
|
|
require(stat.S_ISDIR(s.st_mode) if directory else stat.S_ISREG(s.st_mode),'path_type')
|
|
require(not stat.S_IMODE(s.st_mode)&0o022,'path_write_mode')
|
|
if mode is not None:require(stat.S_IMODE(s.st_mode)==mode,'path_exact_mode')
|
|
return s
|
|
|
|
|
|
def identify(supervisor):
|
|
require(os.getuid()==1000 and os.getgid()==1000 and pwd.getpwuid(1000).pw_name=='tegwick','account_identity')
|
|
for p in (Path('/home/tegwick'),Path('/home/tegwick/.helixforge-factory'),STORE):
|
|
protected(p,directory=True,mode=0o700 if p!=Path('/home/tegwick') else 0o750)
|
|
protected(STORE/'.install.lock',mode=0o600)
|
|
code,output=supervisor.run(['/usr/local/bin/kubectl','get','nodes','-o','json'],20,capture=True)
|
|
require(code==0,'node_readback')
|
|
nodes=json.loads(output)['items']
|
|
require(len(nodes)==1 and any(a['address']=='92.205.62.239' for a in nodes[0]['status']['addresses'])
|
|
and any(c['type']=='Ready' and c['status']=='True' for c in nodes[0]['status']['conditions']),'node_identity')
|
|
require(shutil.disk_usage(STORE).free >= 2*1024**3,'disk_headroom')
|
|
mem=dict(x.split(':',1) for x in Path('/proc/meminfo').read_text().splitlines())
|
|
require(int(mem['MemAvailable'].split()[0])*1024>=1024**3,'memory_headroom')
|
|
require(os.getloadavg()[0]<=2*(os.cpu_count() or 1),'load_headroom')
|
|
read_cpu=lambda:[int(x) for x in Path('/proc/stat').read_text().splitlines()[0].split()[1:9]]
|
|
a=read_cpu();time.sleep(1);b=read_cpu();d=[y-x for x,y in zip(a,b)]
|
|
require(1-(d[3]+d[4])/sum(d)<=0.85,'cpu_headroom')
|
|
|
|
|
|
def runtime_reader():
|
|
p=STAGE/'runtime.py';protected(p,mode=0o600)
|
|
require(file_digest(p)==RUNTIME_MODULE,'runtime_reader_pin')
|
|
spec=importlib.util.spec_from_file_location('approved_runtime_reader',p)
|
|
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def extract():
|
|
archive=STAGE/'runtime.tar.gz';protected(archive,mode=0o600)
|
|
require(archive.stat().st_size==ARCHIVE_BYTES and file_digest(archive)==ARCHIVE,'archive_pin')
|
|
root=STAGE/'runtime';require(not root.exists() and not root.is_symlink(),'extraction_already_present')
|
|
with tarfile.open(archive,mode='r:gz') as tf:
|
|
members=tf.getmembers();require(len(members)==MEMBERS,'archive_member_count')
|
|
seen=set(); logical=0
|
|
for info in members:
|
|
name=PurePosixPath(info.name)
|
|
require(not name.is_absolute() and name.parts and name.parts[0]=='runtime'
|
|
and all(x not in ('','..','.') for x in name.parts),'archive_name')
|
|
require(info.name not in seen,'archive_duplicate');seen.add(info.name)
|
|
require(info.uid==1000 and info.gid==1000 and (info.issym() or not info.mode&0o7022),'archive_modes')
|
|
require(info.isdir() or info.isfile() or info.issym(),'archive_special_member')
|
|
if info.isfile():logical+=info.size
|
|
if info.issym():require(not PurePosixPath(info.linkname).is_absolute(),'archive_absolute_symlink')
|
|
require(logical==LOGICAL_BYTES,'archive_size')
|
|
# Populate directories and files before any symlink; never traverse an
|
|
# archive-created symlink while extracting a later member.
|
|
dirs=[]
|
|
for info in members:
|
|
if not info.isdir():continue
|
|
p=STAGE/info.name;p.mkdir(mode=0o700);dirs.append((p,info.mode))
|
|
for info in members:
|
|
if not info.isfile():continue
|
|
p=STAGE/info.name
|
|
require(all(not x.is_symlink() for x in (p.parent,*p.parent.parents)),'archive_parent_alias')
|
|
fd=os.open(p,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
|
with os.fdopen(fd,'wb') as target, tf.extractfile(info) as source:
|
|
shutil.copyfileobj(source,target,1024*1024)
|
|
p.chmod(info.mode)
|
|
for info in members:
|
|
if info.issym():
|
|
p=STAGE/info.name;p.symlink_to(info.linkname)
|
|
require(p.resolve(strict=True).is_relative_to(root),'archive_symlink_escape')
|
|
for p,mode in reversed(dirs):p.chmod(mode)
|
|
require(runtime_reader().runtime_digest(root)==DIGEST,'extracted_complete_digest')
|
|
return root
|
|
|
|
|
|
def expired(signum, frame):
|
|
raise TimeoutError('bounded_stage_deadline')
|
|
|
|
|
|
def install_phase():
|
|
from sandboxer.extensions import runtime_store, runtime
|
|
source=STAGE/'runtime'
|
|
require(Path(runtime_store.__file__).resolve().is_relative_to(source)
|
|
and file_digest(Path(runtime_store.__file__))==STORE_MODULE,'installer_source_pin')
|
|
require(Path(runtime.__file__).resolve().is_relative_to(source)
|
|
and file_digest(Path(runtime.__file__))==RUNTIME_MODULE,'installed_digest_source_pin')
|
|
require(not DESTINATION.exists() and not DESTINATION.is_symlink(),'destination_not_absent')
|
|
signal.signal(signal.SIGALRM,expired);signal.alarm(180)
|
|
try:result=runtime_store.install_runtime(source,STORE,DIGEST)
|
|
finally:signal.alarm(0)
|
|
require(result['runtime']['path']==str(DESTINATION) and not result['reused']
|
|
and result['profile_activated'] is False and result['credential_delivery_configured'] is False,'installer_result')
|
|
print(json.dumps({'status':'installed','result':result},sort_keys=True))
|
|
|
|
|
|
def rehearse_phase(supervision):
|
|
from rein_aharness import owner_rehearsal
|
|
require(Path(owner_rehearsal.__file__).resolve().is_relative_to(DESTINATION)
|
|
and file_digest(Path(owner_rehearsal.__file__))==REHEARSAL_MODULE,'rehearsal_source_pin')
|
|
require(owner_rehearsal.SYSTEM_RO_BINDS==('/usr','/bin','/lib','/lib64','/etc/resolv.conf','/etc/ssl/certs')
|
|
and all(Path(p).exists() for p in owner_rehearsal.SYSTEM_RO_BINDS),'frozen_system_bind_contract')
|
|
signal.signal(signal.SIGALRM,expired);signal.alarm(240)
|
|
# Keep the frozen owner launch contract; use its supported Runner seam.
|
|
def bounded_run(argv,timeout):
|
|
require(argv[0]=='/usr/bin/bwrap' and '--die-with-parent' not in argv,'bwrap_runner_contract')
|
|
return supervision_request(supervision,[argv[0],'--die-with-parent',*argv[1:]],timeout)[0]
|
|
try:steps=owner_rehearsal.rehearse(DESTINATION,run=bounded_run,bwrap_bin='/usr/bin/bwrap')
|
|
finally:signal.alarm(0)
|
|
require([x.name for x in steps]==['rein-aharness','glas-harness','claude'],'rehearsal_steps')
|
|
report={x.name:x.exit_code for x in steps}
|
|
print(json.dumps({'status':'rehearsed' if all(x.ok for x in steps) else 'rehearsal_failed','steps':report,
|
|
'model_calls':0,'credential_reads':False,'owner_configuration_read':False},sort_keys=True))
|
|
return 0 if all(x.ok for x in steps) else 1
|
|
|
|
|
|
def supervision_module():
|
|
spec=importlib.util.spec_from_file_location('approved_process_supervision',STAGE/'process_supervision.py')
|
|
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def supervision_request(directory,argv,timeout):
|
|
return supervision_module().request(directory,argv,timeout)
|
|
|
|
|
|
def phase(supervisor, argv, timeout):
|
|
code,output=supervisor.run(argv,timeout,capture=True)
|
|
try:result=json.loads(output)
|
|
except Exception:raise ValueError('phase_receipt_unavailable') from None
|
|
return code,result
|
|
|
|
|
|
def main():
|
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--stage-inode',type=int)
|
|
parser.add_argument('--phase',choices=('install','rehearse'))
|
|
parser.add_argument('--supervision-dir')
|
|
args=parser.parse_args()
|
|
if args.phase=='install':install_phase();return 0
|
|
if args.phase=='rehearse':
|
|
require(args.supervision_dir==str(STAGE/'supervision'),'supervision_path')
|
|
return rehearse_phase(args.supervision_dir)
|
|
receipt={'proposal_id':'REINAH-WP-0008-T07-install-f599-r1','revision':2,'status':'failed',
|
|
'model_calls':0,'credential_reads':False,'owner_configuration_read':False,
|
|
'renewal_or_catalog_or_queue_mutation':False,'destination':str(DESTINATION)}
|
|
identity=None;supervisor=None;quiescent=False
|
|
try:
|
|
observed=protected(STAGE,directory=True,mode=0o700)
|
|
require(observed.st_ino==args.stage_inode and observed.st_dev==2049,'stage_identity')
|
|
identity=observed
|
|
os.nice(19)
|
|
supervisor=supervision_module().Supervisor(STAGE/'supervision')
|
|
identify(supervisor)
|
|
require(not DESTINATION.exists() and not DESTINATION.is_symlink(),'destination_not_absent')
|
|
require({p.name for p in STAGE.iterdir()}=={'runtime.tar.gz','runtime.py','install-verified.py','process_supervision.py','supervision'},'stage_inventory')
|
|
receipt['phase']='extract_and_verify';source=extract()
|
|
receipt['extracted_complete_digest_verified']=True
|
|
receipt['phase']='install'
|
|
code,result=phase(supervisor,[str(source/'bin/python3'),'-I','-B',str(STAGE/'install-verified.py'),'--phase','install'],200)
|
|
receipt['installation']=result;require(code==0 and result['status']=='installed','installation_failed')
|
|
require(runtime_reader().runtime_digest(DESTINATION)==DIGEST,'installed_complete_digest')
|
|
receipt['installed_complete_digest_verified']=True
|
|
receipt['phase']='offline_rehearsal'
|
|
code,result=phase(supervisor,[str(DESTINATION/'bin/python3'),'-I','-B',str(STAGE/'install-verified.py'),'--phase','rehearse','--supervision-dir',str(STAGE/'supervision')],260)
|
|
receipt['rehearsal']=result;require(code==0 and result['status']=='rehearsed','rehearsal_failed')
|
|
require(runtime_reader().runtime_digest(DESTINATION)==DIGEST,'post_rehearsal_complete_digest')
|
|
receipt['post_rehearsal_complete_digest_verified']=True
|
|
receipt['status']='installed_and_offline_rehearsal_verified'
|
|
except Exception as exc:
|
|
receipt['failure_type']=type(exc).__name__
|
|
if isinstance(exc,ValueError) and str(exc).replace('_','').isalnum():receipt['failure_code']=str(exc)
|
|
finally:
|
|
receipt['destination_exists']=DESTINATION.exists() or DESTINATION.is_symlink()
|
|
if supervisor is not None:
|
|
try:
|
|
receipt['process_supervision']=supervisor.close();quiescent=True
|
|
except Exception:
|
|
receipt['process_supervision']={'quiescent':False,'status':'cleanup_unverified'}
|
|
receipt['status']='failed'
|
|
if identity is not None and quiescent:
|
|
try:
|
|
current=protected(STAGE,directory=True,mode=0o700)
|
|
require((current.st_dev,current.st_ino)==(identity.st_dev,identity.st_ino),'cleanup_stage_identity')
|
|
require({p.name for p in STAGE.iterdir()} <= {'runtime.tar.gz','runtime.py','install-verified.py','process_supervision.py','runtime','supervision'},'cleanup_unexpected_stage_member')
|
|
shutil.rmtree(STAGE)
|
|
receipt['owned_staging_removed']=not STAGE.exists() and not STAGE.is_symlink()
|
|
except Exception:receipt['owned_staging_removed']=False;receipt['status']='failed'
|
|
else:
|
|
receipt['owned_staging_removed']=False
|
|
receipt['staging_retained_for_quiescence_review']=True
|
|
print(json.dumps(receipt,sort_keys=True))
|
|
return 0 if receipt['status']=='installed_and_offline_rehearsal_verified' else 1
|
|
|
|
|
|
if __name__=='__main__':
|
|
try:raise SystemExit(main())
|
|
except Exception as exc:
|
|
print(json.dumps({'status':'failed','failure_type':type(exc).__name__,'private_diagnostics_suppressed':True}))
|
|
raise SystemExit(1) from None
|