Assert packaged image runtime configuration

This commit is contained in:
fwazb 2026-07-20 17:22:06 -07:00
parent 270bc9563e
commit d980b25198
3 changed files with 115 additions and 4 deletions

View file

@ -170,10 +170,14 @@ environment file, and no production/VPS/Telegram target. CI also builds exactly
one `linux/amd64` image, starts it with the reviewed restrictions, requires the one `linux/amd64` image, starts it with the reviewed restrictions, requires the
built-in zero-adapter/no-`send_message` health response, checks that an built-in zero-adapter/no-`send_message` health response, checks that an
unsupported `send-message` entrypoint command fails with the exact redacted unsupported `send-message` entrypoint command fails with the exact redacted
error, binds the running container to the dynamically resolved image ID, and error, and binds the running container to the dynamically resolved image ID.
removes the candidate container, tag, and visible image ID. The receipt records Before startup, the smoke also requires the built image to declare the implicit
whether `linux/amd64` ran natively or through cross-platform emulation. Pinned root bootstrap user, exact Python entrypoint, `service` command, healthcheck
base images and build cache may remain. timings, and `SIGTERM` stop signal. The successful health command then proves
PID 1 dropped to UID/GID `65532` with zero Linux capabilities. Finally, the
smoke removes the candidate container, tag, and visible image ID. The receipt
records whether `linux/amd64` ran natively or through cross-platform emulation.
Pinned base images and build cache may remain.
The entrypoint command rejection is not a real Hermes tool invocation, and the The entrypoint command rejection is not a real Hermes tool invocation, and the
bridge-network smoke does not prove general network-egress denial. These tests bridge-network smoke does not prove general network-egress denial. These tests

View file

