Coverage for dockerfiles / runtime_smoke.py: 100.00%
53 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
1"""Runtime smoke for the distroless service images.
3Runs as the final Dockerfile stage's only RUN — exec form, as the runtime
4user — with one argument: the service entry module. It proves builder-to-
5scratch parity programmatically instead of via a hand-maintained module
6list:
8- every stdlib C extension that was importable in the builder stage must
9 import here too. ``build_scratch_rootfs.py`` records that set in
10 ``runtime_smoke_manifest.json`` next to this file, derived by actually
11 importing everything under ``lib-dynload`` in the builder. Import parity
12 is a strictly stronger completeness proof than the ldd closure alone: it
13 also catches libraries reached only via ``dlopen``, which ldd cannot see.
14- the service entry module must import (the full third-party graph);
15- ``getpass`` must resolve the synthesized passwd identity for the runtime
16 uid (NSS wiring);
17- OpenSSL's default trust store must load CA certificates (TLS to AWS);
18- ``zoneinfo`` must resolve from the staged tzdata.
20Every failure is collected and reported, then the process exits non-zero so
21the image build — including CDK deploys — fails instead of the pod. The
22script and its manifest live in the builder stage's ``/opt/build`` and reach
23the final stage through a BuildKit bind mount scoped to the smoke RUN alone:
24the deployed image ships neither of them (deleting files in a later layer
25would only hide them — layers are additive — so they are never written into
26the image at all). The manifest is located relative to ``__file__``, so the
27pair works from any mount target.
29Only ``json``, ``sys``, ``importlib``, and ``pathlib`` are imported at module
30scope; everything under test (``ssl``, ``getpass``, ``zoneinfo``, the stdlib
31extensions, the entry module) is imported inside guarded sections so a single
32breakage cannot mask the rest of the report.
33"""
35from __future__ import annotations
37import importlib
38import json
39import sys
40from pathlib import Path
43def main() -> int:
44 if len(sys.argv) != 2:
45 print("usage: runtime_smoke.py <service-entry-module>", file=sys.stderr)
46 return 2
47 entry_module = sys.argv[1]
49 manifest_path = Path(__file__).with_name("runtime_smoke_manifest.json")
50 manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
51 extensions: list[str] = manifest["stdlib_extensions"]
52 expected_user: str = manifest["runtime_user"]
54 failures: list[str] = []
55 if not extensions:
56 failures.append("manifest lists no stdlib extensions; the builder probe broke")
58 for name in extensions:
59 try:
60 # Dynamic import IS the check: the names come from the manifest
61 # this build's rootfs script derived (root-written, read-only at
62 # runtime), never from untrusted input.
63 # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import
64 importlib.import_module(name)
65 except BaseException as exc: # noqa: BLE001 — aggregate every breakage
66 failures.append(f"stdlib extension {name}: {type(exc).__name__}: {exc}")
68 try:
69 # The entry module is a literal baked into each Dockerfile's smoke
70 # RUN, not user input; importing it dynamically is this script's job.
71 # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import
72 importlib.import_module(entry_module)
73 except BaseException as exc: # noqa: BLE001
74 failures.append(f"entry module {entry_module}: {type(exc).__name__}: {exc}")
76 actual_user = "<unresolved>"
77 try:
78 import getpass
80 actual_user = getpass.getuser()
81 if actual_user != expected_user:
82 failures.append(f"runtime user: expected {expected_user!r}, got {actual_user!r}")
83 except BaseException as exc: # noqa: BLE001
84 failures.append(f"runtime identity lookup: {type(exc).__name__}: {exc}")
86 try:
87 import ssl
89 ca_count = ssl.create_default_context().cert_store_stats()["x509_ca"]
90 if ca_count <= 0:
91 failures.append("OpenSSL default trust store loaded zero CA certificates")
92 except BaseException as exc: # noqa: BLE001
93 failures.append(f"CA trust store: {type(exc).__name__}: {exc}")
95 try:
96 import zoneinfo
98 zoneinfo.ZoneInfo("UTC")
99 except BaseException as exc: # noqa: BLE001
100 failures.append(f"zoneinfo/tzdata: {type(exc).__name__}: {exc}")
102 if failures:
103 print(f"distroless runtime smoke FAILED ({len(failures)} problem(s)):", file=sys.stderr)
104 for failure in failures:
105 print(f" - {failure}", file=sys.stderr)
106 return 1
108 print(
109 f"distroless runtime smoke OK: {len(extensions)} stdlib extensions, "
110 f"entry module {entry_module}, user {actual_user}, CA trust and tzdata present"
111 )
112 return 0
115if __name__ == "__main__":
116 raise SystemExit(main())