teleo-infrastructure/ops/install_gcp_leoclean_nosend_release.py
fwazb a0fabc3072
Some checks are pending
CI / lint-and-test (push) Waiting to run
Add fail-closed leoclean staging installer
2026-07-21 10:58:36 -07:00

1363 lines
52 KiB
Python

#!/usr/bin/python3 -I
"""Install, restart, verify, and roll back one leoclean no-send release."""
from __future__ import annotations
import argparse
import contextlib
import fcntl
import hashlib
import json
import os
import re
import shlex
import stat
import subprocess
import sys
import tempfile
import time
from collections.abc import Callable, Mapping
from dataclasses import dataclass
from pathlib import Path
from typing import Any
CONTRACT_ROOT = Path(__file__).resolve().parents[1]
if str(CONTRACT_ROOT) not in sys.path:
sys.path.insert(0, str(CONTRACT_ROOT))
from ops import gcp_leoclean_nosend_package as package # noqa: E402
INSTALL_RECEIPT_SCHEMA = "livingip.leocleanNoSendInstallReceipt.v1"
SERVICE = package.SERVICE
SYSTEMCTL = "/usr/bin/systemctl"
MAX_COMMAND_OUTPUT_BYTES = 1024 * 1024
MAX_RELEASE_BYTES = 8 * 1024 * 1024
MAX_SOURCE_BYTES = 8 * 1024 * 1024
HEX_64 = re.compile(r"^[0-9a-f]{64}$")
HEX_40 = re.compile(r"^[0-9a-f]{40}$")
HEX_32 = re.compile(r"^[0-9a-f]{32}$")
SOURCE_PATHS = (
"ops/gcp_leoclean_nosend_package.py",
"ops/install_gcp_leoclean_nosend_release.py",
"ops/leoclean_nosend_service_control.py",
)
SERVICE_EXEC_PROPERTIES = ("ExecStartPre", "ExecStart", "ExecStop")
SERVICE_SECURITY_PROPERTIES = (
"RuntimeDirectory",
"RuntimeDirectoryMode",
"RuntimeDirectoryPreserve",
"UMask",
"NoNewPrivileges",
"CapabilityBoundingSet",
"AmbientCapabilities",
"PrivateDevices",
"PrivateTmp",
"ProtectSystem",
"ProtectHome",
"ProtectKernelTunables",
"ProtectKernelModules",
"ProtectKernelLogs",
"ProtectControlGroups",
"LockPersonality",
"RestrictSUIDSGID",
"RestrictRealtime",
"RestrictAddressFamilies",
"SystemCallArchitectures",
)
ABSENT_SERVICE_IDENTITY_PROPERTIES = (
"FragmentPath",
"DropInPaths",
"EnvironmentFiles",
"Environment",
"ControlGroup",
*SERVICE_EXEC_PROPERTIES,
"RuntimeDirectory",
)
SERVICE_PROPERTIES = (
"LoadState",
"ActiveState",
"SubState",
"MainPID",
"NRestarts",
"InvocationID",
"FragmentPath",
"DropInPaths",
"EnvironmentFiles",
"Environment",
"ControlGroup",
*SERVICE_EXEC_PROPERTIES,
*SERVICE_SECURITY_PROPERTIES,
)
FORBIDDEN_PROCESS_PREFIXES = (
"DOCKER_",
"BUILDKIT_",
"BUILDX_",
"PG",
"CLOUDSDK_",
"GOOGLE_",
"TELEO_",
"HTTP_PROXY",
"HTTPS_PROXY",
"ALL_PROXY",
"NO_PROXY",
"LD_",
"PYTHON",
)
Runner = Callable[[list[str]], subprocess.CompletedProcess[bytes]]
Sleeper = Callable[[float], None]
class InstallError(RuntimeError):
"""A bounded installer failure safe to report without command output."""
class AtomicWriteError(InstallError):
"""An atomic publication failure retaining any published inode identity."""
def __init__(self, message: str, published_identity: tuple[int, int] | None = None) -> None:
super().__init__(message)
self.published_identity = published_identity
def _require(condition: bool, message: str) -> None:
if not condition:
raise InstallError(message)
@dataclass(frozen=True)
class InstallPaths:
root: Path
unit: Path
helper: Path
state_dir: Path
docker_config: Path
release: Path
receipt: Path
lock: Path
proc_root: Path
dropin_directories: tuple[Path, ...]
@classmethod
def under(cls, root: Path, *, proc_root: Path | None = None) -> InstallPaths:
resolved = root.resolve()
def rooted(absolute: str) -> Path:
return resolved / absolute.removeprefix("/")
state = rooted("/var/lib/livingip/leoclean-nosend")
return cls(
root=resolved,
unit=rooted(f"/etc/systemd/system/{SERVICE}"),
helper=rooted(package.SERVICE_CONTROL_HELPER),
state_dir=state,
docker_config=state / "docker-config",
release=state / "release.json",
receipt=state / "install-receipt.json",
lock=rooted("/run/leoclean-gcp-nosend-installer.lock"),
proc_root=proc_root or rooted("/proc"),
dropin_directories=tuple(
rooted(path)
for path in (
f"/etc/systemd/system/{SERVICE}.d",
f"/run/systemd/system/{SERVICE}.d",
f"/usr/lib/systemd/system/{SERVICE}.d",
f"/lib/systemd/system/{SERVICE}.d",
)
),
)
@dataclass(frozen=True)
class FileSnapshot:
existed: bool
content: bytes = b""
mode: int = 0
@dataclass(frozen=True)
class PreparedInstall:
release: dict[str, Any]
unit: bytes
helper: bytes
source_binding: dict[str, Any]
@dataclass(frozen=True)
class ServicePosture:
kind: str
state: dict[str, Any]
def _sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def _canonical_json(value: Mapping[str, Any]) -> bytes:
try:
return (json.dumps(value, allow_nan=False, indent=2, sort_keys=True) + "\n").encode("utf-8")
except (TypeError, ValueError) as exc:
raise InstallError("installer receipt could not be encoded") from exc
def _default_runner(argv: list[str]) -> subprocess.CompletedProcess[bytes]:
return subprocess.run(
argv,
check=False,
stdin=subprocess.DEVNULL,
capture_output=True,
timeout=120,
env={"PATH": package.SERVICE_PATH},
)
def _run(argv: list[str], runner: Runner, *, label: str) -> subprocess.CompletedProcess[bytes]:
try:
completed = runner(argv)
except (OSError, subprocess.SubprocessError) as exc:
raise InstallError(f"{label} failed") from exc
_require(
len(completed.stdout) <= MAX_COMMAND_OUTPUT_BYTES and len(completed.stderr) <= MAX_COMMAND_OUTPUT_BYTES,
f"{label} output exceeded the safety limit",
)
return completed
def _run_checked(argv: list[str], runner: Runner, *, label: str) -> subprocess.CompletedProcess[bytes]:
completed = _run(argv, runner, label=label)
_require(completed.returncode == 0, f"{label} failed")
return completed
def _strict_json_bytes(raw: bytes, label: str) -> Any:
_require(len(raw) <= MAX_COMMAND_OUTPUT_BYTES, f"{label} output exceeded the safety limit")
def reject_duplicates(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
value: dict[str, Any] = {}
for key, item in pairs:
if key in value:
raise ValueError("duplicate key")
value[key] = item
return value
try:
return json.loads(
raw.decode("utf-8", "strict"),
object_pairs_hook=reject_duplicates,
parse_constant=lambda _value: (_ for _ in ()).throw(ValueError("constant")),
)
except (UnicodeError, ValueError, json.JSONDecodeError) as exc:
raise InstallError(f"{label} output was invalid") from exc
def _read_stable_file(
path: Path,
*,
label: str,
max_bytes: int,
expected_mode: int | None = None,
) -> bytes:
flags = os.O_RDONLY
if hasattr(os, "O_CLOEXEC"):
flags |= os.O_CLOEXEC
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
try:
descriptor = os.open(path, flags)
except OSError as exc:
raise InstallError(f"{label} was unsafe") from exc
try:
before = os.fstat(descriptor)
_require(
stat.S_ISREG(before.st_mode)
and before.st_nlink == 1
and before.st_size <= max_bytes
and (expected_mode is None or stat.S_IMODE(before.st_mode) == expected_mode),
f"{label} was unsafe",
)
chunks: list[bytes] = []
remaining = max_bytes + 1
while remaining:
chunk = os.read(descriptor, min(65536, remaining))
if not chunk:
break
chunks.append(chunk)
remaining -= len(chunk)
content = b"".join(chunks)
after = os.fstat(descriptor)
named = path.stat(follow_symlinks=False)
_require(
len(content) == after.st_size
and (
before.st_dev,
before.st_ino,
before.st_mode,
before.st_nlink,
before.st_size,
before.st_mtime_ns,
before.st_ctime_ns,
)
== (
after.st_dev,
after.st_ino,
after.st_mode,
after.st_nlink,
after.st_size,
after.st_mtime_ns,
after.st_ctime_ns,
)
and (after.st_dev, after.st_ino, after.st_mode, after.st_nlink)
== (named.st_dev, named.st_ino, named.st_mode, named.st_nlink),
f"{label} changed while it was read",
)
return content
except OSError as exc:
raise InstallError(f"{label} could not be read") from exc
finally:
os.close(descriptor)
def _read_stable_source(path: Path) -> bytes:
return _read_stable_file(path, label="installer source file", max_bytes=MAX_SOURCE_BYTES)
def reviewed_source_binding(repo_root: Path = CONTRACT_ROOT) -> dict[str, Any]:
root = repo_root.resolve()
_require(root == CONTRACT_ROOT, "installer source repository was not the contract root")
revision = package._git_commit(root)
expected = {relative: package.sha256_file(root / relative) for relative in SOURCE_PATHS}
package._verify_git_source_closure(root, revision, expected)
stable = {
"revision": revision,
"files": [{"path": relative, "sha256": expected[relative]} for relative in sorted(expected)],
}
return {**stable, "sha256": package.canonical_sha256(stable)}
def _validate_source_binding(value: Mapping[str, Any]) -> None:
_require(set(value) == {"revision", "files", "sha256"}, "installer source binding was invalid")
revision = value.get("revision")
files = value.get("files")
_require(
isinstance(revision, str) and HEX_40.fullmatch(revision) is not None, "installer source binding was invalid"
)
_require(isinstance(files, list) and len(files) == len(SOURCE_PATHS), "installer source binding was invalid")
observed_paths: list[str] = []
for item in files:
_require(isinstance(item, dict) and set(item) == {"path", "sha256"}, "installer source binding was invalid")
path = item.get("path")
digest = item.get("sha256")
_require(
isinstance(path, str) and isinstance(digest, str) and HEX_64.fullmatch(digest) is not None,
"installer source binding was invalid",
)
observed_paths.append(path)
_require(observed_paths == sorted(SOURCE_PATHS), "installer source binding was invalid")
stable = {"revision": revision, "files": files}
_require(value.get("sha256") == package.canonical_sha256(stable), "installer source binding was invalid")
def _bound_source_files(source_binding: Mapping[str, Any]) -> dict[str, bytes]:
_validate_source_binding(source_binding)
_require(
Path(__file__).resolve() == CONTRACT_ROOT / "ops/install_gcp_leoclean_nosend_release.py"
and Path(package.__file__).resolve() == CONTRACT_ROOT / "ops/gcp_leoclean_nosend_package.py",
"executing installer source was outside the contract root",
)
expected = {str(item["path"]): str(item["sha256"]) for item in source_binding["files"]}
sources: dict[str, bytes] = {}
for relative in sorted(SOURCE_PATHS):
path = (CONTRACT_ROOT / relative).resolve()
_require(path == CONTRACT_ROOT / relative, "installer source path escaped the contract root")
content = _read_stable_source(path)
_require(_sha256_bytes(content) == expected[relative], "executing installer source differed from its binding")
sources[relative] = content
return sources
def validate_release_bundle(bundle: Path) -> tuple[dict[str, Any], bytes]:
_require(not bundle.is_symlink(), "release bundle was missing or unsafe")
root = bundle.resolve()
_require(root.is_dir() and not root.is_symlink(), "release bundle was missing or unsafe")
_require(stat.S_IMODE(root.stat().st_mode) & 0o022 == 0, "release bundle was writable by group or other")
entries = sorted(path.name for path in root.iterdir())
_require(entries == [SERVICE, "release.json"], "release bundle entry set was not exact")
release_path = root / "release.json"
unit_path = root / SERVICE
release_bytes = _read_stable_file(
release_path,
label="release descriptor",
max_bytes=MAX_RELEASE_BYTES,
expected_mode=0o644,
)
unit = _read_stable_file(
unit_path,
label="release unit",
max_bytes=MAX_RELEASE_BYTES,
expected_mode=0o644,
)
try:
encoded_release = release_bytes.decode("utf-8", "strict")
release = package._strict_json_loads(encoded_release, "release descriptor")
_require(isinstance(release, dict), "release descriptor was not a JSON object")
package.validate_release_descriptor(release)
except (UnicodeError, package.PackageError) as exc:
raise InstallError("release bundle validation failed") from exc
_require(unit == package.render_systemd_unit(release).encode("utf-8"), "release unit differed from its descriptor")
return release, unit
def _prepare_install(bundle: Path, source_binding: Mapping[str, Any]) -> PreparedInstall:
sources = _bound_source_files(source_binding)
release, unit = validate_release_bundle(bundle)
helper = sources["ops/leoclean_nosend_service_control.py"]
_require(helper.startswith(b"#!/usr/bin/python3 -I\n"), "service-control helper was invalid")
return PreparedInstall(release, unit, helper, dict(source_binding))
def _build_install_plan(prepared: PreparedInstall, paths: InstallPaths) -> dict[str, Any]:
release = prepared.release
return {
"schema": "livingip.leocleanNoSendInstallPlan.v1",
"status": "pass",
"mode": "dry_run",
"installer_source": prepared.source_binding,
"release": {
"schema": release["schema"],
"release_sha256": release["release_sha256"],
"image_reference": release["image"]["reference"],
"image_config_digest": release["image"]["config_digest"],
"image_input_sha256": release["image"]["input_sha256"],
"unit_sha256": _sha256_bytes(prepared.unit),
"helper_sha256": _sha256_bytes(prepared.helper),
},
"target": {
"service": SERVICE,
"unit": str(paths.unit),
"helper": str(paths.helper),
"state_directory": str(paths.state_dir),
"docker_host": package.DOCKER_HOST,
"docker_config": str(paths.docker_config),
},
"authority": {
"image_pull": False,
"database_or_secret_provisioning": False,
"gcp_control_plane": False,
"production_or_transport": False,
"restart_requires_execute_flag": True,
},
}
def build_install_plan(
bundle: Path,
*,
paths: InstallPaths,
source_binding: Mapping[str, Any],
) -> dict[str, Any]:
return _build_install_plan(_prepare_install(bundle, source_binding), paths)
def _snapshot(path: Path) -> FileSnapshot:
try:
observed = path.stat(follow_symlinks=False)
except FileNotFoundError:
return FileSnapshot(False)
except OSError as exc:
raise InstallError("installed file could not be identified") from exc
_require(stat.S_ISREG(observed.st_mode), "installed path was not a regular file")
try:
content = path.read_bytes()
except OSError as exc:
raise InstallError("installed file could not be read") from exc
return FileSnapshot(True, content, stat.S_IMODE(observed.st_mode))
def _ensure_directory(path: Path, mode: int, created: list[Path]) -> None:
missing: list[Path] = []
cursor = path
while not cursor.exists():
_require(not cursor.is_symlink(), "install directory path was unsafe")
missing.append(cursor)
cursor = cursor.parent
observed = cursor.stat(follow_symlinks=False)
_require(stat.S_ISDIR(observed.st_mode), "install directory parent was unsafe")
for directory in reversed(missing):
directory.mkdir(mode=mode if directory == path else 0o755)
created.append(directory)
observed = path.stat(follow_symlinks=False)
_require(
stat.S_ISDIR(observed.st_mode) and observed.st_uid == os.geteuid() and observed.st_mode & 0o022 == 0,
"install directory was unsafe",
)
if path in created:
path.chmod(mode)
def _fsync_directory(path: Path) -> None:
flags = os.O_RDONLY
if hasattr(os, "O_DIRECTORY"):
flags |= os.O_DIRECTORY
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
descriptor = os.open(path, flags)
try:
_require(stat.S_ISDIR(os.fstat(descriptor).st_mode), "fsync directory was invalid")
os.fsync(descriptor)
finally:
os.close(descriptor)
def _atomic_write(path: Path, content: bytes, mode: int) -> tuple[int, int]:
_require(path.parent.is_dir() and not path.parent.is_symlink(), "install destination parent was unsafe")
if path.exists() or path.is_symlink():
_require(path.is_file() and not path.is_symlink(), "install destination was unsafe")
descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", suffix=".tmp", dir=path.parent)
temporary = Path(temporary_name)
published_identity: tuple[int, int] | None = None
try:
os.fchmod(descriptor, mode)
view = memoryview(content)
while view:
written = os.write(descriptor, view)
_require(written > 0, "install file write made no progress")
view = view[written:]
os.fsync(descriptor)
observed = os.fstat(descriptor)
_require(stat.S_ISREG(observed.st_mode) and observed.st_size == len(content), "staged install file was invalid")
staged_identity = (observed.st_dev, observed.st_ino)
os.replace(temporary, path)
published_identity = staged_identity
_fsync_directory(path.parent)
installed = path.stat(follow_symlinks=False)
os.lseek(descriptor, 0, os.SEEK_SET)
installed_content = b""
while len(installed_content) < len(content) + 1:
chunk = os.read(descriptor, min(65536, len(content) + 1 - len(installed_content)))
if not chunk:
break
installed_content += chunk
after = os.fstat(descriptor)
named_after_readback = path.stat(follow_symlinks=False)
_require(
stat.S_ISREG(installed.st_mode)
and (installed.st_dev, installed.st_ino) == staged_identity
and (after.st_dev, after.st_ino) == staged_identity
and (named_after_readback.st_dev, named_after_readback.st_ino) == staged_identity
and stat.S_IMODE(installed.st_mode) == mode
and stat.S_IMODE(named_after_readback.st_mode) == mode
and installed_content == content,
"installed file verification failed",
)
return staged_identity
except (OSError, InstallError) as exc:
raise AtomicWriteError("atomic install file publication failed", published_identity) from exc
finally:
if descriptor >= 0:
os.close(descriptor)
if temporary.exists() or temporary.is_symlink():
temporary.unlink()
def _publish_managed_file(
path: Path,
content: bytes,
mode: int,
installed_identities: dict[Path, tuple[int, int]],
) -> None:
try:
installed_identities[path] = _atomic_write(path, content, mode)
except AtomicWriteError as exc:
if exc.published_identity is not None:
installed_identities[path] = exc.published_identity
raise
def _restore_snapshot(path: Path, snapshot: FileSnapshot, installed_identity: tuple[int, int] | None) -> None:
if installed_identity is not None:
observed = path.stat(follow_symlinks=False)
_require(
stat.S_ISREG(observed.st_mode) and (observed.st_dev, observed.st_ino) == installed_identity,
"rollback refused a replaced installed file",
)
if snapshot.existed:
_atomic_write(path, snapshot.content, snapshot.mode)
return
if path.exists() or path.is_symlink():
path.unlink()
_fsync_directory(path.parent)
def _dropins_absent(paths: InstallPaths) -> None:
for directory in paths.dropin_directories:
if not directory.exists() and not directory.is_symlink():
continue
_require(directory.is_dir() and not directory.is_symlink(), "service drop-in path was unsafe")
_require(not any(directory.iterdir()), "service has an unreviewed drop-in")
def _parse_properties(raw: bytes) -> dict[str, str]:
try:
lines = raw.decode("utf-8", "strict").splitlines()
except UnicodeError as exc:
raise InstallError("systemd property output was invalid") from exc
value: dict[str, str] = {}
for line in lines:
key, separator, item = line.partition("=")
_require(
separator == "=" and key in SERVICE_PROPERTIES and key not in value, "systemd property output was invalid"
)
value[key] = item
_require(set(value) == set(SERVICE_PROPERTIES), "systemd property output was incomplete")
return value
def _service_state(runner: Runner) -> dict[str, Any]:
argv = [SYSTEMCTL, "show", SERVICE, "--no-pager"]
argv.extend(f"--property={name}" for name in SERVICE_PROPERTIES)
completed = _run_checked(argv, runner, label="systemd service readback")
raw = _parse_properties(completed.stdout)
try:
main_pid = int(raw["MainPID"] or "0")
n_restarts = int(raw["NRestarts"] or "0")
except ValueError as exc:
raise InstallError("systemd numeric property was invalid") from exc
_require(main_pid >= 0 and n_restarts >= 0, "systemd numeric property was invalid")
return {**raw, "MainPID": main_pid, "NRestarts": n_restarts}
def _rendered_service_fields(release: Mapping[str, Any]) -> dict[str, list[str]]:
fields: dict[str, list[str]] = {}
in_service = False
for line in package.render_systemd_unit(dict(release)).splitlines():
if line == "[Service]":
in_service = True
continue
if line.startswith("["):
in_service = False
continue
if not in_service or not line:
continue
key, separator, value = line.partition("=")
_require(separator == "=" and key, "rendered service contract was invalid")
fields.setdefault(key, []).append(value)
return fields
def _parse_effective_exec(value: str) -> list[str]:
matched = re.fullmatch(
r"\{ path=([^ ;]+) ; argv\[\]=([^;]+?) ; ignore_errors=(yes|no) ;.*\}",
value,
)
_require(matched is not None and matched.group(3) == "no", "effective service command was invalid")
try:
argv = shlex.split(matched.group(2))
except ValueError as exc:
raise InstallError("effective service command was invalid") from exc
_require(argv and argv[0] == matched.group(1), "effective service command was invalid")
return argv
def _validate_unit_configuration(
state: Mapping[str, Any],
paths: InstallPaths,
release: Mapping[str, Any],
) -> None:
_require(state.get("LoadState") == "loaded", "service unit was not loaded")
try:
fragment = Path(str(state.get("FragmentPath", ""))).resolve()
except OSError as exc:
raise InstallError("service fragment path was invalid") from exc
_require(fragment == paths.unit.resolve(), "service loaded an unexpected fragment")
expected_unit = package.render_systemd_unit(dict(release)).encode("utf-8")
installed_unit = _read_stable_file(
paths.unit,
label="installed service unit",
max_bytes=MAX_RELEASE_BYTES,
expected_mode=0o644,
)
_require(installed_unit == expected_unit, "installed service unit differed from the release")
_require(not str(state.get("DropInPaths", "")).strip(), "service has an effective later drop-in")
_require(not str(state.get("EnvironmentFiles", "")).strip(), "service has an effective environment file")
try:
configured_environment = shlex.split(str(state.get("Environment", "")))
except ValueError as exc:
raise InstallError("service environment property was invalid") from exc
_require(
configured_environment == [f"PATH={package.SERVICE_PATH}"],
"service configured environment was not exact",
)
fields = _rendered_service_fields(release)
for property_name in SERVICE_EXEC_PROPERTIES:
expected_values = fields.get(property_name)
_require(expected_values is not None and len(expected_values) == 1, "rendered service command was invalid")
expected_argv = shlex.split(expected_values[0])
_require(
_parse_effective_exec(str(state.get(property_name, ""))) == expected_argv,
"effective service command differed from the release",
)
for property_name in SERVICE_SECURITY_PROPERTIES:
expected_values = fields.get(property_name)
_require(expected_values is not None and len(expected_values) == 1, "rendered service security was invalid")
observed_value = str(state.get(property_name, ""))
expected_value = expected_values[0]
if property_name in {"RestrictAddressFamilies", "SystemCallArchitectures"}:
_require(
sorted(shlex.split(observed_value)) == sorted(shlex.split(expected_value)),
"effective service security differed from the release",
)
else:
_require(observed_value == expected_value, "effective service security differed from the release")
def _stable_service_posture(
release: Mapping[str, Any] | None,
*,
runner: Runner,
paths: InstallPaths,
sleeper: Sleeper,
) -> ServicePosture:
first = _service_state(runner)
sleeper(0.05)
second = _service_state(runner)
_require(first == second, "service posture changed between readbacks")
if release is None:
_require(
first["LoadState"] == "not-found"
and first["ActiveState"] == "inactive"
and first["SubState"] == "dead"
and first["MainPID"] == 0
and first["NRestarts"] == 0
and first["InvocationID"] == ""
and all(first[property_name] == "" for property_name in ABSENT_SERVICE_IDENTITY_PROPERTIES),
"unmanaged service posture was not exactly absent",
)
return ServicePosture("absent", first)
_dropins_absent(paths)
_validate_unit_configuration(first, paths, release)
if (
first["ActiveState"] == "active"
and first["SubState"] == "running"
and first["MainPID"] > 1
and HEX_32.fullmatch(str(first["InvocationID"])) is not None
and first["ControlGroup"] == f"/system.slice/{SERVICE}"
):
return ServicePosture("active", first)
_require(
first["ActiveState"] == "inactive"
and first["SubState"] == "dead"
and first["MainPID"] == 0
and first["InvocationID"] == ""
and first["ControlGroup"] == "",
"managed service posture was transitional or failed",
)
return ServicePosture("inactive", first)
def _parse_process_environment(raw: bytes) -> dict[str, str]:
_require(raw and raw.endswith(b"\0") and b"\0\0" not in raw, "service process environment was malformed")
environment: dict[str, str] = {}
try:
for record in raw[:-1].split(b"\0"):
key, separator, value = record.partition(b"=")
_require(bool(key) and separator == b"=", "service process environment was malformed")
decoded_key = key.decode("utf-8", "strict")
decoded_value = value.decode("utf-8", "strict")
_require(decoded_key not in environment, "service process environment was malformed")
environment[decoded_key] = decoded_value
except UnicodeError as exc:
raise InstallError("service process environment was malformed") from exc
return environment
def _validate_process_environment(pid: int, paths: InstallPaths) -> tuple[str, ...]:
_require(pid > 1, "service main PID was invalid")
try:
environment = _parse_process_environment((paths.proc_root / str(pid) / "environ").read_bytes())
except OSError as exc:
raise InstallError("service process environment was unreadable") from exc
_require(environment.get("PATH") == package.SERVICE_PATH, "service process PATH was not exact")
forbidden = tuple(
sorted(
key for key in environment if any(key.upper().startswith(prefix) for prefix in FORBIDDEN_PROCESS_PREFIXES)
)
)
_require(not forbidden, "service process inherited a forbidden environment field")
return tuple(sorted(environment))
def _validate_supervising_process(
pid: int,
paths: InstallPaths,
release: Mapping[str, Any],
control_group: str,
) -> dict[str, Any]:
_require(pid > 1, "service main PID was invalid")
process_root = paths.proc_root / str(pid)
fields = _rendered_service_fields(release)
expected_start = shlex.split(fields["ExecStart"][0])
expected_executable = expected_start[0]
try:
executable = os.readlink(process_root / "exe")
command_line = (process_root / "cmdline").read_bytes()
cgroup = (process_root / "cgroup").read_text(encoding="utf-8")
except (OSError, UnicodeError) as exc:
raise InstallError("service supervising process identity was unreadable") from exc
accepted_executables = {expected_executable, str(Path(expected_executable).resolve())}
_require(executable in accepted_executables, "service supervising executable differed from the release")
_require(
command_line == b"\0".join(item.encode("utf-8") for item in expected_start) + b"\0",
"service supervising command line differed from the release",
)
_require(control_group == f"/system.slice/{SERVICE}", "service control group was not exact")
cgroup_paths: list[str] = []
for line in cgroup.splitlines():
hierarchy, separator, remainder = line.partition(":")
controllers, second_separator, path = remainder.partition(":")
_require(
bool(hierarchy) and separator == ":" and second_separator == ":" and path,
"service cgroup identity was malformed",
)
del controllers
cgroup_paths.append(path)
_require(cgroup_paths and set(cgroup_paths) == {control_group}, "service cgroup differed from systemd")
return {
"executable": executable,
"cmdline_sha256": _sha256_bytes(command_line),
"cgroup": control_group,
}
def _docker_prefix(paths: InstallPaths) -> list[str]:
return [
package.DOCKER_BINARY,
f"--host={package.DOCKER_HOST}",
f"--config={paths.docker_config}",
]
def _docker_inspect(arguments: list[str], runner: Runner, paths: InstallPaths, label: str) -> dict[str, Any] | None:
completed = _run([*_docker_prefix(paths), *arguments], runner, label=label)
if completed.returncode != 0:
return None
value = _strict_json_bytes(completed.stdout, label)
_require(isinstance(value, list) and len(value) == 1 and isinstance(value[0], dict), f"{label} was not exact")
return value[0]
def _validate_local_image(release: dict[str, Any], runner: Runner, paths: InstallPaths) -> None:
reference = release["image"]["reference"]
inspected = _docker_inspect(["image", "inspect", reference], runner, paths, "local image inspection")
_require(inspected is not None, "digest-bound image was not present locally")
try:
normalized = package.build_image_inspection(release["image_input"], reference, inspected)
except package.PackageError as exc:
raise InstallError("local image differed from the finalized release") from exc
_require(normalized == release["inspection"], "local image differed from the finalized inspection")
def _validate_running_container(value: dict[str, Any], release: dict[str, Any]) -> dict[str, Any]:
config = value.get("Config")
state = value.get("State")
health = state.get("Health") if isinstance(state, dict) else None
host = value.get("HostConfig")
labels = config.get("Labels") if isinstance(config, dict) else None
runtime = release["inspection"]["runtime_config"]
container_id = value.get("Id")
_require(isinstance(container_id, str) and HEX_64.fullmatch(container_id) is not None, "container ID was invalid")
_require(
value.get("Name") == f"/{package.CONTAINER_NAME}"
and value.get("Image") == release["image"]["config_digest"]
and isinstance(config, dict)
and config.get("Image") == release["image"]["reference"]
and config.get("Env") == runtime["environment"]
and isinstance(labels, dict)
and labels.get("livingip.release.sha256") == release["release_sha256"]
and labels.get("livingip.container.role") == "leoclean-gcp-nosend"
and isinstance(state, dict)
and state.get("Running") is True
and isinstance(state.get("Pid"), int)
and state["Pid"] > 1
and isinstance(health, dict)
and health.get("Status") == "healthy"
and isinstance(host, dict)
and host.get("ReadonlyRootfs") is True
and host.get("NetworkMode") == "bridge"
and host.get("PidsLimit") == 512
and sorted(host.get("CapDrop", [])) == ["ALL"]
and sorted(host.get("CapAdd", [])) == ["CHOWN", "SETGID", "SETPCAP", "SETUID"]
and host.get("SecurityOpt") == ["no-new-privileges:true"],
"running container differed from the release contract",
)
expected_path = runtime["entrypoint"][0]
expected_args = [*runtime["entrypoint"][1:], *runtime["cmd"]]
_require(
value.get("Path") == expected_path and value.get("Args") == expected_args,
"running container command differed from the release contract",
)
return {"container_id": container_id, "container_pid": state["Pid"], "health": "healthy"}
def verify_running_release(
release: dict[str, Any],
*,
runner: Runner,
paths: InstallPaths,
sleeper: Sleeper = time.sleep,
attempts: int = 30,
) -> dict[str, Any]:
first: dict[str, Any] | None = None
for _attempt in range(attempts):
observed = _service_state(runner)
if observed["ActiveState"] == "active" and observed["SubState"] == "running" and observed["MainPID"] > 1:
first = observed
break
sleeper(0.25)
_require(first is not None, "service did not become active within the bounded wait")
_require(HEX_32.fullmatch(str(first["InvocationID"])) is not None, "service invocation ID was invalid")
_dropins_absent(paths)
_validate_unit_configuration(first, paths, release)
validated_environment_fields = _validate_process_environment(first["MainPID"], paths)
supervising_process = _validate_supervising_process(
first["MainPID"],
paths,
release,
str(first["ControlGroup"]),
)
first_container = _docker_inspect(
["container", "inspect", package.CONTAINER_NAME],
runner,
paths,
"running container inspection",
)
_require(first_container is not None, "running container was absent")
container = _validate_running_container(first_container, release)
_validate_local_image(release, runner, paths)
second_container = _docker_inspect(
["container", "inspect", container["container_id"]],
runner,
paths,
"stable container inspection",
)
_require(second_container is not None, "running container exited during verification")
stable_container = _validate_running_container(second_container, release)
second = _service_state(runner)
_validate_unit_configuration(second, paths, release)
stable_supervising_process = _validate_supervising_process(
second["MainPID"],
paths,
release,
str(second["ControlGroup"]),
)
_require(
first["MainPID"] == second["MainPID"]
and first["NRestarts"] == second["NRestarts"]
and first["InvocationID"] == second["InvocationID"]
and first["ControlGroup"] == second["ControlGroup"]
and first["ActiveState"] == second["ActiveState"] == "active"
and first["SubState"] == second["SubState"] == "running"
and container == stable_container
and supervising_process == stable_supervising_process,
"service or container changed during verification",
)
return {
"active_state": "active",
"sub_state": "running",
"main_pid": first["MainPID"],
"n_restarts": first["NRestarts"],
"invocation_id": first["InvocationID"],
"supervising_process": supervising_process,
**container,
"validated_process_environment_fields": list(validated_environment_fields),
"dropins": "absent",
"environment_files": "absent",
}
def _read_managed_release(paths: InstallPaths) -> dict[str, Any] | None:
if not paths.release.exists() and not paths.release.is_symlink():
return None
try:
value = package.load_json(paths.release, "installed release descriptor")
package.validate_release_descriptor(value)
except (OSError, package.PackageError) as exc:
raise InstallError("installed release descriptor was invalid") from exc
return value
def _validate_managed_prior_state(paths: InstallPaths, helper_source: bytes) -> dict[str, Any] | None:
unit_exists = paths.unit.exists() or paths.unit.is_symlink()
helper_exists = paths.helper.exists() or paths.helper.is_symlink()
release_exists = paths.release.exists() or paths.release.is_symlink()
receipt_exists = paths.receipt.exists() or paths.receipt.is_symlink()
if not any((unit_exists, helper_exists, release_exists, receipt_exists)):
return None
_require(all((unit_exists, helper_exists, release_exists, receipt_exists)), "managed install state was partial")
for path, mode in (
(paths.unit, 0o644),
(paths.helper, 0o755),
(paths.release, 0o600),
(paths.receipt, 0o600),
):
observed = path.stat(follow_symlinks=False)
_require(
stat.S_ISREG(observed.st_mode)
and observed.st_uid == os.geteuid()
and stat.S_IMODE(observed.st_mode) == mode
and observed.st_nlink == 1,
"managed install file posture drifted",
)
for directory in (paths.state_dir, paths.docker_config):
observed = directory.stat(follow_symlinks=False)
_require(
stat.S_ISDIR(observed.st_mode)
and observed.st_uid == os.geteuid()
and stat.S_IMODE(observed.st_mode) == 0o700,
"managed install directory posture drifted",
)
_require(not any(paths.docker_config.iterdir()), "service Docker config directory was not empty")
release = _read_managed_release(paths)
_require(release is not None, "managed release state was absent")
_require(paths.unit.read_bytes() == package.render_systemd_unit(release).encode(), "installed unit drifted")
_require(paths.helper.is_file() and not paths.helper.is_symlink(), "installed helper was unsafe")
try:
receipt = package.load_json(paths.receipt, "install receipt")
except (OSError, package.PackageError) as exc:
raise InstallError("installed receipt was invalid") from exc
_require(
receipt.get("schema") == INSTALL_RECEIPT_SCHEMA
and receipt.get("status") == "pass"
and receipt.get("release_sha256") == release["release_sha256"],
"installed receipt was invalid",
)
_require(
isinstance(receipt.get("installed_files"), dict)
and receipt["installed_files"].get("unit_sha256") == _sha256_bytes(paths.unit.read_bytes())
and receipt["installed_files"].get("helper_sha256") == _sha256_bytes(paths.helper.read_bytes()),
"installed file receipt drifted",
)
_require(bool(helper_source), "service-control helper source was empty")
return release
def _systemctl(action: str, runner: Runner) -> None:
if action == "daemon-reload":
argv = [SYSTEMCTL, "daemon-reload"]
else:
_require(action in {"restart", "stop", "reset-failed"}, "systemd action was not allowed")
argv = [SYSTEMCTL, action, SERVICE]
_run_checked(argv, runner, label=f"systemd {action}")
def _cleanup_created_directories(created: list[Path]) -> bool:
complete = True
for directory in reversed(created):
try:
directory.rmdir()
except FileNotFoundError:
pass
except OSError:
complete = False
return complete
@contextlib.contextmanager
def _install_lock(paths: InstallPaths):
if paths.root == Path("/"):
_require(os.geteuid() == 0, "live installation lock requires root")
created: list[Path] = []
_ensure_directory(paths.lock.parent, 0o755, created)
flags = os.O_RDWR | os.O_CREAT
if hasattr(os, "O_CLOEXEC"):
flags |= os.O_CLOEXEC
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
try:
descriptor = os.open(paths.lock, flags, 0o600)
except OSError as exc:
raise InstallError("installer lock was unsafe") from exc
try:
observed = os.fstat(descriptor)
named = paths.lock.stat(follow_symlinks=False)
_require(
stat.S_ISREG(observed.st_mode)
and (observed.st_dev, observed.st_ino) == (named.st_dev, named.st_ino)
and observed.st_uid == os.geteuid()
and stat.S_IMODE(observed.st_mode) == 0o600
and observed.st_nlink == 1,
"installer lock posture was unsafe",
)
try:
fcntl.flock(descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError as exc:
raise InstallError("another installer transaction holds the lock") from exc
os.fsync(descriptor)
_fsync_directory(paths.lock.parent)
yield
finally:
try:
fcntl.flock(descriptor, fcntl.LOCK_UN)
finally:
os.close(descriptor)
def _install_release_transaction(
bundle: Path,
*,
paths: InstallPaths,
source_binding: Mapping[str, Any],
runner: Runner = _default_runner,
sleeper: Sleeper = time.sleep,
execute_restart: bool = False,
) -> dict[str, Any]:
prepared = _prepare_install(bundle, source_binding)
plan = _build_install_plan(prepared, paths)
if not execute_restart:
return plan
release = prepared.release
unit_content = prepared.unit
helper_content = prepared.helper
_dropins_absent(paths)
prior_release = _validate_managed_prior_state(paths, helper_content)
prior_posture = _stable_service_posture(
prior_release,
runner=runner,
paths=paths,
sleeper=sleeper,
)
before = prior_posture.state
prior_active = prior_posture.kind == "active"
created: list[Path] = []
try:
_ensure_directory(paths.unit.parent, 0o755, created)
_ensure_directory(paths.helper.parent, 0o755, created)
_ensure_directory(paths.state_dir, 0o700, created)
_ensure_directory(paths.docker_config, 0o700, created)
_require(not any(paths.docker_config.iterdir()), "service Docker config directory was not empty")
_validate_local_image(release, runner, paths)
except Exception as exc:
cleanup_complete = _cleanup_created_directories(created)
if not cleanup_complete:
raise InstallError("preflight failed and cleanup was incomplete") from exc
raise
snapshots = {
paths.unit: _snapshot(paths.unit),
paths.helper: _snapshot(paths.helper),
paths.release: _snapshot(paths.release),
paths.receipt: _snapshot(paths.receipt),
}
installed_identities: dict[Path, tuple[int, int]] = {}
attempted_paths: set[Path] = set()
exact_already_installed = (
prior_release is not None
and prior_release == release
and snapshots[paths.unit].content == unit_content
and snapshots[paths.helper].content == helper_content
)
if exact_already_installed:
verified = verify_running_release(release, runner=runner, paths=paths, sleeper=sleeper)
existing_receipt = package.load_json(paths.receipt, "install receipt")
return {**existing_receipt, "result": "already_installed", "verification": verified}
if prior_active:
_require(prior_release is not None, "active managed service had no prior release")
prior_verification = verify_running_release(prior_release, runner=runner, paths=paths, sleeper=sleeper)
_require(
prior_verification["main_pid"] == before["MainPID"]
and prior_verification["n_restarts"] == before["NRestarts"]
and prior_verification["invocation_id"] == before["InvocationID"],
"active prior service changed before the trusted stop",
)
candidate_may_be_active = False
try:
if prior_active:
# Stop with the currently loaded unit so its release-bound helper
# validates the old container before the unit is replaced.
_systemctl("stop", runner)
attempted_paths.add(paths.helper)
_publish_managed_file(paths.helper, helper_content, 0o755, installed_identities)
attempted_paths.add(paths.unit)
_publish_managed_file(paths.unit, unit_content, 0o644, installed_identities)
_systemctl("daemon-reload", runner)
configured_posture = _stable_service_posture(
release,
runner=runner,
paths=paths,
sleeper=sleeper,
)
_require(configured_posture.kind == "inactive", "candidate service was active before its explicit restart")
candidate_may_be_active = True
_systemctl("restart", runner)
verified = verify_running_release(release, runner=runner, paths=paths, sleeper=sleeper)
release_bytes = _canonical_json(release)
attempted_paths.add(paths.release)
_publish_managed_file(paths.release, release_bytes, 0o600, installed_identities)
receipt = {
"schema": INSTALL_RECEIPT_SCHEMA,
"status": "pass",
"result": "installed",
"installer_source": dict(source_binding),
"release_sha256": release["release_sha256"],
"image": {
"reference": release["image"]["reference"],
"digest": release["image"]["digest"],
"config_digest": release["image"]["config_digest"],
"input_sha256": release["image"]["input_sha256"],
},
"installed_files": {
"unit_sha256": _sha256_bytes(unit_content),
"helper_sha256": _sha256_bytes(helper_content),
"release_descriptor_sha256": _sha256_bytes(release_bytes),
},
"service_before": {
"load_state": before["LoadState"],
"active_state": before["ActiveState"],
"sub_state": before["SubState"],
"main_pid": before["MainPID"],
"n_restarts": before["NRestarts"],
},
"verification": verified,
"rollback": {
"previous_release_present": prior_release is not None,
"previous_service_active": prior_active,
"previous_service_posture": prior_posture.kind,
"automatic_on_failure": True,
},
"authority": {
"image_pulled": False,
"cloud_or_database_authority_changed": False,
"transport_authority_changed": False,
},
}
receipt_bytes = _canonical_json(receipt)
attempted_paths.add(paths.receipt)
_publish_managed_file(paths.receipt, receipt_bytes, 0o600, installed_identities)
return receipt
except Exception as exc:
rollback_errors: list[str] = []
if candidate_may_be_active:
try:
_systemctl("stop", runner)
except InstallError:
rollback_errors.append("candidate_stop")
for path in (paths.receipt, paths.release, paths.unit, paths.helper):
if path not in attempted_paths:
continue
try:
_restore_snapshot(path, snapshots[path], installed_identities.get(path))
except (OSError, InstallError):
rollback_errors.append("file_restore")
try:
_systemctl("daemon-reload", runner)
except InstallError:
rollback_errors.append("daemon_reload")
if prior_active:
try:
_systemctl("restart", runner)
_require(prior_release is not None, "prior release was missing during rollback")
restored = verify_running_release(prior_release, runner=runner, paths=paths, sleeper=sleeper)
_require(
restored["n_restarts"] == before["NRestarts"],
"rollback did not restore the prior active posture",
)
except InstallError:
rollback_errors.append("prior_restart")
else:
try:
if prior_posture.kind == "absent":
_systemctl("reset-failed", runner)
restored_posture = _stable_service_posture(
prior_release,
runner=runner,
paths=paths,
sleeper=sleeper,
)
_require(
restored_posture.kind == prior_posture.kind
and restored_posture.state["NRestarts"] == before["NRestarts"],
"rollback did not restore the prior service posture",
)
except InstallError:
rollback_errors.append("inactive_cleanup")
if not _cleanup_created_directories(created):
rollback_errors.append("directory_cleanup")
if rollback_errors:
raise InstallError("installation failed and automatic rollback was incomplete") from exc
if isinstance(exc, InstallError):
raise
raise InstallError("installation failed and was rolled back") from exc
def install_release(
bundle: Path,
*,
paths: InstallPaths,
source_binding: Mapping[str, Any],
runner: Runner = _default_runner,
sleeper: Sleeper = time.sleep,
execute_restart: bool = False,
) -> dict[str, Any]:
if not execute_restart:
return _install_release_transaction(
bundle,
paths=paths,
source_binding=source_binding,
runner=runner,
sleeper=sleeper,
execute_restart=False,
)
with _install_lock(paths):
return _install_release_transaction(
bundle,
paths=paths,
source_binding=source_binding,
runner=runner,
sleeper=sleeper,
execute_restart=True,
)
class _Parser(argparse.ArgumentParser):
def error(self, _message: str) -> None:
raise InstallError("invalid arguments")
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = _Parser(description=__doc__, add_help=False)
parser.add_argument("--bundle", required=True, type=Path)
parser.add_argument("--execute-restart", action="store_true")
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
try:
args = parse_args(argv)
root = Path("/")
if args.execute_restart:
_require(os.geteuid() == 0, "live installation requires root")
result = install_release(
args.bundle.absolute(),
paths=InstallPaths.under(root),
source_binding=reviewed_source_binding(),
execute_restart=args.execute_restart,
)
sys.stdout.write(json.dumps(result, allow_nan=False, separators=(",", ":"), sort_keys=True) + "\n")
return 0
except (InstallError, package.PackageError) as exc:
sys.stderr.write(
json.dumps({"error": str(exc), "status": "fail"}, separators=(",", ":"), sort_keys=True) + "\n"
)
return 65
except Exception:
sys.stderr.write('{"error":"installer failed","status":"fail"}\n')
return 65
if __name__ == "__main__":
raise SystemExit(main())