@ -36,6 +36,16 @@ FAILURE_CLAIM_CEILING = (
) )
ENTRYPOINT = "/opt/livingip/leoclean-nosend/entrypoint.py" ENTRYPOINT = "/opt/livingip/leoclean-nosend/entrypoint.py"
RUNTIME_PYTHON = "/opt/livingip/leoclean-nosend/venv/bin/python" RUNTIME_PYTHON = "/opt/livingip/leoclean-nosend/venv/bin/python"
EXPECTED_ENTRYPOINT = [RUNTIME_PYTHON, ENTRYPOINT]
EXPECTED_COMMAND = ["service"]
EXPECTED_HEALTHCHECK = {
"Test": ["CMD", RUNTIME_PYTHON, ENTRYPOINT, "health"],
"Interval": 15_000_000_000,
"Timeout": 10_000_000_000,
"StartPeriod": 30_000_000_000,
"Retries": 4,
}
EXPECTED_STOP_SIGNAL = "SIGTERM"
class SmokeError(RuntimeError): class SmokeError(RuntimeError):
@ -76,6 +86,32 @@ def _docker_json(arguments: list[str], *, timeout: float = 120.0) -> Any:
raise SmokeError("Docker did not return JSON") from exc raise SmokeError("Docker did not return JSON") from exc
def _validate_image_runtime_configuration(image: dict[str, Any]) -> dict[str, Any]:
config = image.get("Config")
_require(isinstance(config, dict), "smoke image runtime configuration is missing")
user = config.get("User")
_require(user in (None, ""), "smoke image must begin as the root bootstrap user")
_require(config.get("Entrypoint") == EXPECTED_ENTRYPOINT, "smoke image entrypoint drifted")
_require(config.get("Cmd") == EXPECTED_COMMAND, "smoke image command drifted")
_require(config.get("StopSignal") == EXPECTED_STOP_SIGNAL, "smoke image stop signal drifted")
healthcheck = config.get("Healthcheck")
_require(isinstance(healthcheck, dict), "smoke image healthcheck is missing")
normalized_healthcheck = dict(healthcheck)
start_interval = normalized_healthcheck.pop("StartInterval", None)
_require(
start_interval is None or (type(start_interval) is int and start_interval == 0),
"smoke image healthcheck start interval drifted",
)
_require(normalized_healthcheck == EXPECTED_HEALTHCHECK, "smoke image healthcheck drifted")
return {
"bootstrap_user": "root",
"entrypoint": EXPECTED_ENTRYPOINT,
"command": EXPECTED_COMMAND,
"healthcheck": {**EXPECTED_HEALTHCHECK, "StartInterval": 0},
"stop_signal": EXPECTED_STOP_SIGNAL,
}
def _write_receipt(path: Path, value: dict[str, Any]) -> None: def _write_receipt(path: Path, value: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True) path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp") temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
@ -311,6 +347,7 @@ def run_smoke(*, repo_root: Path, artifact: Path, runtime_receipt: Path, output:
for key, value in package.expected_image_labels(image_input).items(): for key, value in package.expected_image_labels(image_input).items():
_require(labels.get(key) == value, f"smoke image label drifted: {key}") _require(labels.get(key) == value, f"smoke image label drifted: {key}")
_require(labels.get("livingip.leoclean-nosend-smoke") == smoke_id, "smoke image label is missing") _require(labels.get("livingip.leoclean-nosend-smoke") == smoke_id, "smoke image label is missing")
runtime_configuration = _validate_image_runtime_configuration(image)
_run(_run_command(tag=tag, name=name, smoke_label=smoke_label), timeout=60) _run(_run_command(tag=tag, name=name, smoke_label=smoke_label), timeout=60)
health_wait = _wait_healthy(name, expected_image_id=image_id) health_wait = _wait_healthy(name, expected_image_id=image_id)
health = _verify_health(name) health = _verify_health(name)
@ -333,6 +370,7 @@ def run_smoke(*, repo_root: Path, artifact: Path, runtime_receipt: Path, output:
"docker_server_architecture": docker_server_arch, "docker_server_architecture": docker_server_arch,
"host_machine": host_platform.machine(), "host_machine": host_platform.machine(),
"target_execution_mode": execution_mode, "target_execution_mode": execution_mode,
"image_runtime_configuration": runtime_configuration,
"health_wait": health_wait, "health_wait": health_wait,
"health": health, "health": health,
"unsupported_send_message": unsupported, "unsupported_send_message": unsupported,

View file

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import copy
import json import json
import subprocess import subprocess
from pathlib import Path from pathlib import Path
@ -9,6 +10,74 @@ import pytest
from scripts import run_gcp_leoclean_nosend_oci_smoke as smoke from scripts import run_gcp_leoclean_nosend_oci_smoke as smoke
def _runtime_image(*, user: str | None = "", start_interval: int | None = 0) -> dict:
healthcheck = {
**smoke.EXPECTED_HEALTHCHECK,
"StartInterval": start_interval,
}
return {
"Config": {
"User": user,
"Entrypoint": list(smoke.EXPECTED_ENTRYPOINT),
"Cmd": list(smoke.EXPECTED_COMMAND),
"Healthcheck": healthcheck,
"StopSignal": smoke.EXPECTED_STOP_SIGNAL,
}
}
@pytest.mark.parametrize("user", [None, ""])
@pytest.mark.parametrize("start_interval", [None, 0])
def test_runtime_configuration_accepts_portable_unset_values(user: str | None, start_interval: int | None) -> None:
receipt = smoke._validate_image_runtime_configuration(
_runtime_image(user=user, start_interval=start_interval)
)
assert receipt["bootstrap_user"] == "root"
assert receipt["entrypoint"] == smoke.EXPECTED_ENTRYPOINT
assert receipt["command"] == ["service"]
assert receipt["stop_signal"] == "SIGTERM"
@pytest.mark.parametrize(
("field", "value", "message"),
[
("User", "65532:65532", "root bootstrap user"),
("Entrypoint", ["/bin/sh"], "entrypoint drifted"),
("Cmd", ["probe"], "command drifted"),
("StopSignal", "SIGKILL", "stop signal drifted"),
],
)
def test_runtime_configuration_rejects_authoritative_field_drift(field: str, value: object, message: str) -> None:
image = _runtime_image()
image["Config"][field] = value
with pytest.raises(smoke.SmokeError, match=message):
smoke._validate_image_runtime_configuration(image)
@pytest.mark.parametrize(
("field", "value"),
[
("Test", ["CMD-SHELL", "true"]),
("Interval", 1),
("Timeout", 1),
("StartPeriod", 1),
("Retries", 1),
("StartInterval", 1),
("StartInterval", False),
("Unexpected", 1),
],
)
def test_runtime_configuration_rejects_healthcheck_drift(field: str, value: object) -> None:
image = _runtime_image()
image["Config"]["Healthcheck"] = copy.deepcopy(image["Config"]["Healthcheck"])
image["Config"]["Healthcheck"][field] = value
with pytest.raises(smoke.SmokeError, match="healthcheck"):
smoke._validate_image_runtime_configuration(image)
def test_build_command_is_one_platform_pinned_local_build(tmp_path: Path) -> None: def test_build_command_is_one_platform_pinned_local_build(tmp_path: Path) -> None:
command = smoke._build_command( command = smoke._build_command(
context=tmp_path, context=tmp_path,