1187 lines
44 KiB
Python
1187 lines
44 KiB
Python
#!/usr/bin/env python3
|
|
"""Publish only the frozen MCP Hub connector-ingestion image by digest."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
FIXED_REPOSITORY = "jens/itworx_mcp_hub"
|
|
FIXED_IMAGE_REPOSITORY = "gitea.example.com/jens/itworx-mcp-hub/connector-ingestion"
|
|
FIXED_SOURCE_URI = "https://gitea.example.com/jens/itworx-mcp-hub"
|
|
FIXED_SERVICE = "connector-ingestion"
|
|
FIXED_CONTAINER = "gitea-deploy-control"
|
|
FIXED_DOCKER_BINARY = "/usr/bin/docker"
|
|
FIXED_DOCKER_CONTEXT = "default"
|
|
FIXED_DOCKER_SOCKET = "unix:///var/run/docker.sock"
|
|
FIXED_DOCKER_ROOT = "/var/lib/docker"
|
|
FIXED_DOCKER_HOST_NAME = "Tower"
|
|
FIXED_DOCKER_VERSION = "27.5.1"
|
|
FIXED_BUILDX_VERSION = "v0.20.1"
|
|
FIXED_BUILDER_NAME = "itworx-mcp-hub-ingestion"
|
|
FIXED_BUILDKIT_IMAGE = (
|
|
"moby/buildkit:v0.33.0@sha256:6c2fa84a6b61ccd72899dde4239f8d5717f05f9a8ca6f3cad185fb1a95a94de3"
|
|
)
|
|
FIXED_BUILDKIT_CONTAINER = "buildx_buildkit_itworx-mcp-hub-ingestion0"
|
|
FIXED_BUILDKIT_CONFIG_PATH = Path("/root/.docker/buildx/buildkitd.default.toml")
|
|
FIXED_BUILDKIT_ENTRYPOINT = ["/usr/bin/buildkitd-entrypoint"]
|
|
FIXED_BUILDKIT_COMMAND = ["--allow-insecure-entitlement=network.host"]
|
|
FIXED_BUILDKIT_STATE_VOLUME = f"{FIXED_BUILDKIT_CONTAINER}_state"
|
|
FIXED_BUILDKIT_STATE_PATH = "/var/lib/buildkit"
|
|
FIXED_VOLUME_NAME = "itworx-controller-hub-releases"
|
|
FIXED_VOLUME_ROOT = Path("/var/lib/docker/volumes/itworx-controller-hub-releases/_data")
|
|
FIXED_RELEASES_ROOT = FIXED_VOLUME_ROOT / "releases"
|
|
FIXED_STATE_ROOT = Path("/mnt/cache/appdata/gitea-deploy-runners/state")
|
|
ATTESTATION_SCHEMA = "itworx.mcp-hub-ingestion-freeze-attestation/v1"
|
|
RECEIPT_SCHEMA = "itworx.mcp-hub-ingestion-publication/v1"
|
|
MAX_METADATA_BYTES = 1024 * 1024
|
|
COMMAND_TIMEOUT_SECONDS = 30
|
|
BUILDER_BOOTSTRAP_TIMEOUT_SECONDS = 300
|
|
BUILD_TIMEOUT_SECONDS = 1800
|
|
|
|
OCI_LABELS = (
|
|
("org.opencontainers.image.source", FIXED_SOURCE_URI),
|
|
("org.opencontainers.image.revision", "revision"),
|
|
("org.opencontainers.image.source-tree", "tree"),
|
|
("org.opencontainers.image.version", "id"),
|
|
("org.opencontainers.image.created", "builtAt"),
|
|
("org.opencontainers.image.source-date-epoch", "epoch"),
|
|
)
|
|
ALLOWED_MANIFEST_MEDIA_TYPES = {
|
|
"application/vnd.docker.distribution.manifest.v2+json",
|
|
"application/vnd.oci.image.manifest.v1+json",
|
|
}
|
|
EXPECTED_VOLUME_LABELS = {
|
|
"itworx.managed-by": "deployment-controller",
|
|
"itworx.purpose": "mcp-hub-protected-release",
|
|
}
|
|
DOCKER_INFO_FORMAT = "{{.ID}}|{{.DockerRootDir}}|{{.ServerVersion}}|{{.Name}}"
|
|
IMAGE_INSPECT_FORMAT = "|".join(
|
|
[
|
|
"{{.Id}}",
|
|
"{{.Config.User}}",
|
|
*(f'{{{{index .Config.Labels "{name}"}}}}' for name, _ in OCI_LABELS),
|
|
]
|
|
)
|
|
PINNED_BUILDKIT_IMAGE_FIELDS = (
|
|
"Id",
|
|
"Config.Env",
|
|
"Config.Entrypoint",
|
|
"Config.User",
|
|
"Config.WorkingDir",
|
|
"Config.Labels",
|
|
)
|
|
PINNED_BUILDKIT_IMAGE_FORMAT = "\n".join(
|
|
f"{{{{json .{field}}}}}" for field in PINNED_BUILDKIT_IMAGE_FIELDS
|
|
)
|
|
BUILDKIT_CONTAINER_FIELDS = (
|
|
"Image",
|
|
"Config.Image",
|
|
"Config.Env",
|
|
"Config.Entrypoint",
|
|
"Config.Cmd",
|
|
"Config.User",
|
|
"Config.WorkingDir",
|
|
"Config.Labels",
|
|
"State.Running",
|
|
"HostConfig.Privileged",
|
|
"HostConfig.Init",
|
|
"HostConfig.RestartPolicy.Name",
|
|
"HostConfig.RestartPolicy.MaximumRetryCount",
|
|
"HostConfig.NetworkMode",
|
|
"HostConfig.UsernsMode",
|
|
"HostConfig.CgroupParent",
|
|
"HostConfig.CgroupnsMode",
|
|
"HostConfig.Mounts",
|
|
"Mounts",
|
|
"HostConfig.Binds",
|
|
"HostConfig.Devices",
|
|
"HostConfig.DeviceRequests",
|
|
"HostConfig.CapAdd",
|
|
"HostConfig.CapDrop",
|
|
"HostConfig.SecurityOpt",
|
|
"HostConfig.PortBindings",
|
|
"HostConfig.PublishAllPorts",
|
|
"HostConfig.ReadonlyRootfs",
|
|
"HostConfig.PidMode",
|
|
"HostConfig.IpcMode",
|
|
"HostConfig.UTSMode",
|
|
"HostConfig.AutoRemove",
|
|
"HostConfig.Runtime",
|
|
"HostConfig.Memory",
|
|
"HostConfig.MemorySwap",
|
|
"HostConfig.CpuShares",
|
|
"HostConfig.CpuQuota",
|
|
"HostConfig.CpuPeriod",
|
|
"HostConfig.CpusetCpus",
|
|
"HostConfig.CpusetMems",
|
|
"HostConfig.OomKillDisable",
|
|
)
|
|
BUILDKIT_CONTAINER_FORMAT = "\n".join(
|
|
f"{{{{json .{field}}}}}" for field in BUILDKIT_CONTAINER_FIELDS
|
|
)
|
|
|
|
|
|
class PublishError(RuntimeError):
|
|
"""A fixed publication precondition or attestation failed."""
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FrozenSource:
|
|
repository: str
|
|
revision: str
|
|
build: dict[str, str]
|
|
source_date_epoch: str
|
|
freeze_sha256: str
|
|
controller_source_path: str
|
|
|
|
|
|
Runner = Callable[..., subprocess.CompletedProcess[str]]
|
|
|
|
|
|
_CONTROLLER_ATTESTATION_PROGRAM = r"""
|
|
import hashlib, json, sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, "/opt/gitea-deploy")
|
|
import deploy as controller
|
|
import prepare_mcp_hub_candidate as candidate
|
|
|
|
repository = "jens/itworx_mcp_hub"
|
|
revision = sys.argv[1]
|
|
project, repo = candidate._fixed_target(repository, revision)
|
|
record = candidate._load_freeze_record(revision)
|
|
build, epoch = candidate._validate_freeze_record(record, repository, revision, repo)
|
|
source = controller.MCP_HUB_RELEASES_ROOT / revision
|
|
if not controller.exact_source_matches_revision(repo, revision, source):
|
|
raise RuntimeError("staged source mismatch")
|
|
controller._mcp_hub_verify_release_tree_metadata(source)
|
|
controller._mcp_hub_require_trusted_state_root()
|
|
freeze_path = candidate._freeze_directory(revision, create=False) / candidate.FREEZE_FILENAME
|
|
raw = freeze_path.read_bytes()
|
|
if json.loads(raw) != record:
|
|
raise RuntimeError("freeze changed")
|
|
result = {
|
|
"schema": "itworx.mcp-hub-ingestion-freeze-attestation/v1",
|
|
"repository": repository,
|
|
"revision": revision,
|
|
"build": build,
|
|
"sourceDateEpoch": epoch,
|
|
"freezeSha256": hashlib.sha256(raw).hexdigest(),
|
|
"sourcePath": source.as_posix(),
|
|
}
|
|
print(json.dumps(result, sort_keys=True, separators=(",", ":")))
|
|
"""
|
|
|
|
|
|
def _docker(*arguments: str) -> list[str]:
|
|
return [FIXED_DOCKER_BINARY, "--context", FIXED_DOCKER_CONTEXT, *arguments]
|
|
|
|
|
|
def _run(
|
|
runner: Runner,
|
|
command: list[str],
|
|
*,
|
|
cwd: Path | None = None,
|
|
timeout: int = COMMAND_TIMEOUT_SECONDS,
|
|
stage: str,
|
|
) -> str:
|
|
try:
|
|
result = runner(command, cwd=cwd, timeout=timeout)
|
|
except (OSError, subprocess.SubprocessError):
|
|
raise PublishError(f"{stage} command failed") from None
|
|
if result.returncode != 0 or not isinstance(result.stdout, str):
|
|
raise PublishError(f"{stage} command failed")
|
|
return result.stdout.strip()
|
|
|
|
|
|
def _docker_runner(
|
|
command: list[str], *, cwd: Path | None, timeout: int
|
|
) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run( # noqa: S603 -- command is fixed by this module; callers inject only the test runner.
|
|
command,
|
|
cwd=cwd,
|
|
timeout=timeout,
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
errors="replace",
|
|
)
|
|
|
|
|
|
def _validate_revision(revision: str) -> None:
|
|
if re.fullmatch(r"[0-9a-f]{40}", revision) is None:
|
|
raise PublishError("revision must be a full lowercase Git SHA")
|
|
|
|
|
|
def _validate_digest(value: object) -> str:
|
|
digest = str(value or "")
|
|
if re.fullmatch(r"sha256:[0-9a-f]{64}", digest) is None:
|
|
raise PublishError("image digest is invalid")
|
|
return digest
|
|
|
|
|
|
def _validate_frozen_attestation(document: object, revision: str) -> FrozenSource:
|
|
_validate_revision(revision)
|
|
required = {
|
|
"schema",
|
|
"repository",
|
|
"revision",
|
|
"build",
|
|
"sourceDateEpoch",
|
|
"freezeSha256",
|
|
"sourcePath",
|
|
}
|
|
if not isinstance(document, dict) or set(document) != required:
|
|
raise PublishError("official source-freeze attestation has an invalid shape")
|
|
build = document.get("build")
|
|
build_fields = {"id", "repository", "revision", "tree", "treeDigest", "builtAt"}
|
|
if (
|
|
document.get("schema") != ATTESTATION_SCHEMA
|
|
or document.get("repository") != FIXED_REPOSITORY
|
|
or document.get("revision") != revision
|
|
or not isinstance(build, dict)
|
|
or set(build) != build_fields
|
|
or any(not isinstance(value, str) for value in build.values())
|
|
):
|
|
raise PublishError("official source-freeze attestation does not match the fixed target")
|
|
tree = build["tree"]
|
|
epoch = document.get("sourceDateEpoch")
|
|
if re.fullmatch(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z", build["builtAt"]) is None:
|
|
raise PublishError("official source-freeze build time is invalid") from None
|
|
try:
|
|
datetime.strptime(build["builtAt"], "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
except ValueError:
|
|
raise PublishError("official source-freeze build time is invalid") from None
|
|
if (
|
|
build["id"] != revision[:12]
|
|
or build["repository"] != FIXED_REPOSITORY
|
|
or build["revision"] != revision
|
|
or re.fullmatch(r"[0-9a-f]{40}", tree) is None
|
|
or build["treeDigest"] != hashlib.sha256(f"git-tree:{tree}".encode("ascii")).hexdigest()
|
|
or not isinstance(epoch, str)
|
|
or re.fullmatch(r"[1-9][0-9]{0,11}", epoch) is None
|
|
or re.fullmatch(r"[0-9a-f]{64}", str(document.get("freezeSha256") or "")) is None
|
|
or document.get("sourcePath") != f"/controller-hub-releases/releases/{revision}"
|
|
):
|
|
raise PublishError("official source-freeze metadata is invalid")
|
|
return FrozenSource(
|
|
repository=FIXED_REPOSITORY,
|
|
revision=revision,
|
|
build=dict(build),
|
|
source_date_epoch=epoch,
|
|
freeze_sha256=document["freezeSha256"],
|
|
controller_source_path=document["sourcePath"],
|
|
)
|
|
|
|
|
|
def _attest_official_freeze(revision: str, runner: Runner) -> FrozenSource:
|
|
_validate_revision(revision)
|
|
command = _docker(
|
|
"exec",
|
|
"--user",
|
|
"0",
|
|
FIXED_CONTAINER,
|
|
"python3",
|
|
"-c",
|
|
_CONTROLLER_ATTESTATION_PROGRAM,
|
|
revision,
|
|
)
|
|
raw = _run(runner, command, stage="official source-freeze attestation")
|
|
if len(raw) > 16 * 1024:
|
|
raise PublishError("official source-freeze attestation is oversized")
|
|
try:
|
|
document = json.loads(raw)
|
|
except (ValueError, TypeError):
|
|
raise PublishError("official source-freeze attestation is invalid") from None
|
|
return _validate_frozen_attestation(document, revision)
|
|
|
|
|
|
def _parse_engine_identity(value: str, *, source: str) -> tuple[str, str, str, str]:
|
|
parts = value.split("|")
|
|
if (
|
|
len(parts) != 4
|
|
or re.fullmatch(r"[A-Za-z0-9:_-]{16,128}", parts[0]) is None
|
|
or parts[1] != FIXED_DOCKER_ROOT
|
|
or parts[2] != FIXED_DOCKER_VERSION
|
|
or parts[3] != FIXED_DOCKER_HOST_NAME
|
|
):
|
|
raise PublishError(f"{source} Docker engine identity is not the fixed Tower engine")
|
|
return parts[0], parts[1], parts[2], parts[3]
|
|
|
|
|
|
def _preflight_engine(runner: Runner) -> tuple[str, str]:
|
|
selected = _run(runner, [FIXED_DOCKER_BINARY, "context", "show"], stage="Docker context")
|
|
endpoint = _run(
|
|
runner,
|
|
[
|
|
FIXED_DOCKER_BINARY,
|
|
"context",
|
|
"inspect",
|
|
FIXED_DOCKER_CONTEXT,
|
|
"--format",
|
|
'{{(index .Endpoints "docker").Host}}',
|
|
],
|
|
stage="Docker endpoint inspection",
|
|
)
|
|
if selected != FIXED_DOCKER_CONTEXT or endpoint != FIXED_DOCKER_SOCKET:
|
|
raise PublishError("Docker CLI is not bound to the fixed local Tower socket")
|
|
host = _parse_engine_identity(
|
|
_run(
|
|
runner,
|
|
_docker("info", "--format", DOCKER_INFO_FORMAT),
|
|
stage="host Docker identity inspection",
|
|
),
|
|
source="host",
|
|
)
|
|
controller = _parse_engine_identity(
|
|
_run(
|
|
runner,
|
|
_docker(
|
|
"exec",
|
|
"--user",
|
|
"0",
|
|
FIXED_CONTAINER,
|
|
"docker",
|
|
"--host",
|
|
FIXED_DOCKER_SOCKET,
|
|
"info",
|
|
"--format",
|
|
DOCKER_INFO_FORMAT,
|
|
),
|
|
stage="controller Docker identity inspection",
|
|
),
|
|
source="controller-socket",
|
|
)
|
|
if host != controller:
|
|
raise PublishError("host and controller Docker socket identities differ")
|
|
inspected = _run(
|
|
runner,
|
|
_docker("inspect", "--format", "{{.Id}}|{{.State.Running}}", FIXED_CONTAINER),
|
|
stage="controller container inspection",
|
|
).split("|")
|
|
if (
|
|
len(inspected) != 2
|
|
or re.fullmatch(r"[0-9a-f]{64}", inspected[0]) is None
|
|
or inspected[1] != "true"
|
|
):
|
|
raise PublishError("fixed deployment controller is not running")
|
|
buildx_version = _run(runner, _docker("buildx", "version"), stage="Buildx availability")
|
|
if re.search(rf"(?:^|\s){re.escape(FIXED_BUILDX_VERSION)}(?:\s|$)", buildx_version) is None:
|
|
raise PublishError("Buildx version differs from the fixed Tower toolchain")
|
|
return host[0], inspected[0]
|
|
|
|
|
|
def _builder_rows(runner: Runner) -> set[tuple[str, str]]:
|
|
raw = _run(
|
|
runner,
|
|
_docker("buildx", "ls", "--format", "{{.Name}}|{{.DriverEndpoint}}"),
|
|
stage="buildx builder inventory",
|
|
)
|
|
rows: set[tuple[str, str]] = set()
|
|
for line in raw.splitlines():
|
|
parts = line.strip().split("|", 1)
|
|
if len(parts) != 2 or not parts[0] or not parts[1]:
|
|
raise PublishError("buildx builder inventory is invalid")
|
|
rows.add((parts[0], parts[1]))
|
|
return rows
|
|
|
|
|
|
def _json_inspection_record(raw: str, fields: tuple[str, ...], *, stage: str) -> dict[str, Any]:
|
|
lines = raw.splitlines()
|
|
if len(lines) != len(fields):
|
|
raise PublishError(f"{stage} returned an invalid fixed record")
|
|
try:
|
|
values = [json.loads(line) for line in lines]
|
|
except (ValueError, TypeError):
|
|
raise PublishError(f"{stage} returned an invalid fixed record") from None
|
|
return dict(zip(fields, values, strict=True))
|
|
|
|
|
|
def _empty_host_config(value: Any) -> bool:
|
|
return value in (None, [], {}, "")
|
|
|
|
|
|
def _mount_extras_are_empty(mount: dict[str, Any], allowed: set[str]) -> bool:
|
|
return all(key in allowed or value in (None, "", False, [], {}) for key, value in mount.items())
|
|
|
|
|
|
def _verify_dedicated_builder_container(runner: Runner, *, require_running: bool) -> None:
|
|
image_raw = _run(
|
|
runner,
|
|
_docker(
|
|
"image",
|
|
"inspect",
|
|
"--format",
|
|
PINNED_BUILDKIT_IMAGE_FORMAT,
|
|
FIXED_BUILDKIT_IMAGE,
|
|
),
|
|
stage="pinned BuildKit image configuration inspection",
|
|
)
|
|
image = _json_inspection_record(
|
|
image_raw,
|
|
PINNED_BUILDKIT_IMAGE_FIELDS,
|
|
stage="pinned BuildKit image configuration inspection",
|
|
)
|
|
image_id = image["Id"]
|
|
image_env = image["Config.Env"]
|
|
image_entrypoint = image["Config.Entrypoint"]
|
|
image_user = image["Config.User"]
|
|
image_working_dir = image["Config.WorkingDir"]
|
|
image_labels = image["Config.Labels"]
|
|
if (
|
|
not isinstance(image_id, str)
|
|
or re.fullmatch(r"sha256:[0-9a-f]{64}", image_id) is None
|
|
or image_entrypoint != FIXED_BUILDKIT_ENTRYPOINT
|
|
or not isinstance(image_env, list)
|
|
or not all(isinstance(item, str) for item in image_env)
|
|
or image_user is not None
|
|
and not isinstance(image_user, str)
|
|
or image_working_dir is not None
|
|
and not isinstance(image_working_dir, str)
|
|
or image_labels is not None
|
|
and not isinstance(image_labels, dict)
|
|
):
|
|
raise PublishError("pinned BuildKit image configuration is outside its fixed contract")
|
|
|
|
container_raw = _run(
|
|
runner,
|
|
_docker(
|
|
"inspect",
|
|
"--format",
|
|
BUILDKIT_CONTAINER_FORMAT,
|
|
FIXED_BUILDKIT_CONTAINER,
|
|
),
|
|
stage="dedicated BuildKit container inspection",
|
|
)
|
|
container = _json_inspection_record(
|
|
container_raw,
|
|
BUILDKIT_CONTAINER_FIELDS,
|
|
stage="dedicated BuildKit container inspection",
|
|
)
|
|
config_matches_image = all(
|
|
container[container_field] == image[image_field]
|
|
for container_field, image_field in (
|
|
("Config.Env", "Config.Env"),
|
|
("Config.Entrypoint", "Config.Entrypoint"),
|
|
("Config.User", "Config.User"),
|
|
("Config.WorkingDir", "Config.WorkingDir"),
|
|
("Config.Labels", "Config.Labels"),
|
|
)
|
|
)
|
|
mounts = container["HostConfig.Mounts"]
|
|
actual_mounts = container["Mounts"]
|
|
expected_host_mount = {
|
|
"Type": "volume",
|
|
"Source": FIXED_BUILDKIT_STATE_VOLUME,
|
|
"Target": FIXED_BUILDKIT_STATE_PATH,
|
|
}
|
|
expected_actual_mount = {
|
|
"Type": "volume",
|
|
"Name": FIXED_BUILDKIT_STATE_VOLUME,
|
|
"Source": f"{FIXED_DOCKER_ROOT}/volumes/{FIXED_BUILDKIT_STATE_VOLUME}/_data",
|
|
"Destination": FIXED_BUILDKIT_STATE_PATH,
|
|
"Driver": "local",
|
|
"Mode": "",
|
|
"RW": True,
|
|
"Propagation": "",
|
|
}
|
|
mounts_match = (
|
|
isinstance(mounts, list)
|
|
and len(mounts) == 1
|
|
and isinstance(mounts[0], dict)
|
|
and all(mounts[0].get(key) == value for key, value in expected_host_mount.items())
|
|
# Docker's Mount.ReadOnly is tagged json:",omitempty". Buildx leaves it
|
|
# unset for the writable state volume, so the daemon omits the key.
|
|
# Accept only omission or the explicit JSON boolean false; reject null,
|
|
# numbers, strings, and true rather than treating them as equivalent.
|
|
and ("ReadOnly" not in mounts[0] or mounts[0]["ReadOnly"] is False)
|
|
and _mount_extras_are_empty(mounts[0], set(expected_host_mount) | {"ReadOnly"})
|
|
and isinstance(actual_mounts, list)
|
|
and len(actual_mounts) == 1
|
|
and isinstance(actual_mounts[0], dict)
|
|
and all(actual_mounts[0].get(key) == value for key, value in expected_actual_mount.items())
|
|
and _mount_extras_are_empty(actual_mounts[0], set(expected_actual_mount))
|
|
)
|
|
empty_config_fields = (
|
|
"HostConfig.Binds",
|
|
"HostConfig.Devices",
|
|
"HostConfig.DeviceRequests",
|
|
"HostConfig.CapAdd",
|
|
"HostConfig.CapDrop",
|
|
"HostConfig.SecurityOpt",
|
|
"HostConfig.PortBindings",
|
|
)
|
|
no_extra_host_config = all(
|
|
_empty_host_config(container[field]) for field in empty_config_fields
|
|
)
|
|
zero_resource_limits = all(
|
|
container[field] in (None, 0, "")
|
|
for field in (
|
|
"HostConfig.Memory",
|
|
"HostConfig.MemorySwap",
|
|
"HostConfig.CpuShares",
|
|
"HostConfig.CpuQuota",
|
|
"HostConfig.CpuPeriod",
|
|
"HostConfig.CpusetCpus",
|
|
"HostConfig.CpusetMems",
|
|
)
|
|
)
|
|
if (
|
|
container["Image"] != image_id
|
|
or container["Config.Image"] != FIXED_BUILDKIT_IMAGE
|
|
or not config_matches_image
|
|
or container["Config.Cmd"] != FIXED_BUILDKIT_COMMAND
|
|
or not mounts_match
|
|
or container["HostConfig.Privileged"] is not True
|
|
or container["HostConfig.Init"] is not True
|
|
or container["HostConfig.RestartPolicy.Name"] != "unless-stopped"
|
|
or container["HostConfig.RestartPolicy.MaximumRetryCount"] != 0
|
|
or container["HostConfig.NetworkMode"] != "default"
|
|
or container["HostConfig.UsernsMode"] not in (None, "")
|
|
or container["HostConfig.CgroupParent"] != "/docker/buildx"
|
|
or container["HostConfig.CgroupnsMode"] != "private"
|
|
or not no_extra_host_config
|
|
or container["HostConfig.PublishAllPorts"] is not False
|
|
or container["HostConfig.ReadonlyRootfs"] is not False
|
|
or any(
|
|
container[field] not in (None, "")
|
|
for field in (
|
|
"HostConfig.PidMode",
|
|
"HostConfig.UTSMode",
|
|
)
|
|
)
|
|
or container["HostConfig.IpcMode"] != "private"
|
|
or container["HostConfig.Runtime"] != "runc"
|
|
or container["HostConfig.AutoRemove"] is not False
|
|
or not zero_resource_limits
|
|
or container["HostConfig.OomKillDisable"] not in (None, False)
|
|
or (require_running and container["State.Running"] is not True)
|
|
):
|
|
raise PublishError("dedicated BuildKit container runtime configuration is unsafe")
|
|
|
|
|
|
def _verify_buildkit_default_config_absent() -> None:
|
|
path = FIXED_BUILDKIT_CONFIG_PATH
|
|
current = Path(path.anchor)
|
|
for part in path.parts[1:]:
|
|
current = current / part
|
|
try:
|
|
metadata = current.lstat()
|
|
except FileNotFoundError:
|
|
continue
|
|
except OSError:
|
|
raise PublishError("default BuildKit config path cannot be verified") from None
|
|
if stat.S_ISLNK(metadata.st_mode):
|
|
raise PublishError("default BuildKit config path contains a symlink")
|
|
try:
|
|
path.lstat()
|
|
except FileNotFoundError:
|
|
return
|
|
except OSError:
|
|
raise PublishError("default BuildKit config path cannot be verified") from None
|
|
raise PublishError("default BuildKit config would alter the pinned daemon command")
|
|
|
|
|
|
def _inspect_dedicated_builder(
|
|
runner: Runner, *, require_running: bool, validate_container: bool = False
|
|
) -> None:
|
|
rows = _builder_rows(runner)
|
|
expected_rows = {
|
|
(FIXED_BUILDER_NAME, "docker-container"),
|
|
(f"{FIXED_BUILDER_NAME}0", FIXED_DOCKER_SOCKET),
|
|
}
|
|
own_rows = {row for row in rows if row[0].startswith(FIXED_BUILDER_NAME)}
|
|
if own_rows != expected_rows:
|
|
raise PublishError("dedicated BuildKit builder does not match its fixed local contract")
|
|
details = _run(
|
|
runner,
|
|
_docker("buildx", "inspect", FIXED_BUILDER_NAME),
|
|
stage="dedicated builder inspection",
|
|
)
|
|
names = re.findall(r"(?m)^Name:\s*(\S+)\s*$", details)
|
|
options = re.search(r"(?m)^Driver Options:\s*(.*?)\s*$", details)
|
|
if (
|
|
names != [FIXED_BUILDER_NAME, f"{FIXED_BUILDER_NAME}0"]
|
|
or not re.search(r"(?m)^Driver:\s+docker-container\s*$", details)
|
|
or not re.search(rf"(?m)^Endpoint:\s+{re.escape(FIXED_DOCKER_SOCKET)}\s*$", details)
|
|
or options is None
|
|
or options.group(1) != f'image="{FIXED_BUILDKIT_IMAGE}"'
|
|
):
|
|
raise PublishError("dedicated BuildKit builder is not bound to the fixed local engine")
|
|
if require_running:
|
|
version_lines = re.findall(r"(?m)^BuildKit(?: version)?:\s*(\S+)\s*$", details)
|
|
if version_lines != ["v0.33.0"] or not re.search(r"(?m)^Status:\s+running\s*$", details):
|
|
raise PublishError("dedicated BuildKit version differs from the pinned release")
|
|
if validate_container or require_running:
|
|
_verify_dedicated_builder_container(runner, require_running=require_running)
|
|
|
|
|
|
def _ensure_dedicated_builder(runner: Runner, frozen: FrozenSource) -> None:
|
|
checked_frozen = _validate_frozen_attestation(
|
|
{
|
|
"schema": ATTESTATION_SCHEMA,
|
|
"repository": frozen.repository,
|
|
"revision": frozen.revision,
|
|
"build": frozen.build,
|
|
"sourceDateEpoch": frozen.source_date_epoch,
|
|
"freezeSha256": frozen.freeze_sha256,
|
|
"sourcePath": frozen.controller_source_path,
|
|
},
|
|
frozen.revision,
|
|
)
|
|
if checked_frozen != frozen:
|
|
raise PublishError("a validated source freeze is required before builder setup")
|
|
_verify_buildkit_default_config_absent()
|
|
rows = _builder_rows(runner)
|
|
builder_registered = any(
|
|
name in {FIXED_BUILDER_NAME, f"{FIXED_BUILDER_NAME}0"} for name, _ in rows
|
|
)
|
|
containers = _run(
|
|
runner,
|
|
_docker("container", "ls", "--all", "--format", "{{.Names}}"),
|
|
stage="dedicated builder container inventory",
|
|
).splitlines()
|
|
has_reserved_container = FIXED_BUILDKIT_CONTAINER in containers
|
|
if not builder_registered and has_reserved_container:
|
|
raise PublishError("reserved BuildKit container name is occupied without its builder")
|
|
if builder_registered:
|
|
# Validate the configured pin before starting any existing builder.
|
|
_inspect_dedicated_builder(
|
|
runner,
|
|
require_running=False,
|
|
validate_container=has_reserved_container,
|
|
)
|
|
else:
|
|
command = _docker(
|
|
"buildx",
|
|
"create",
|
|
"--name",
|
|
FIXED_BUILDER_NAME,
|
|
"--driver",
|
|
"docker-container",
|
|
"--driver-opt",
|
|
f"image={FIXED_BUILDKIT_IMAGE}",
|
|
FIXED_DOCKER_CONTEXT,
|
|
)
|
|
_run(runner, command, stage="fixed dedicated BuildKit builder creation")
|
|
_inspect_dedicated_builder(runner, require_running=False)
|
|
_run(
|
|
runner,
|
|
_docker("buildx", "inspect", "--bootstrap", FIXED_BUILDER_NAME),
|
|
timeout=BUILDER_BOOTSTRAP_TIMEOUT_SECONDS,
|
|
stage="pinned dedicated BuildKit bootstrap",
|
|
)
|
|
_inspect_dedicated_builder(runner, require_running=True, validate_container=True)
|
|
|
|
|
|
def _verify_release_volume(runner: Runner) -> None:
|
|
raw = _run(
|
|
runner,
|
|
_docker(
|
|
"volume",
|
|
"inspect",
|
|
"--format",
|
|
"{{json .}}",
|
|
FIXED_VOLUME_NAME,
|
|
),
|
|
stage="fixed release-volume inspection",
|
|
)
|
|
try:
|
|
document = json.loads(raw)
|
|
except (ValueError, TypeError):
|
|
raise PublishError("fixed release volume metadata is invalid") from None
|
|
if isinstance(document, list):
|
|
if len(document) != 1:
|
|
raise PublishError("fixed release volume metadata is ambiguous")
|
|
document = document[0]
|
|
if (
|
|
not isinstance(document, dict)
|
|
or document.get("Name") != FIXED_VOLUME_NAME
|
|
or document.get("Driver") != "local"
|
|
or document.get("Scope") != "local"
|
|
or document.get("Mountpoint") != FIXED_VOLUME_ROOT.as_posix()
|
|
or document.get("Options") not in (None, {})
|
|
or document.get("Labels") != EXPECTED_VOLUME_LABELS
|
|
):
|
|
raise PublishError("fixed release volume differs from its reviewed contract")
|
|
|
|
|
|
def _require_no_acl(path: Path) -> None:
|
|
if os.name == "nt":
|
|
return
|
|
listxattr = getattr(os, "listxattr", None)
|
|
if not callable(listxattr):
|
|
raise PublishError("filesystem ACLs cannot be checked")
|
|
try:
|
|
names = listxattr(path, follow_symlinks=False)
|
|
except OSError:
|
|
raise PublishError("filesystem ACLs cannot be checked") from None
|
|
if any(name in {"system.posix_acl_access", "system.posix_acl_default"} for name in names):
|
|
raise PublishError("source path has an unreviewed POSIX ACL")
|
|
|
|
|
|
def _verify_local_release_root(path: Path, revision: str) -> None:
|
|
expected = FIXED_RELEASES_ROOT / revision
|
|
if path != expected or path.is_symlink():
|
|
raise PublishError("staged source path is not the fixed release generation")
|
|
try:
|
|
info = path.lstat()
|
|
volume = FIXED_VOLUME_ROOT.lstat()
|
|
except OSError:
|
|
raise PublishError("staged source generation is unavailable") from None
|
|
releases_info = FIXED_RELEASES_ROOT.lstat()
|
|
if (
|
|
path.is_symlink()
|
|
or not stat.S_ISDIR(info.st_mode)
|
|
or not stat.S_ISDIR(volume.st_mode)
|
|
or not stat.S_ISDIR(releases_info.st_mode)
|
|
or FIXED_VOLUME_ROOT.is_symlink()
|
|
or FIXED_RELEASES_ROOT.is_symlink()
|
|
):
|
|
raise PublishError("staged source generation metadata is unsafe")
|
|
if os.name != "nt" and (
|
|
info.st_uid != 0
|
|
or stat.S_IMODE(info.st_mode) != 0o555
|
|
or volume.st_uid != 0
|
|
or stat.S_IMODE(volume.st_mode) & 0o022
|
|
or releases_info.st_uid != 0
|
|
or stat.S_IMODE(releases_info.st_mode) & 0o022
|
|
):
|
|
raise PublishError("staged source generation metadata is unsafe")
|
|
_require_no_acl(path)
|
|
_require_no_acl(FIXED_VOLUME_ROOT)
|
|
_require_no_acl(FIXED_RELEASES_ROOT)
|
|
_require_no_acl(path / "infrastructure")
|
|
dockerfile = path / "infrastructure" / "images" / "python-service" / "Dockerfile"
|
|
try:
|
|
dockerfile_info = dockerfile.lstat()
|
|
except OSError:
|
|
raise PublishError("fixed ingestion Dockerfile is unavailable") from None
|
|
if dockerfile.is_symlink() or not stat.S_ISREG(dockerfile_info.st_mode):
|
|
raise PublishError("fixed ingestion Dockerfile metadata is unsafe")
|
|
if os.name != "nt" and (
|
|
dockerfile_info.st_uid != 0
|
|
or stat.S_IMODE(dockerfile_info.st_mode) not in (0o444, 0o555)
|
|
or dockerfile_info.st_nlink != 1
|
|
):
|
|
raise PublishError("fixed ingestion Dockerfile metadata is unsafe")
|
|
_require_no_acl(dockerfile)
|
|
|
|
|
|
def _parse_build_metadata(path: Path) -> tuple[str, str]:
|
|
try:
|
|
info = path.lstat()
|
|
if (
|
|
path.is_symlink()
|
|
or not stat.S_ISREG(info.st_mode)
|
|
or info.st_nlink != 1
|
|
or (os.name != "nt" and info.st_uid != 0)
|
|
or info.st_size <= 0
|
|
or info.st_size > MAX_METADATA_BYTES
|
|
):
|
|
raise PublishError("build metadata file metadata is unsafe")
|
|
_require_no_acl(path)
|
|
metadata = json.loads(path.read_text(encoding="utf-8"))
|
|
except PublishError:
|
|
raise
|
|
except (OSError, ValueError, TypeError):
|
|
raise PublishError("build metadata file is invalid") from None
|
|
if not isinstance(metadata, dict):
|
|
raise PublishError("build metadata file is invalid")
|
|
return (
|
|
_validate_digest(metadata.get("containerimage.digest")),
|
|
_validate_digest(metadata.get("containerimage.config.digest")),
|
|
)
|
|
|
|
|
|
def _parse_manifest_descriptor(raw: str, expected_digest: str) -> None:
|
|
if len(raw) > 16 * 1024:
|
|
raise PublishError("registry manifest descriptor is oversized")
|
|
try:
|
|
descriptor = json.loads(raw)
|
|
except (ValueError, TypeError):
|
|
raise PublishError("registry manifest descriptor is invalid") from None
|
|
if not isinstance(descriptor, dict) or set(descriptor) != {"digest", "mediaType", "size"}:
|
|
raise PublishError("registry manifest descriptor has an invalid shape")
|
|
if (
|
|
descriptor.get("digest") != expected_digest
|
|
or descriptor.get("mediaType") not in ALLOWED_MANIFEST_MEDIA_TYPES
|
|
or isinstance(descriptor.get("size"), bool)
|
|
or not isinstance(descriptor.get("size"), int)
|
|
or descriptor["size"] <= 0
|
|
):
|
|
raise PublishError("registry manifest digest does not match the frozen build")
|
|
|
|
|
|
def _validate_image_inspection(raw: str, frozen: FrozenSource, config_digest: str) -> None:
|
|
fields = raw.split("|")
|
|
if len(fields) != 2 + len(OCI_LABELS):
|
|
raise PublishError("registry image inspection has an invalid shape")
|
|
if fields[0] != config_digest or fields[1] != "10001:10001":
|
|
raise PublishError("registry image identity or non-root user does not match the build")
|
|
expected = [
|
|
FIXED_SOURCE_URI,
|
|
frozen.revision,
|
|
frozen.build["tree"],
|
|
frozen.build["id"],
|
|
frozen.build["builtAt"],
|
|
frozen.source_date_epoch,
|
|
]
|
|
if fields[2:] != expected:
|
|
raise PublishError("registry image provenance labels do not match the freeze")
|
|
|
|
|
|
def _safe_private_dir(path: Path, *, create: bool) -> None:
|
|
if os.name != "nt" and (not hasattr(os, "geteuid") or os.geteuid() != 0):
|
|
raise PublishError("private publication receipts require host root on POSIX")
|
|
fixed_parent = FIXED_STATE_ROOT.parent
|
|
for trusted in (fixed_parent, FIXED_STATE_ROOT):
|
|
try:
|
|
info = trusted.lstat()
|
|
except OSError:
|
|
raise PublishError("fixed controller state root is unavailable") from None
|
|
if (
|
|
trusted.is_symlink()
|
|
or not stat.S_ISDIR(info.st_mode)
|
|
or (os.name != "nt" and (info.st_uid != 0 or stat.S_IMODE(info.st_mode) & 0o022))
|
|
):
|
|
raise PublishError("fixed controller state root is unsafe")
|
|
_require_no_acl(trusted)
|
|
if not path.is_relative_to(FIXED_STATE_ROOT):
|
|
raise PublishError("publication receipt path is outside fixed controller state")
|
|
relative = path.relative_to(FIXED_STATE_ROOT)
|
|
current = FIXED_STATE_ROOT
|
|
for part in relative.parts:
|
|
current = current / part
|
|
if create:
|
|
try:
|
|
current.mkdir(mode=0o700)
|
|
except FileExistsError:
|
|
pass
|
|
except OSError:
|
|
raise PublishError("private publication directory could not be prepared") from None
|
|
elif not current.exists() and not current.is_symlink():
|
|
raise PublishError("private publication directory is unavailable")
|
|
try:
|
|
info = current.lstat()
|
|
except OSError:
|
|
raise PublishError("private publication directory is unavailable") from None
|
|
if current.is_symlink() or not stat.S_ISDIR(info.st_mode):
|
|
raise PublishError("private publication directory metadata is unsafe")
|
|
if os.name != "nt" and (info.st_uid != 0 or stat.S_IMODE(info.st_mode) != 0o700):
|
|
raise PublishError("private publication directory metadata is unsafe")
|
|
_require_no_acl(current)
|
|
|
|
|
|
def _require_publication_slot(state_root: Path, revision: str) -> None:
|
|
if state_root != FIXED_STATE_ROOT:
|
|
raise PublishError("publication receipt root is not fixed")
|
|
_safe_private_dir(state_root, create=False)
|
|
current = state_root
|
|
for part in ("mcp-hub-ingestion-publications", revision):
|
|
current = current / part
|
|
if not current.exists() and not current.is_symlink():
|
|
break
|
|
try:
|
|
info = current.lstat()
|
|
except OSError:
|
|
raise PublishError("publication receipt path is unsafe") from None
|
|
if current.is_symlink() or not stat.S_ISDIR(info.st_mode):
|
|
raise PublishError("publication receipt path is unsafe")
|
|
if os.name != "nt" and (info.st_uid != 0 or stat.S_IMODE(info.st_mode) != 0o700):
|
|
raise PublishError("publication receipt path is unsafe")
|
|
_require_no_acl(current)
|
|
receipt = state_root / "mcp-hub-ingestion-publications" / revision / "publication.json"
|
|
if receipt.exists() or receipt.is_symlink():
|
|
raise PublishError("publication receipt already exists and is never overwritten")
|
|
|
|
|
|
def _write_receipt(path: Path, payload: dict[str, Any]) -> str:
|
|
_safe_private_dir(path.parent, create=True)
|
|
if path.exists() or path.is_symlink():
|
|
raise PublishError("publication receipt already exists and is never overwritten")
|
|
receipt_hash = hashlib.sha256(
|
|
(json.dumps(payload, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
|
|
).hexdigest()
|
|
envelope = {"receipt": payload, "receiptSha256": receipt_hash}
|
|
serialized = (json.dumps(envelope, sort_keys=True, separators=(",", ":")) + "\n").encode(
|
|
"utf-8"
|
|
)
|
|
temporary = path.with_name(f".{path.name}.{os.urandom(8).hex()}.tmp")
|
|
created = False
|
|
try:
|
|
descriptor = os.open(
|
|
temporary,
|
|
os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0),
|
|
0o600,
|
|
)
|
|
created = True
|
|
try:
|
|
if os.name != "nt":
|
|
os.fchmod(descriptor, 0o600)
|
|
with os.fdopen(descriptor, "wb", closefd=False) as stream:
|
|
stream.write(serialized)
|
|
stream.flush()
|
|
os.fsync(stream.fileno())
|
|
finally:
|
|
os.close(descriptor)
|
|
info = temporary.lstat()
|
|
if temporary.is_symlink() or not stat.S_ISREG(info.st_mode):
|
|
raise PublishError("private publication receipt temporary file is unsafe")
|
|
if os.name != "nt" and (
|
|
info.st_uid != 0 or info.st_nlink != 1 or stat.S_IMODE(info.st_mode) != 0o600
|
|
):
|
|
raise PublishError("private publication receipt temporary file is unsafe")
|
|
_require_no_acl(temporary)
|
|
os.link(temporary, path, follow_symlinks=False)
|
|
temporary.unlink()
|
|
created = False
|
|
if os.name != "nt":
|
|
directory_fd = os.open(path.parent, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
|
|
try:
|
|
os.fsync(directory_fd)
|
|
finally:
|
|
os.close(directory_fd)
|
|
except PublishError:
|
|
raise
|
|
except FileExistsError:
|
|
raise PublishError("publication receipt already exists and is never overwritten") from None
|
|
except OSError:
|
|
raise PublishError("private publication receipt could not be written") from None
|
|
finally:
|
|
if created:
|
|
temporary.unlink(missing_ok=True)
|
|
return receipt_hash
|
|
|
|
|
|
def _publish_frozen_revision(
|
|
revision: str,
|
|
*,
|
|
frozen: FrozenSource,
|
|
docker_runner: Runner,
|
|
source_root: Path,
|
|
state_root: Path,
|
|
script_path: Path,
|
|
before_receipt: Callable[[], None] | None = None,
|
|
) -> dict[str, str]:
|
|
"""Build and publish only the fixed service from an attested immutable freeze."""
|
|
_validate_revision(revision)
|
|
if frozen.revision != revision or frozen.repository != FIXED_REPOSITORY:
|
|
raise PublishError("frozen source does not match the fixed target")
|
|
checked_frozen = _validate_frozen_attestation(
|
|
{
|
|
"schema": ATTESTATION_SCHEMA,
|
|
"repository": frozen.repository,
|
|
"revision": frozen.revision,
|
|
"build": frozen.build,
|
|
"sourceDateEpoch": frozen.source_date_epoch,
|
|
"freezeSha256": frozen.freeze_sha256,
|
|
"sourcePath": frozen.controller_source_path,
|
|
},
|
|
revision,
|
|
)
|
|
if checked_frozen != frozen:
|
|
raise PublishError("frozen source attestation is invalid")
|
|
expected_state_root = FIXED_STATE_ROOT if os.name != "nt" else state_root
|
|
if state_root != expected_state_root:
|
|
raise PublishError("publication receipt root is not the fixed controller state root")
|
|
_verify_local_release_root(source_root, revision)
|
|
expected_script = source_root / "scripts" / "publish_mcp_hub_ingestion.py"
|
|
if script_path.resolve() != expected_script.resolve() or script_path.is_symlink():
|
|
raise PublishError("publisher script is not from the frozen source tree")
|
|
if not script_path.is_file():
|
|
raise PublishError("publisher script is not present in the frozen source tree")
|
|
if os.name != "nt":
|
|
script_info = script_path.lstat()
|
|
if (
|
|
script_path.is_symlink()
|
|
or not stat.S_ISREG(script_info.st_mode)
|
|
or script_info.st_uid != 0
|
|
or stat.S_IMODE(script_info.st_mode) not in (0o444, 0o555)
|
|
or script_info.st_nlink != 1
|
|
):
|
|
raise PublishError("publisher script metadata is unsafe")
|
|
_require_no_acl(script_path)
|
|
_require_publication_slot(state_root, revision)
|
|
receipt_path = state_root / "mcp-hub-ingestion-publications" / revision / "publication.json"
|
|
|
|
_inspect_dedicated_builder(docker_runner, require_running=True)
|
|
with tempfile.TemporaryDirectory(prefix="mcp-hub-ingestion-") as temporary:
|
|
metadata_path = Path(temporary) / "build-metadata.json"
|
|
output = (
|
|
"type=image,"
|
|
f"name={FIXED_IMAGE_REPOSITORY},"
|
|
"push=true,push-by-digest=true,name-canonical=true"
|
|
)
|
|
command = _docker(
|
|
"buildx",
|
|
"build",
|
|
"--builder",
|
|
FIXED_BUILDER_NAME,
|
|
"--platform",
|
|
"linux/amd64",
|
|
"--pull",
|
|
"--provenance=false",
|
|
"--file",
|
|
"infrastructure/images/python-service/Dockerfile",
|
|
"--metadata-file",
|
|
str(metadata_path),
|
|
"--output",
|
|
output,
|
|
)
|
|
for key, value in (
|
|
("SERVICE_NAME", FIXED_SERVICE),
|
|
("GIT_REVISION", revision),
|
|
("GIT_SOURCE_TREE", frozen.build["tree"]),
|
|
("IMAGE_VERSION", frozen.build["id"]),
|
|
("SOURCE_DATE_EPOCH", frozen.source_date_epoch),
|
|
("BUILD_DATE", frozen.build["builtAt"]),
|
|
):
|
|
command.extend(("--build-arg", f"{key}={value}"))
|
|
command.append(".")
|
|
_run(
|
|
docker_runner,
|
|
command,
|
|
cwd=source_root,
|
|
timeout=BUILD_TIMEOUT_SECONDS,
|
|
stage="fixed ingestion image build and digest publication",
|
|
)
|
|
_inspect_dedicated_builder(docker_runner, require_running=True)
|
|
manifest_digest, config_digest = _parse_build_metadata(metadata_path)
|
|
image_ref = f"{FIXED_IMAGE_REPOSITORY}@{manifest_digest}"
|
|
descriptor = _run(
|
|
docker_runner,
|
|
_docker(
|
|
"buildx",
|
|
"imagetools",
|
|
"inspect",
|
|
"--format",
|
|
"{{json .Manifest}}",
|
|
image_ref,
|
|
),
|
|
stage="registry manifest readback",
|
|
)
|
|
_parse_manifest_descriptor(descriptor, manifest_digest)
|
|
_run(
|
|
docker_runner,
|
|
_docker("pull", "--quiet", image_ref),
|
|
timeout=BUILD_TIMEOUT_SECONDS,
|
|
stage="immutable registry image pull",
|
|
)
|
|
inspected = _run(
|
|
docker_runner,
|
|
_docker("image", "inspect", "--format", IMAGE_INSPECT_FORMAT, image_ref),
|
|
stage="immutable image provenance inspection",
|
|
)
|
|
_validate_image_inspection(inspected, frozen, config_digest)
|
|
|
|
receipt = {
|
|
"schema": RECEIPT_SCHEMA,
|
|
"repository": FIXED_REPOSITORY,
|
|
"service": FIXED_SERVICE,
|
|
"revision": revision,
|
|
"build": dict(frozen.build),
|
|
"sourceDateEpoch": frozen.source_date_epoch,
|
|
"sourceFreezeSha256": frozen.freeze_sha256,
|
|
"imageRepository": FIXED_IMAGE_REPOSITORY,
|
|
"imageRef": image_ref,
|
|
"manifestDigest": manifest_digest,
|
|
"imageId": config_digest,
|
|
"platform": "linux/amd64",
|
|
"registryReadbackVerified": True,
|
|
}
|
|
if before_receipt is not None:
|
|
before_receipt()
|
|
receipt_hash = _write_receipt(receipt_path, receipt)
|
|
return {
|
|
"status": "published",
|
|
"revision": revision,
|
|
"imageRef": image_ref,
|
|
"imageId": config_digest,
|
|
"receiptSha256": receipt_hash,
|
|
}
|
|
|
|
|
|
def _reject_docker_environment(environ: dict[str, str]) -> None:
|
|
forbidden = {
|
|
"DOCKER_HOST",
|
|
"DOCKER_CONTEXT",
|
|
"DOCKER_CONFIG",
|
|
"DOCKER_TLS_VERIFY",
|
|
"DOCKER_CERT_PATH",
|
|
"BUILDX_BUILDER",
|
|
"BUILDKIT_HOST",
|
|
"DOCKER_AUTH_CONFIG",
|
|
"BUILDX_CONFIG",
|
|
}
|
|
if any(key in environ for key in forbidden):
|
|
raise PublishError("Docker endpoint, builder, or credential-path overrides are not allowed")
|
|
if environ.get("HOME") != "/root":
|
|
raise PublishError("publisher must use the existing host-root Docker credential context")
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("revision", help="exact frozen protected-main revision")
|
|
args = parser.parse_args(argv)
|
|
try:
|
|
_validate_revision(args.revision)
|
|
_reject_docker_environment(dict(os.environ))
|
|
if os.name != "posix" or not hasattr(os, "geteuid") or os.geteuid() != 0:
|
|
raise PublishError("publisher requires POSIX host root")
|
|
runner = _docker_runner
|
|
initial_engine, initial_container = _preflight_engine(runner)
|
|
_verify_release_volume(runner)
|
|
frozen = _attest_official_freeze(args.revision, runner)
|
|
source_root = FIXED_RELEASES_ROOT / args.revision
|
|
_verify_local_release_root(source_root, args.revision)
|
|
_require_publication_slot(FIXED_STATE_ROOT, args.revision)
|
|
_ensure_dedicated_builder(runner, frozen)
|
|
|
|
def reattest_before_receipt() -> None:
|
|
final_engine, final_container = _preflight_engine(runner)
|
|
_verify_release_volume(runner)
|
|
final_frozen = _attest_official_freeze(args.revision, runner)
|
|
if (initial_engine, initial_container) != (
|
|
final_engine,
|
|
final_container,
|
|
) or final_frozen != frozen:
|
|
raise PublishError(
|
|
"controller, Docker engine, or source freeze changed during publication"
|
|
)
|
|
|
|
result = _publish_frozen_revision(
|
|
args.revision,
|
|
frozen=frozen,
|
|
docker_runner=runner,
|
|
source_root=source_root,
|
|
state_root=FIXED_STATE_ROOT,
|
|
script_path=Path(__file__),
|
|
before_receipt=reattest_before_receipt,
|
|
)
|
|
print(json.dumps(result, sort_keys=True))
|
|
return 0
|
|
except PublishError as exc:
|
|
print(f"publication denied: {exc}", file=sys.stderr)
|
|
return 2
|
|
except (OSError, ValueError):
|
|
print("publication denied: fixed publication precondition failed", file=sys.stderr)
|
|
return 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|