Files
ai-contract-platform/yuqei-infra/scripts/run-object-storage-runtime-probe.py

324 lines
15 KiB
Python

#!/usr/bin/env python3
"""Run the fail-closed Linux RustFS/rc candidate runtime probe and sign its receipt."""
from __future__ import annotations
import argparse
import base64
import hashlib
import json
import os
import platform
import re
import stat
import subprocess
import time
from pathlib import Path, PurePosixPath
from typing import Any, Callable, Mapping, Sequence
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
SCHEMA = "yuqei.object-storage-runtime-probe.v1"
EXECUTOR = "yuqei-controlled-object-storage-runtime-probe-v1"
DOMAIN = b"yuqei-object-storage-runtime-probe-v1\x00"
DOCKER = "/usr/bin/docker"
DIGEST_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
SAFE_ENTRYPOINT_RE = re.compile(r"^/[A-Za-z0-9._/-]{1,255}$")
class ProbeError(RuntimeError):
pass
def canonical(value: object) -> bytes:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode()
def _read_json(path: Path) -> dict[str, Any]:
try:
value = json.loads(path.read_bytes())
except (OSError, ValueError) as exc:
raise ProbeError(f"invalid JSON input: {path}") from exc
if not isinstance(value, dict):
raise ProbeError(f"JSON input must be an object: {path}")
return value
def _under(root: Path, relative: object) -> Path:
if not isinstance(relative, str):
raise ProbeError("artifact path is invalid")
pure = PurePosixPath(relative)
if pure.is_absolute() or ".." in pure.parts:
raise ProbeError("artifact path escapes artifact root")
path = root.joinpath(*pure.parts).resolve(strict=True)
try:
path.relative_to(root)
except ValueError as exc:
raise ProbeError("artifact path escapes artifact root") from exc
return path
def _run_default(argv: Sequence[str], *, input_bytes: bytes | None = None) -> bytes:
completed = subprocess.run(
list(argv), input=input_bytes, capture_output=True, check=False, timeout=120
)
if completed.returncode != 0:
raise ProbeError(f"controlled Docker command failed: {argv[1]}")
return completed.stdout
def _inspect(run: Callable[..., bytes], reference: str) -> dict[str, Any]:
try:
values = json.loads(run((DOCKER, "image", "inspect", reference)))
except (ValueError, TypeError) as exc:
raise ProbeError("Docker image inspect returned invalid JSON") from exc
if not isinstance(values, list) or len(values) != 1 or not isinstance(values[0], dict):
raise ProbeError("Docker image inspect must return exactly one image")
return values[0]
def _image(source: Mapping[str, Any], role: str) -> Mapping[str, Any]:
images = source.get("images")
if not isinstance(images, list):
raise ProbeError("source set image inventory is missing")
matches = [item for item in images if isinstance(item, Mapping) and item.get("role") == role]
if len(matches) != 1:
raise ProbeError(f"source set must contain exactly one {role} image")
return matches[0]
def _load_key(path: Path) -> Ed25519PrivateKey:
if path.is_symlink():
raise ProbeError("builder signing key must not be a symbolic link")
metadata = path.stat()
if not stat.S_ISREG(metadata.st_mode) or metadata.st_nlink != 1:
raise ProbeError("builder signing key must be a single-link regular file")
if os.name == "posix" and metadata.st_mode & 0o077:
raise ProbeError("builder signing key permissions are not private")
raw = path.read_bytes()
try:
if raw.startswith(b"-----BEGIN"):
key = serialization.load_pem_private_key(raw, password=None)
if not isinstance(key, Ed25519PrivateKey):
raise TypeError
return key
return Ed25519PrivateKey.from_private_bytes(raw)
except (TypeError, ValueError) as exc:
raise ProbeError("builder signing key is not an Ed25519 private key") from exc
def _cleanup_created(
created: list[tuple[str, str]], run: Callable[..., bytes]
) -> None:
failures: list[str] = []
for kind, name in reversed(created):
command = (
(DOCKER, "rm", "--force", name)
if kind == "container"
else (DOCKER, kind, "rm", name)
)
try:
run(command)
except Exception:
failures.append(f"{kind}:{name}")
if failures:
raise ProbeError(
"object-storage probe cleanup failed: " + ", ".join(failures)
)
def execute(
*,
artifact_root: Path,
source_set_path: Path,
output_path: Path,
private_key_path: Path,
run: Callable[..., bytes] = _run_default,
require_host: bool = True,
) -> dict[str, Any]:
if require_host and (
os.name != "posix"
or platform.system() != "Linux"
or platform.machine().lower() not in {"x86_64", "amd64"}
or not Path(DOCKER).is_file()
):
raise ProbeError("probe requires controlled linux/amd64 with /usr/bin/docker")
root = artifact_root.resolve(strict=True)
source = _read_json(source_set_path.resolve(strict=True))
supplied = source.get("source_set_payload_sha256")
unsigned = dict(source)
unsigned.pop("source_set_payload_sha256", None)
if supplied != hashlib.sha256(canonical(unsigned)).hexdigest():
raise ProbeError("source set payload hash drifted")
candidate = source.get("candidate")
if not isinstance(candidate, dict) or set(candidate) != {"commit", "tag"}:
raise ProbeError("source set candidate binding is invalid")
records = [_image(source, "minio"), _image(source, "minio-client")]
inspected: dict[str, dict[str, Any]] = {}
for record in records:
archive_record = record.get("docker_archive")
if not isinstance(archive_record, Mapping):
raise ProbeError("object-storage archive record is missing")
archive = _under(root, archive_record.get("path"))
raw = archive.read_bytes()
if (
archive_record.get("sha256") != hashlib.sha256(raw).hexdigest()
or archive_record.get("size_bytes") != len(raw)
):
raise ProbeError("object-storage archive bytes drifted")
run((DOCKER, "image", "load", "--input", str(archive)))
reference = record.get("expected_repo_tag")
if not isinstance(reference, str):
raise ProbeError("loaded image reference is invalid")
details = _inspect(run, reference)
expected_config = record.get("config_digest")
expected_manifest = record.get("manifest_digest")
repo_digests = details.get("RepoDigests")
if (
not isinstance(expected_config, str)
or not DIGEST_RE.fullmatch(expected_config)
or details.get("Id") != expected_config
or not isinstance(expected_manifest, str)
or not DIGEST_RE.fullmatch(expected_manifest)
or not isinstance(repo_digests, list)
or not any(str(item).endswith("@" + expected_manifest) for item in repo_digests)
):
raise ProbeError(f"loaded image identity drifted: {record['role']}")
inspected[str(record["role"])] = details
suffix = str(candidate["commit"])[:12]
network = f"yuqei-os-probe-{suffix}"
server = network + "-server"
client = network + "-client"
volume = network + "-data"
created: list[tuple[str, str]] = []
cleanup_attempted = False
server_record, client_record = records
try:
run((DOCKER, "network", "create", "--internal", network)); created.append(("network", network))
run((DOCKER, "volume", "create", volume)); created.append(("volume", volume))
run((
DOCKER, "create", "--pull", "never", "--name", server, "--network", network, "--read-only",
"--tmpfs", "/tmp:rw,nosuid,nodev", "--mount", f"type=volume,source={volume},target=/data",
"--security-opt", "no-new-privileges:true", "-e", "MINIO_ROOT_USER=yuqei_probe",
"-e", "MINIO_ROOT_PASSWORD=yuqei_probe_password_123",
"--health-cmd", "test -f /data/.minio.sys/format.json",
"--health-interval", "2s", "--health-timeout", "2s",
"--health-retries", "30", "--health-start-period", "0s",
str(server_record["expected_repo_tag"]),
"server", "/data", "--console-address", ":9001",
)); created.append(("container", server))
run((DOCKER, "start", server))
server_state = None
for _attempt in range(30):
candidate_state = json.loads(
run((DOCKER, "container", "inspect", server))
)[0]
health = candidate_state.get("State", {}).get("Health", {}).get("Status")
if health == "healthy":
server_state = candidate_state
break
if health == "unhealthy" or candidate_state.get("State", {}).get("Running") is not True:
raise ProbeError("object-storage server failed its Docker healthcheck")
time.sleep(2)
if server_state is None:
raise ProbeError("object-storage server Docker healthcheck timed out")
server_ids = run((DOCKER, "exec", server, "/bin/sh", "-ceu", "id -u; stat -c %u /data; test -w /data; touch /data/.yuqei-probe; test -f /data/.minio.sys/format.json")).decode().splitlines()
uid, owner = (int(server_ids[0]), int(server_ids[1]))
client_config = (
f"http://{server}:9000\nyuqei_probe\nyuqei_probe_password_123\n"
).encode("utf-8")
entrypoint = ((inspected["minio-client"].get("Config") or {}).get("Entrypoint") or [None])[0]
if not isinstance(entrypoint, str) or not SAFE_ENTRYPOINT_RE.fullmatch(entrypoint):
raise ProbeError("rc image entrypoint is not a safe absolute executable")
client_script = (
"set -eu; IFS= read -r probe_endpoint; IFS= read -r probe_access; "
"IFS= read -r probe_secret; test -n \"$probe_endpoint\"; "
"test -n \"$probe_access\"; test -n \"$probe_secret\"; "
f"test -x {entrypoint}; {entrypoint} --version >/tmp/version; "
f"{entrypoint} alias set probe \"$probe_endpoint\" \"$probe_access\" \"$probe_secret\"; "
f"mkdir -p /tmp/src; printf candidate-bound >/tmp/src/marker; {entrypoint} mb --ignore-existing probe/probe; "
f"{entrypoint} mirror /tmp/src probe/probe; {entrypoint} cp probe/probe/marker /tmp/roundtrip; "
"test \"$(cat /tmp/roundtrip)\" = candidate-bound; test -r /probe-data/.yuqei-probe; "
"test ! -w /probe-data; id -u"
)
created.append(("container", client))
client_out = run((
DOCKER, "run", "--pull", "never", "--name", client, "--network", network, "--read-only",
"--tmpfs", "/tmp:rw,nosuid,nodev", "--security-opt", "no-new-privileges:true",
"--mount", f"type=volume,source={volume},target=/probe-data,readonly",
"-e", "MC_CONFIG_DIR=/tmp/.mc", "--entrypoint", "/bin/sh", "-i",
str(client_record["expected_repo_tag"]), "-ceu", client_script,
), input_bytes=client_config)
client_uid = int(client_out.decode().splitlines()[-1])
if uid <= 0 or owner != uid or client_uid <= 0 or server_state.get("State", {}).get("Running") is not True:
raise ProbeError("object-storage runtime identity or health checks failed")
payload = {
"candidate": dict(candidate),
"controls": {"network_mode": "isolated", "real_container_runtime": True, "synthetic": False},
"executor": EXECUTOR,
"images": [
{"role": "minio", "manifest_digest": server_record["manifest_digest"], "config_digest": server_record["config_digest"], "archive_sha256": server_record["docker_archive"]["sha256"], "checks": {"bin_sh_executable": True, "container_started": True, "data_volume_owner_uid": owner, "data_volume_writable": True, "effective_uid": uid, "entrypoint_invoked": True, "healthcheck_passed": True, "read_only_rootfs": True, "tmpfs_paths": ["/tmp"]}},
{"role": "minio-client", "manifest_digest": client_record["manifest_digest"], "config_digest": client_record["config_digest"], "archive_sha256": client_record["docker_archive"]["sha256"], "checks": {"bin_sh_executable": True, "config_from_stdin": True, "container_started": True, "effective_uid": client_uid, "entrypoint_invoked": True, "mirror_roundtrip_verified": True, "rc_config_uses_tmpfs": True, "rc_executable": True, "read_only_rootfs": True, "tmpfs_paths": ["/tmp"], "volume_permissions_verified": True}},
],
"platform": "linux/amd64",
}
cleanup_attempted = True
_cleanup_created(created, run)
created.clear()
signature = _load_key(private_key_path.absolute()).sign(DOMAIN + canonical(payload))
envelope = {"schema_version": SCHEMA, "payload": payload, "signature_algorithm": "Ed25519", "signature_base64": base64.b64encode(signature).decode("ascii")}
parent = output_path.parent.resolve(strict=True)
target = parent / output_path.name
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
try:
descriptor = os.open(target, flags, 0o600)
with os.fdopen(descriptor, "wb") as stream:
metadata = os.fstat(stream.fileno())
if (
not stat.S_ISREG(metadata.st_mode)
or metadata.st_nlink != 1
or (os.name == "posix" and metadata.st_mode & 0o077)
):
raise ProbeError(
"probe receipt output is not a private single-link regular file"
)
stream.write(canonical(envelope) + b"\n")
stream.flush()
os.fsync(stream.fileno())
if os.name == "posix":
directory_flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0)
directory_descriptor = os.open(parent, directory_flags)
try:
os.fsync(directory_descriptor)
finally:
os.close(directory_descriptor)
except OSError as exc:
raise ProbeError("probe receipt output must be a new private regular file") from exc
return envelope
finally:
if not cleanup_attempted and created:
_cleanup_created(created, run)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--artifact-root", type=Path, required=True)
parser.add_argument("--source-set", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--builder-private-key", type=Path, required=True)
args = parser.parse_args()
try:
execute(artifact_root=args.artifact_root, source_set_path=args.source_set, output_path=args.output, private_key_path=args.builder_private_key)
except ProbeError as exc:
print(f"FAILED_CLOSED: {exc}", file=os.sys.stderr)
return 2
return 0
if __name__ == "__main__":
raise SystemExit(main())