Skip to content

build: assemble the image from verified local bundles - #13

Merged
joey-huckabee merged 2 commits into
mainfrom
build/hermetic-local-assembly
Sep 16, 2026
Merged

joey-huckabee merged 2 commits into
mainfrom
build/hermetic-local-assembly

Conversation

@joey-huckabee

Copy link
Copy Markdown
Contributor

Closes the remaining Package 2 build items: consume verified local bundles
with networking and pulling disabled, and prove rejection of invalid bundles.

What changes

The builder stage no longer resolves anything. It installs the exact locked
RPM closure from a verified local bundle supplied as a named build context:

  • scripts/build-image.sh re-verifies the bundle, explicitly preloads the two
    digest-pinned UBI bases, confirms they are present, and invokes Podman with
    --pull=never, --network none, and the bundle as a named context.
    PULL_BASES=0 requires the bases to be present already, for a disconnected
    build.
  • scripts/install-rpm-bundle.sh imports only the approved keys, checks every
    digest, signature, signer key ID, and NEVRA, installs with rpm --root, then
    compares the installed inventory against the lock-derived manifest and fails
    closed on any difference. It embeds the verified manifest into the image.
  • The final stage removes repository configuration inherited from the base or
    the closure, so the runtime has no package manager and no repo definitions.
  • compose.yaml no longer builds; the image is built by the script.

Negative evidence

  • tests/rpm-bundle-negative.py mutates isolated copies of the real acquired
    bundle and proves rejection of byte tampering, signature removal, signer
    mismatch, wrong version, wrong architecture, missing RPMs, and extra RPMs.
    It needs rpmsign, so it runs in the native jobs rather than the unit suite.
  • tests/hermetic-build-negative.sh proves the build rejects a wrong lock
    identity and cannot fetch an unavailable base.

CI

The native jobs replace the buildx build: acquire, verify, reject-invalid,
hermetic Podman build, hermetic negative checks, native Podman smoke, then
podman save / docker load into Docker for the existing compatibility smoke
and scanner steps. That transfer performs no build and no registry pull.

Why this matters more than it looked

PR #12 found that the buildx pipeline had been green only because a cached
dnf install layer was masking a package set the repositories no longer
offered — the pinned nginx-core build had been superseded and removed, and
nothing detected it. This build cannot fail that way: the lock enumerates every
RPM with its digest, installation compares the result against that lock, and
assembly runs with no network at all. A superseded upstream package becomes an
explicit validation failure instead of a silent cache hit.

This is also the first run that exercises the refreshed locks end to end —
acquire and verify through hermetic build, then Grype against the real
hermetic image.

🤖 Generated with Claude Code

Replace the builder-stage DNF resolution with installation of the exact
locked RPM closure. `scripts/build-image.sh` re-verifies the acquired
bundle, preloads the digest-pinned UBI bases, and builds with `--pull=never`
and `--network none`; `scripts/install-rpm-bundle.sh` imports the approved
keys, checks every digest, signature, signer, and NEVRA, and compares the
installed inventory with the lock-derived manifest.

Add negative evidence: `tests/rpm-bundle-negative.py` mutates isolated
copies of the real bundle to prove rejection of tampering, signature
removal, signer mismatch, wrong version, wrong architecture, and missing or
extra RPMs, and `tests/hermetic-build-negative.sh` proves the build rejects
a wrong lock identity and an unavailable base.

Native CI now builds with Podman and transfers the result into Docker by
local archive for the existing compatibility smoke and scanner steps.
@joey-huckabee
joey-huckabee merged commit 1d95436 into main Sep 16, 2026
7 checks passed
@joey-huckabee
joey-huckabee deleted the build/hermetic-local-assembly branch September 16, 2026 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant