320 lines
16 KiB
Python
320 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
"""Default is a read-only plan. Docker execution awaits independent exact review."""
|
|
import argparse
|
|
import base64
|
|
import hashlib
|
|
import importlib.util
|
|
import io
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import secrets
|
|
import tarfile
|
|
import tempfile
|
|
import time
|
|
import uuid
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
MANIFEST = HERE / 'manifest.json'
|
|
|
|
# Fixed local diagnostic parser; no dependency or credential discovery.
|
|
_spec = importlib.util.spec_from_file_location('fixture_diagnostic_protocol', HERE / 'diagnostic_protocol.py')
|
|
diagnostics = importlib.util.module_from_spec(_spec)
|
|
_spec.loader.exec_module(diagnostics)
|
|
|
|
|
|
def require(condition):
|
|
if not condition:
|
|
raise RuntimeError('bounded_synthetic_fixture_refused')
|
|
|
|
|
|
def reviewed_manifest(expected):
|
|
raw = MANIFEST.read_bytes()
|
|
require(expected == hashlib.sha256(raw).hexdigest())
|
|
manifest = json.loads(raw)
|
|
for path, digest in manifest['source_sha256'].items():
|
|
require(bool(re.fullmatch(r'sha256:[0-9a-f]{64}', digest)))
|
|
require('sha256:' + hashlib.sha256(Path(path).read_bytes()).hexdigest() == digest)
|
|
return manifest
|
|
|
|
|
|
def load_containment(manifest):
|
|
spec = importlib.util.spec_from_file_location('reviewed_existing_containment', manifest['containment_helper'])
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def session_plan(manifest, run_id):
|
|
require(bool(re.fullmatch(r'[a-f0-9]{32}', run_id)))
|
|
anchor = 'nk49-kc-' + run_id + '-journal-postgres'
|
|
probe = 'nk49-kc-' + run_id + '-journal-probe'
|
|
return {'run_id': run_id, 'containers': [anchor, probe], 'worker': anchor, 'issuer': probe,
|
|
'images': {anchor: manifest['postgres_image'], probe: manifest['python_image']}, 'volumes': {}}
|
|
|
|
|
|
def base_args(session, name, network, user="1000:1000"):
|
|
uid, gid = user.split(":")
|
|
return ['docker', 'run', '--detach', '--pull=never', '--name', name, '--network', network,
|
|
'--label', 'net-kingdom.recovery-run=' + session['run_id'], '--read-only', '--cap-drop', 'ALL',
|
|
'--security-opt', 'no-new-privileges', '--user', user, '--memory', '384m', '--cpus', '0.5',
|
|
'--pids-limit', '64', '--log-driver', 'none', '--restart', 'no',
|
|
'--tmpfs', '/tmp:rw,nosuid,nodev,noexec,size=16777216,uid=' + uid + ',gid=' + gid + ',mode=0700']
|
|
|
|
|
|
def tar_bytes(files):
|
|
out = io.BytesIO()
|
|
with tarfile.open(fileobj=out, mode='w') as archive:
|
|
for name, value in files.items():
|
|
require(bool(re.fullmatch(r'[a-zA-Z0-9._/-]+', name)) and not name.startswith('/') and '..' not in Path(name).parts)
|
|
entry = tarfile.TarInfo(name)
|
|
entry.size = len(value)
|
|
entry.mode = 0o600
|
|
entry.uid = entry.gid = 1000
|
|
archive.addfile(entry, io.BytesIO(value))
|
|
return out.getvalue()
|
|
|
|
|
|
IMPORT = r'''
|
|
import base64,json,os,sys
|
|
from pathlib import Path
|
|
os.umask(0o077)
|
|
root=Path('/fixture')
|
|
(root/'source').mkdir(mode=0o700)
|
|
(root/'source/migrations').mkdir(mode=0o700)
|
|
(root/'source/tests').mkdir(mode=0o700)
|
|
for name,encoded in json.load(sys.stdin).items():
|
|
if name not in ('source/provisioner.py','source/group_provenance.py','source/group_provenance_postgres.py','source/migrations/group_journal_postgres_v1.sql','source/tests/test_service_groups.py','probe.py','private.json','diagnostic_protocol.py'):raise ValueError()
|
|
fd=os.open(root/name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
|
with os.fdopen(fd,'wb') as output:output.write(base64.b64decode(encoded,validate=True))
|
|
'''
|
|
|
|
|
|
def helper_python(manifest):
|
|
# Exact producer source installs into official Python's system prefix.
|
|
# Never fall back to PATH or another interpreter/virtual environment.
|
|
require(manifest.get('helper_interpreter') == '/usr/local/bin/python3')
|
|
return manifest['helper_interpreter']
|
|
|
|
|
|
INTERPRETER_PROBE = r"""
|
|
import json,sys
|
|
try:
|
|
import psycopg
|
|
valid=sys.executable=='/usr/local/bin/python3' and psycopg.__version__.split('.')[0]=='3'
|
|
print(json.dumps({'helper_ready':valid,'executable':sys.executable,'psycopg_version':psycopg.__version__}))
|
|
except Exception:
|
|
print(json.dumps({'helper_ready':False}))
|
|
"""
|
|
|
|
|
|
def preflight(adapter, manifest):
|
|
helper_python(manifest)
|
|
adapter.docker_environment() # Refuse ambient alternate routing before calls.
|
|
contexts = json.loads(adapter.command(['docker', 'context', 'inspect'], timeout=15))
|
|
require(len(contexts) == 1 and contexts[0]['Endpoints']['docker']['Host'] == manifest['fixed_endpoint'])
|
|
facts = json.loads(adapter.command(['docker', 'info', '--format',
|
|
'{"ID":{{json .ID}},"Name":{{json .Name}},"OSType":{{json .OSType}}}'], timeout=15))
|
|
require(facts == {'ID': manifest['engine_id'], 'Name': manifest['engine_name'], 'OSType': 'linux'})
|
|
for image in (manifest['postgres_image'], manifest['python_image']):
|
|
value = adapter.command(['docker', 'image', 'inspect', '--format', '{{.Id}}', image], timeout=15).decode().strip()
|
|
require(bool(re.fullmatch(r'sha256:[a-f0-9]{64}', value)))
|
|
|
|
|
|
def retain_helper_result(result, phase, raw, publish):
|
|
stages, value = diagnostics.parse_frames(raw)
|
|
safe = {'status': value['status'], 'stages': stages}
|
|
if value['status'] == 'failed':
|
|
safe['diagnostic'] = value['diagnostic']
|
|
result.setdefault('helper_diagnostics', {})[phase] = safe
|
|
publish() # Persist failure evidence before the phase oracle can raise.
|
|
return value
|
|
|
|
|
|
def execute(manifest, adapter, result, checkpoint=None):
|
|
session = session_plan(manifest, uuid.uuid4().hex)
|
|
result['session'] = {'run_id': session['run_id'], 'containers': session['containers'],
|
|
'label': 'net-kingdom.recovery-run=' + session['run_id']}
|
|
python = helper_python(manifest)
|
|
deadline = time.monotonic() + 210
|
|
original_command = adapter.command
|
|
def publish():
|
|
if checkpoint is not None:
|
|
try: checkpoint()
|
|
except OSError: result['receipt_checkpoint_failed'] = True
|
|
def mark(stage):
|
|
require(stage in diagnostics.DRIVER_STAGES)
|
|
result['driver_stage'] = stage
|
|
publish()
|
|
def event(argv):
|
|
require(len(result.setdefault('rpc_trace', [])) < 192)
|
|
operation = argv[1] if len(argv) > 1 and argv[1] in {'run','exec','rm','container','context','image','info','volume'} else 'other'
|
|
row = {'stage': result['driver_stage'], 'operation': operation, 'status': 'started'}
|
|
result['rpc_trace'].append(row)
|
|
publish()
|
|
return row
|
|
def run(argv, **kwargs):
|
|
row = event(argv)
|
|
remaining = deadline - time.monotonic()
|
|
require(remaining > 0)
|
|
try:
|
|
value = original_command(argv, timeout=min(50, remaining), **kwargs)
|
|
row['status'] = 'completed'
|
|
publish()
|
|
return value
|
|
except BaseException as error:
|
|
row['status'] = 'failed'
|
|
result['daemon_request_uncertain'] = True
|
|
result.setdefault('driver_failure', diagnostics.error_metadata(error, result['driver_stage']))
|
|
# Timeout output can contain complete public progress frames. Never
|
|
# retain raw stdout/stderr or command/exception text.
|
|
if result['driver_stage'] in manifest['phase_oracles']:
|
|
partial = getattr(error, 'stdout', None)
|
|
if partial:
|
|
try:
|
|
stages, _ = diagnostics.parse_frames(partial, require_final=False)
|
|
result.setdefault('helper_diagnostics', {})[result['driver_stage']] = {'status': 'interrupted', 'stages': stages}
|
|
except Exception:
|
|
pass
|
|
publish()
|
|
raise
|
|
mark('session_registered')
|
|
adapter.command = run
|
|
try:
|
|
anchor = session['worker']
|
|
args = base_args(session, anchor, 'none', user='999:999') + [
|
|
'--tmpfs', '/var/lib/postgresql:rw,nosuid,nodev,noexec,size=1048576,uid=999,gid=999,mode=0700',
|
|
'--tmpfs', '/var/lib/postgresql/data:rw,nosuid,nodev,noexec,size=134217728,uid=999,gid=999,mode=0700',
|
|
'--tmpfs', '/run/fixture:rw,nosuid,nodev,noexec,size=1048576,uid=999,gid=999,mode=0700',
|
|
'--env', 'PGDATA=/var/lib/postgresql/data', '--entrypoint', '/bin/sh',
|
|
manifest['postgres_image'], '-c', 'sleep 240']
|
|
mark('anchor_launch')
|
|
run(args)
|
|
mark('anchor_containment')
|
|
adapter.inspect_session({**session, 'containers': [anchor], 'issuer': anchor}, worker_only=True)
|
|
admin_password, runtime_password = secrets.token_hex(24), secrets.token_hex(24)
|
|
mark('postgres_private_import')
|
|
run(['docker', 'exec', '-i', anchor, 'tar', '-xf', '-', '-C', '/run/fixture'],
|
|
input=tar_bytes({'password': admin_password.encode()}))
|
|
mark('postgres_initdb')
|
|
initialized = json.loads(run(['docker', 'exec', anchor, '/bin/sh', '-c',
|
|
"if initdb -D /var/lib/postgresql/data --username=fixture_admin --auth-local=trust --auth-host=scram-sha-256 --pwfile=/run/fixture/password > /dev/null 2>&1; then echo '{\"postgres_initdb\":true}'; else echo '{\"postgres_initdb\":false}'; fi"], max_output=256))
|
|
result['postgres_initdb_complete'] = initialized == {'postgres_initdb': True}
|
|
publish()
|
|
require(result['postgres_initdb_complete'])
|
|
mark('postgres_launch')
|
|
run(['docker', 'exec', '--detach', anchor, '/bin/sh', '-c',
|
|
'exec postgres -D /var/lib/postgresql/data -c listen_addresses=127.0.0.1 -c unix_socket_directories=/run/fixture > /dev/null 2>&1'])
|
|
mark('anchor_identity')
|
|
anchor_id = run(['docker', 'container', 'inspect', anchor, '--format', '{{.Id}}']).decode().strip()
|
|
require(bool(re.fullmatch(r'[a-f0-9]{64}', anchor_id)))
|
|
args = base_args(session, session['issuer'], 'container:' + anchor_id) + [
|
|
'--tmpfs', '/fixture:rw,nosuid,nodev,noexec,size=16777216,uid=1000,gid=1000,mode=0700',
|
|
'--entrypoint', python, manifest['python_image'], '-c', 'import time;time.sleep(240)']
|
|
mark('helper_launch')
|
|
run(args)
|
|
mark('full_containment')
|
|
adapter.inspect_session(session, worker_only=True)
|
|
result['containment_verified_before_generated_private_import'] = True
|
|
mark('helper_interpreter')
|
|
interpreter = json.loads(run(['docker', 'exec', session['issuer'], python, '-c', INTERPRETER_PROBE], max_output=4096))
|
|
require(interpreter.get('helper_ready') is True and interpreter.get('executable') == python)
|
|
require(isinstance(interpreter.get('psycopg_version'), str) and re.fullmatch(r'3\.[0-9]+\.[0-9]+(?:[a-z]+[0-9]+)?', interpreter['psycopg_version']))
|
|
result['helper_interpreter_verified_before_candidate_import'] = True
|
|
result['helper_psycopg_version'] = interpreter['psycopg_version']
|
|
files = {'source/' + name: (HERE.parent / 'postimage/identity-provisioner' / name).read_bytes()
|
|
for name in ('provisioner.py', 'group_provenance.py', 'group_provenance_postgres.py',
|
|
'migrations/group_journal_postgres_v1.sql')}
|
|
files['source/tests/test_service_groups.py'] = (HERE / 'test_service_groups.py').read_bytes()
|
|
files.update({'probe.py': (HERE / 'probe.py').read_bytes(), 'diagnostic_protocol.py': (HERE / 'diagnostic_protocol.py').read_bytes(), 'private.json': json.dumps({
|
|
'admin_password': admin_password, 'runtime_password': runtime_password}).encode()})
|
|
mark('candidate_import')
|
|
run(['docker', 'exec', '-i', session['issuer'], python, '-c', IMPORT],
|
|
input=json.dumps({k: base64.b64encode(v).decode() for k, v in files.items()}).encode())
|
|
result['checks'] = {}
|
|
backup = None
|
|
active_database = 'postgres'
|
|
for phase in ('bootstrap', 'restart', 'concurrency', 'fork', 'lost-ack', 'restored'):
|
|
if phase == 'restored':
|
|
require(backup is not None)
|
|
mark('database_restore')
|
|
run(['docker', 'exec', '-i', anchor, 'pg_restore', '--host', '/run/fixture', '-U', 'fixture_admin',
|
|
'--dbname', active_database, '--clean', '--if-exists', '--single-transaction', '--exit-on-error', '--no-owner'], input=backup)
|
|
mark(phase)
|
|
raw = run(['docker', 'exec', session['issuer'], python, '/fixture/probe.py', phase], max_output=32768)
|
|
value = retain_helper_result(result, phase, raw, publish)
|
|
expected = set(manifest['phase_oracles'][phase])
|
|
require(value.get('status') == 'passed' and set(value.get('checks', {})) == expected)
|
|
require(all(v is True for v in value['checks'].values()))
|
|
result['checks'].update(value['checks'])
|
|
result['psycopg_version'] = value['psycopg_version']
|
|
require(value.get('active_database') in {'postgres', 'fixture_fork'})
|
|
active_database = value['active_database']
|
|
if phase == 'bootstrap':
|
|
mark('database_dump')
|
|
backup = run(['docker', 'exec', anchor, 'pg_dump', '--host', '/run/fixture', '-U', 'fixture_admin',
|
|
'--dbname', 'postgres', '--format=custom', '--no-owner'], max_output=4194304)
|
|
result['status'] = 'synthetic_postgres_journal_compatibility_passed'
|
|
mark('complete')
|
|
except BaseException as error:
|
|
result.setdefault('driver_failure', diagnostics.error_metadata(error, result['driver_stage']))
|
|
publish()
|
|
raise
|
|
finally:
|
|
mark('cleanup')
|
|
def cleanup_command(argv, **kwargs):
|
|
row = event(argv)
|
|
try:
|
|
value = original_command(argv, timeout=15, **kwargs)
|
|
row['status'] = 'completed'
|
|
publish()
|
|
return value
|
|
except BaseException:
|
|
result['daemon_request_uncertain'] = True
|
|
row['status'] = 'failed'
|
|
publish()
|
|
raise
|
|
adapter.command = cleanup_command
|
|
try:
|
|
adapter.cleanup(session, result)
|
|
result['container_cleanup_verified'] = True
|
|
except BaseException:
|
|
result['status'] = 'failed'
|
|
result['container_cleanup_verified'] = False
|
|
adapter.command = original_command
|
|
publish()
|
|
if result.get('daemon_request_uncertain') or result.get('receipt_checkpoint_failed'):
|
|
result['status'] = 'failed'
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--plan', action='store_true')
|
|
parser.add_argument('--execute-reviewed-fixture', action='store_true')
|
|
parser.add_argument('--expected-manifest-sha256')
|
|
args = parser.parse_args()
|
|
if not args.execute_reviewed_fixture:
|
|
print(MANIFEST.read_text())
|
|
return 0
|
|
require(not args.plan)
|
|
manifest = reviewed_manifest(args.expected_manifest_sha256)
|
|
adapter = load_containment(manifest)
|
|
preflight(adapter, manifest)
|
|
os.umask(0o077)
|
|
output = Path(tempfile.mkdtemp(prefix='nk48-postgres-native-review-', dir='/tmp')) / 'result.json'
|
|
result = {'schema': 'nk48.synthetic-postgres-journal-result.v1', 'status': 'failed',
|
|
'manifest_sha256': args.expected_manifest_sha256, 'production_contacted': False,
|
|
'actual_credentials_used': False, 'native_acceptance_claimed': False}
|
|
try:
|
|
execute(manifest, adapter, result, checkpoint=lambda: output.write_text(json.dumps(result, indent=2) + '\n'))
|
|
except BaseException:
|
|
result['status'] = 'failed'
|
|
finally:
|
|
output.write_text(json.dumps(result, indent=2) + '\n')
|
|
print(json.dumps({'status': result['status'], 'safe_result': str(output)}))
|
|
return int(result['status'] != 'synthetic_postgres_journal_compatibility_passed')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|