railiance-apps/tools/vergabe_pilot_restore_remote.py
tegwick 1a35ee8113
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Record offsite recovery proof and prepare isolated native restore
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1abf1-7e95-7141-87c9-ee78332bf790
2026-11-06 09:28:26 +01:00

175 lines
9.1 KiB
Python

#!/usr/bin/env python3
"""Founder workstation transport for the isolated native restore rehearsal.
Default reviews only. --execute stages the provider-retrieved point privately,
runs the scoped node helper, then removes its stage after successful cleanup.
--cleanup NONCE removes only that journal's owned clone and the private stage.
"""
import argparse
import hashlib
import json
import os
import re
import shlex
import shutil
import subprocess
import sys
import tempfile
import uuid
from pathlib import Path
import vergabe_pilot_export as export
import vergabe_pilot_export_remote as transfer
import vergabe_pilot_restore as restore
from vergabe_pilot_recovery_store import Refused, digest, private_directory, require
PINS = {
"vergabe_pilot_recovery_store": "724a2dd9db8a0f57e1efa709ec989d8a47c681a86b1fca923bfdfea1698ae23c",
"vergabe_pilot_export": "b45733fa10ae59b56563045c43c53b5f824a74e1114f68fbed28404f14b43e3a",
"vergabe_pilot_restore": "407df786ce8994744df7079771ddadf56019f2d384d4b6cbda8dc8d051270dd4",
}
PROVIDER_RECEIPT = "provider-receipt-db975b7a730f.json"
STAGING = r'''
import json,os,pwd,re,stat,sys
from pathlib import Path
try:
nonce,mode=sys.argv[1:]
assert re.fullmatch('[0-9a-f]{12}',nonce) and mode in ('create','cleanup')
account=pwd.getpwuid(os.getuid())
assert account.pw_name=='tegwick' and account.pw_dir=='/home/tegwick'
directory=Path(account.pw_dir)/('vergabe-pilot-restore-'+nonce)
if mode=='create':directory.mkdir(mode=0o700)
else:
if directory.exists() or directory.is_symlink():
info=directory.lstat()
assert stat.S_ISDIR(info.st_mode) and info.st_uid==os.getuid() and not info.st_mode&0o077
allowed={'database.dump','stores.tar.gz','manifest.private.json','receipt.json','provider.json','origin.json'}
assert {p.name for p in directory.iterdir()}<=allowed
for path in directory.iterdir():
assert stat.S_ISREG(path.lstat().st_mode) and path.lstat().st_uid==os.getuid()
path.unlink()
directory.rmdir()
print(json.dumps({'status':'restore_stage_'+('ready' if mode=='create' else 'removed'),'nonce':nonce}))
except Exception:
print(json.dumps({'status':'restore_stage_failed','details_suppressed':True}))
raise SystemExit(1)
'''
def remote_command(nonce, cleanup=False):
require(re.fullmatch(r"[0-9a-f]{12}", nonce), "invalid_restore_nonce")
sources = {}
for name, pin in PINS.items():
raw = Path(__file__).with_name(name + ".py").read_bytes()
require(hashlib.sha256(raw).hexdigest() == pin, "restore_source_pin_mismatch")
sources[name] = raw.decode()
bootstrap = "import sys,types;"
for name in ("vergabe_pilot_recovery_store", "vergabe_pilot_export"):
bootstrap += ("m=types.ModuleType(" + repr(name) + ");sys.modules[m.__name__]=m;"
"exec(compile(" + repr(sources[name]) + "," + repr(name + ".py") + ",'exec'),m.__dict__);")
bootstrap += ("sys.argv=['vergabe_pilot_restore.py',*sys.argv[1:]];exec(compile(" +
repr(sources["vergabe_pilot_restore"]) + ",'vergabe_pilot_restore.py','exec'),{'__name__':'__main__'})")
operation = ["sudo", "/usr/bin/env", "KUBECONFIG=/etc/rancher/k3s/k3s.yaml", "/usr/bin/python3", "-c",
bootstrap, "--nonce", nonce, *(["--cleanup"] if cleanup else [])]
text = shlex.join(operation)
require(len(text.encode()) < 120000, "restore_transport_size_bound")
return ["ssh", "-t", "railiance01", text]
def stage_command(nonce, mode):
return ["ssh", "-t", "railiance01", shlex.join(["/usr/bin/python3", "-c", STAGING, nonce, mode])]
def verify_input(receipt_path):
require(receipt_path.is_absolute() and transfer.outside_git(receipt_path.resolve()),
"private_provider_receipt_outside_git_required")
export.regular(receipt_path, os.geteuid())
require(receipt_path.stat().st_size <= 16384, "provider_receipt_bound")
body = json.loads(receipt_path.read_text())
kit = receipt_path.parent
private_directory(kit)
require(body["status"] == "published_read_back_and_decrypted" and body["run_id"] == export.RUN_ID
and body["warden_exit_code"] == 0 and body["ciphertext_sha256"] == restore.CIPHER
and body["plaintext_packet_sha256"] == restore.PLAIN
and all(body.get(k) is True for k in ("provider_publication_verified", "independent_readback_verified",
"decryption_verified", "session_cleanup_confirmed")), "successful_provider_receipt_required")
recovery = Path(body["recovery_directory"])
require(recovery.is_absolute() and recovery.parent == kit and recovery.name.startswith("provider-recovery-"),
"provider_recovery_directory_required")
private_directory(recovery)
require(digest(recovery / "recovery-point.zip") == restore.PLAIN
and digest(recovery / "fetched.zip.age") == restore.CIPHER, "provider_retrieved_packet_changed")
point = recovery / "point"
private_directory(point)
metadata = json.loads((kit / (export.NAME + ".transfer.json")).read_text())["packet"]
require(metadata["run_id"] == export.RUN_ID and metadata["image"] == export.IMAGE, "wrong_recovery_point")
for name in restore.FILES[:4]:
export.regular(point / name, os.geteuid())
require(metadata["files"][name] == {"bytes": (point / name).stat().st_size,
"sha256": digest(point / name)}, "provider_recovered_file_changed")
for name, (size, checksum) in export.EXPECTED.items():
require(metadata["files"][name] == {"bytes": size, "sha256": checksum}, "capture_hash_mismatch")
return kit, point, metadata["files"]
def main():
parser = argparse.ArgumentParser(description=__doc__)
modes = parser.add_mutually_exclusive_group()
modes.add_argument("--execute", action="store_true")
modes.add_argument("--cleanup")
parser.add_argument("--receipt", type=Path)
args = parser.parse_args()
nonce = args.cleanup or uuid.uuid4().hex[:12]
command = remote_command(nonce, bool(args.cleanup))
if not args.execute and not args.cleanup:
print(json.dumps({"status": "prepared", "run_id": export.RUN_ID, "target": "railiance01",
"fresh_namespace": "vergabe-restore-<nonce>", "fresh_claims": list(restore.CLAIMS.values()),
"application_changed": False, "next": "--execute", "sources": PINS}))
return 0
require(sys.stdin.isatty(), "founder_interactive_terminal_required")
if args.cleanup:
result = subprocess.run(command, check=False)
staged = subprocess.run(stage_command(nonce, "cleanup"), check=False)
return 0 if result.returncode == staged.returncode == 0 else 1
receipt_path = args.receipt or Path.home() / ".local/state/vergabe-pilot-recovery" / export.RUN_ID / PROVIDER_RECEIPT
kit, point, files = verify_input(receipt_path)
result = subprocess.run(stage_command(nonce, "create"), check=False)
require(result.returncode == 0, "restore_stage_creation_failed")
print(json.dumps({"status": "restore_transport_started", "nonce": nonce}), flush=True)
with tempfile.TemporaryDirectory(prefix="restore-transfer-", dir=kit) as temporary:
temporary = Path(temporary)
for name in restore.FILES[:4]:
shutil.copy2(point / name, temporary / name)
shutil.copy2(receipt_path, temporary / "provider.json")
(temporary / "origin.json").write_text(json.dumps({"run_id": export.RUN_ID, "files": files}))
(temporary / "origin.json").chmod(0o600)
for name in restore.FILES:
copied = subprocess.run(["scp", "-p", str(temporary / name),
"railiance01:vergabe-pilot-restore-" + nonce + "/" + name],
capture_output=True, timeout=120, check=False)
require(copied.returncode == 0, "restore_private_transfer_failed")
result = subprocess.run(command, check=False)
stage_removed = False
if result.returncode == 0:
stage_removed = subprocess.run(stage_command(nonce, "cleanup"), check=False).returncode == 0
metadata = {"schema": "railiance.vergabe-pilot-native-restore-transport.v1", "nonce": nonce,
"run_id": export.RUN_ID, "worker_exit_code": result.returncode,
"node_stage_removed": stage_removed,
"status": "complete" if result.returncode == 0 and stage_removed else "requires_review"}
output = kit / ("native-restore-transport-" + nonce + ".json")
fd = os.open(output, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "w") as stream:
json.dump(metadata, stream, indent=2)
print(json.dumps(metadata, indent=2))
return 0 if metadata["status"] == "complete" else 1
if __name__ == "__main__":
os.umask(0o077)
try:
raise SystemExit(main())
except Exception as error: # noqa: BLE001 — suppress private path/transport details
print(json.dumps({"status": "restore_transport_failed", "failure": str(error) if isinstance(error, Refused)
else "restore_transport_error", "details_suppressed": True}))
raise SystemExit(1)