Recognize verified preserved relay ownership during later updates
This commit is contained in:
@@ -523,3 +523,23 @@ Source review found byte-download progress unconditionally changes Updating to
|
||||
Installing; failure cleanup only releases Updating. The narrow progress-state
|
||||
fix and regression are pending. This is not full target rollback acceptance.
|
||||
The recovered guest is QMP-paused without reboot for serialized validation.
|
||||
|
||||
The progress-state fix at 105454bd passed the full isolated suite: **2,026
|
||||
tests**, zero failures, five existing ignores, all 532 inputs unchanged. Its
|
||||
matching executable retained all seven IDs across manager startup. Actual RPC
|
||||
`2d4c8fc9-ee90-4167-a2d3-90647e756df0` safely restored before target startup
|
||||
and **returned package state to Running with progress cleared**, accepting the
|
||||
state fix on the actual manager path.
|
||||
|
||||
That transaction exposed a preserved-relay ownership edge: native pre-target
|
||||
recovery publishes the latest operation as installed-recipe owner even when the
|
||||
relay container/image is preserved. The helper incorrectly required that owner
|
||||
to be the historical image-producing operation. The correction separately
|
||||
validates a unique terminal schema-2 preservation owner against exact running
|
||||
intent, live container ID, raw configuration hash, image and unit body, then
|
||||
retains the existing unique image ancestry to the qualified original. It adds
|
||||
no image edge or binary-only fallback. **59 controller tests pass**, and a
|
||||
separate candidate verifier passed against the actual preserved guest relay and
|
||||
record chain without replacing the installed helper or modifying journals.
|
||||
Matching embedded-helper build and full target rollback/cutover remain required;
|
||||
the 2,026-test receipt predates this helper-only correction.
|
||||
|
||||
@@ -86,7 +86,25 @@ elif [ "$1" = signal ]; then
|
||||
else exit 1; fi
|
||||
'''
|
||||
LEGACY_RELAY_IMAGE = '061516573b143b44e331f960036a6a3dc43c9b256ef8ca71afedbeb2cf797a4b'
|
||||
def verify_relay_image_lineage(image, unit_sha256, records, installed=None):
|
||||
def verify_relay_image_lineage(image, unit_sha256, records, installed=None, current=None):
|
||||
preserved_owner=False
|
||||
if image.removeprefix('sha256:')!=LEGACY_RELAY_IMAGE:
|
||||
require(isinstance(installed,dict) and installed.get('schema')==1 and installed.get('name')=='indeedhub-relay','Relay installed recipe missing')
|
||||
owners=[r for r in records if r.get('id')==installed.get('operation')]
|
||||
require(len(owners)==1,'Relay installed owner is missing or ambiguous')
|
||||
owner=owners[0]
|
||||
try:require(str(uuid.UUID(owner['id']))==owner['id'],'Invalid relay installed operation')
|
||||
except (KeyError,ValueError,AttributeError):raise RuntimeError('Invalid relay installed operation')
|
||||
require(owner.get('schema') in (1,2) and owner.get('package')=='indeedhub' and owner.get('phase')=='Restored' and owner.get('cleanup_done') is True,'Relay installed owner is not a completed recovery')
|
||||
members=[m for m in owner.get('members',[]) if m.get('original',{}).get('name')=='indeedhub-relay']
|
||||
require(len(members)==1,'Relay installed owner has ambiguous members')
|
||||
member=members[0];original=member['original']
|
||||
if member.get('preserve_original') is True:
|
||||
require(owner['schema']==2 and owner.get('target_startup_began') is False and original.get('running') is True,'Relay preservation ownership changed')
|
||||
require(isinstance(current,dict) and current.get('name')=='indeedhub-relay' and original.get('container_id')==current.get('container_id') and original.get('config_sha256')==current.get('config_sha256'),'Relay preserved live identity changed')
|
||||
require(re.fullmatch('[0-9a-f]{64}',original.get('container_id','')) is not None and re.fullmatch('[0-9a-f]{64}',original.get('config_sha256','')) is not None,'Invalid preserved relay identity')
|
||||
require(original.get('image','').removeprefix('sha256:')==image.removeprefix('sha256:') and installed.get('body')==original.get('body') and isinstance(original.get('body'),str) and hashlib.sha256(original['body'].encode()).hexdigest()==unit_sha256,'Relay preserved image or recipe changed')
|
||||
preserved_owner=True
|
||||
seen=set()
|
||||
for _ in range(16):
|
||||
image=image.removeprefix('sha256:')
|
||||
@@ -107,7 +125,7 @@ def verify_relay_image_lineage(image, unit_sha256, records, installed=None):
|
||||
require((record['schema']==1 and (member.get('preserve_original') is None or member.get('preserve_original') is False) or record['schema']==2 and (record.get('target_startup_began') is True and member.get('preserve_original') is None or record.get('target_startup_began') is False and member.get('preserve_original') is False)) and recovery.get('operation_id')==record['id'] and recovery.get('source_container_id')==original.get('container_id'),'Relay recovery ownership changed')
|
||||
require(hashlib.sha256(member['pinned_original_body'].encode()).hexdigest()==unit_sha256,'Relay recovery unit lineage changed')
|
||||
require(re.fullmatch('[0-9a-f]{64}',original.get('container_id','')) is not None,'Invalid relay source identity')
|
||||
if len(seen)==1:require(isinstance(installed,dict) and installed.get('schema')==1 and installed.get('name')=='indeedhub-relay' and installed.get('operation')==record['id'] and installed.get('body')==member['pinned_original_body'],'Relay installed recipe does not own recovery lineage')
|
||||
if len(seen)==1:require(preserved_owner and installed['operation']!=record['id'] or installed.get('operation')==record['id'] and installed.get('body')==member['pinned_original_body'],'Relay installed recipe does not own recovery lineage')
|
||||
matches.append((original['image'],hashlib.sha256(original['body'].encode()).hexdigest()))
|
||||
require(len(matches)==1,'Relay image lacks unique completed owned recovery lineage')
|
||||
image,unit_sha256=matches[0]
|
||||
@@ -356,7 +374,7 @@ class Controller:
|
||||
directory=self.data/'update-transactions'/'installed-units';path=directory/'indeedhub-relay.json'
|
||||
require(directory.is_dir() and not directory.is_symlink() and directory.stat().st_uid==os.getuid() and directory.stat().st_mode & 0o077==0 and path.is_file() and not path.is_symlink() and path.stat().st_uid==os.getuid() and path.stat().st_mode & 0o077==0 and path.stat().st_size<=1024*1024,'Unsafe relay installed recipe')
|
||||
installed=json.loads(path.read_text())
|
||||
verify_relay_image_lineage(member['image_id'],member['unit_sha256'],records,installed)
|
||||
verify_relay_image_lineage(member['image_id'],member['unit_sha256'],records,installed,member)
|
||||
image=images[0];expected=(LEGACY_API_CMD,['docker-entrypoint.sh']) if role=='api' else (LEGACY_RELAY_CMD,None)
|
||||
require((image['Config'].get('Cmd'),image['Config'].get('Entrypoint'))==expected,'Unrecognized legacy signal command')
|
||||
source=pathlib.Path(self.run(['systemctl','--user','show',member['name']+'.service','--property=SourcePath','--value']).decode().strip())
|
||||
|
||||
@@ -562,6 +562,31 @@ console.log('process identity cases passed');'''
|
||||
if change=='wrong-body':bad['members'][0]['pinned_original_body']='changed'
|
||||
if change=='cycle':bad['members'][0]['original']['image']=image
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage(image,digest,[bad],installed)
|
||||
def test_preserved_relay_owner_attests_identity_without_replacing_image_ancestry(self):
|
||||
import copy,hashlib
|
||||
image='d'*64;body='[Container]\nImage='+image+'\n';digest=hashlib.sha256(body.encode()).hexdigest()
|
||||
producer={'schema':2,'id':self.operation,'package':'indeedhub','phase':'Restored','cleanup_done':True,'target_startup_began':False,'members':[{'original':{'name':'indeedhub-relay','container_id':'e'*64,'image':module.LEGACY_RELAY_IMAGE,'body':'base'},'pinned_original_body':body,'preserve_original':False,'recovery_image':{'image':image,'operation_id':self.operation,'source_container_id':'e'*64}}]}
|
||||
current={'name':'indeedhub-relay','container_id':'c'*64,'config_sha256':'a'*64}
|
||||
owner={'schema':2,'id':str(uuid.uuid4()),'package':'indeedhub','phase':'Restored','cleanup_done':True,'target_startup_began':False,'members':[{'original':dict(current,image=image,body=body,running=True),'preserve_original':True,'recovery_image':{'image':'f'*64}}]}
|
||||
installed={'schema':1,'name':'indeedhub-relay','operation':owner['id'],'body':body}
|
||||
module.verify_relay_image_lineage(image,digest,[producer,owner],installed,current)
|
||||
newer=copy.deepcopy(owner);newer['id']=str(uuid.uuid4());newer['members'][0]['recovery_image']['image']='b'*64
|
||||
module.verify_relay_image_lineage(image,digest,[producer,owner,newer],dict(installed,operation=newer['id']),current)
|
||||
for records in ([owner],[producer],[producer,owner,owner]):
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage(image,digest,records,installed,current)
|
||||
for key,value in [('container_id','b'*64),('config_sha256','b'*64),('name','indeedhub-api')]:
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage(image,digest,[producer,owner],installed,dict(current,**{key:value}))
|
||||
for field,value in [('schema',1),('target_startup_began',True),('target_startup_began',0),('cleanup_done',False),('phase','Restoring')]:
|
||||
bad=copy.deepcopy(owner);bad[field]=value
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage(image,digest,[producer,bad],installed,current)
|
||||
for field,value in [('preserve_original',False),('preserve_original',1),('preserve_original',None)]:
|
||||
bad=copy.deepcopy(owner);bad['members'][0][field]=value
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage(image,digest,[producer,bad],installed,current)
|
||||
for field,value in [('image','b'*64),('body','changed'),('running',False),('running',1),('container_id','bad'),('config_sha256','bad')]:
|
||||
bad=copy.deepcopy(owner);bad['members'][0]['original'][field]=value
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage(image,digest,[producer,bad],installed,current)
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage(image,digest,[producer,owner],dict(installed,body='changed'),current)
|
||||
with self.assertRaises(RuntimeError):module.verify_relay_image_lineage('b'*64,digest,[producer,owner],installed,current)
|
||||
def test_column_commitments_retain_logical_order_and_every_semantic_field(self):
|
||||
import copy
|
||||
columns=[['first','integer',True,'','',''],['last','text',False,'','','']]
|
||||
|
||||
Reference in New Issue
Block a user