images: mirror digest-pinned indexes byte for byte - #391
Open
mpuig wants to merge 5 commits into
Open
Conversation
A Dockerfile that pins its base image by digest almost always pins the
multi-arch *index* digest -- it is the digest `docker pull` and Docker Hub
print. MirrorBaseImage resolved that reference to the platform image and
pushed it under the index digest, which the local registry correctly
refuses:
push to local registry: PUT .../v2/library/python/manifests/sha256:9b4929a7...:
unexpected status code 400 Bad Request: digest mismatch:
expected sha256:9b4929a7..., got sha256:1e58d36e...
The mirror failure is only a WARN, so the build then proceeds and fails
later with an unrelated error -- every digest-pinned FROM is unbuildable,
and supply-chain-pinned Dockerfiles are exactly the ones that hit it.
An index named by digest is now mirrored as the index itself (children
first, then the index manifest), so the destination stores content whose
digest is exactly the one the caller pinned. Tag references keep the
existing platform-image behavior, which is what saves storage; a digest
reference that names a plain manifest is also unchanged.
The push logic moves into pushMirrored so the property is testable against
in-memory registries: the new tests pin a random two-manifest index by
digest, mirror it, and prove the pinned digest resolves at the destination.
mpuig
added a commit
to mpuig/barista.sh
that referenced
this pull request
Aug 11, 2026
§8 and §9 went upstream as fix PRs (kernel/hypeman#391, #393), §10 and §11 as issues (#394, #395). Each finding now names the workaround it retires when the upstream fix ships, so the CI hacks have their deletion triggers on record rather than in memory.
chruffins
requested changes
Aug 12, 2026
chruffins
left a comment
Contributor
There was a problem hiding this comment.
thanks for putting up this PR! it's just missing some test coverage but looks good otherwise
…sted platform Addresses the review note on kernel#391: the existing by-tag test used a single- platform image, so it proved only that the tag path avoids mirroring the whole index — not that it resolves a multi-platform index down to the requested platform. The new test builds a two-platform index (linux/amd64 + linux/arm64), fetches the tag with WithPlatform(linux/arm64), and asserts the mirror arrives as that arm64 image: not the index, and not the amd64 sibling.
Author
|
Thanks! Added test coverage in |
Author
|
@chruffins the requested test coverage is in |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A Dockerfile that pins its base image by digest almost always pins the multi-arch index digest — it's the digest
docker pulland Docker Hub print.MirrorBaseImageresolves that reference to the platform image and pushes it under the index digest, which the local registry correctly refuses:Because the mirror failure is only a WARN, the build proceeds and fails later with an unrelated error (
create builder instance: image is requiredon v0.3.0), so the root cause is invisible in the API response. Net effect: every digest-pinnedFROMis unbuildable — and supply-chain-pinned Dockerfiles are exactly the ones that hit it.Reproduced on v0.3.0 on a GitHub-hosted ubuntu runner with
FROM python:3.13-alpine@sha256:9b4929a7…; journal evidence above.Fix
An index named by digest is mirrored as the index itself (
remote.WriteIndex— children first, then the index manifest), so the destination stores content whose digest is exactly the one the caller pinned.Unchanged behavior:
The push logic moves into
pushMirroredso the property is testable against in-memory registries (pkg/registry, as inimagepush/manager_test.go): the new tests pin a random two-manifest index by digest, mirror it, and prove the pinned digest resolves at the destination — which is the assertion the old code could not pass.Testing
go test ./lib/images/green (new + existing tests)go build ./...greenFound while building barista on hypeman; happy to adjust if you'd rather handle index-pinned refs differently (e.g. refusing them loudly instead of mirroring all platforms).
Note
Medium Risk
Changes how mirrored images are written to the local registry (index vs platform image). Tag-based mirroring is unchanged; digest-pinned indexes now copy all platforms.
Overview
Fixes
MirrorBaseImageso a digest that names a multi-arch index is mirrored as that index (remote.WriteIndex), matching the digest Dockerfiles typically pin. Previously the platform-resolved image was pushed under the index digest, which registries reject as a digest mismatch — so digest-pinnedFROMlines could not build.Tag references still mirror only the requested platform image (storage-saving path). Single-platform digest refs are unchanged.
Push logic is extracted to
pushMirroredand covered with in-memory registry tests for index-by-digest preservation, tag-as-image, and multi-platform tag resolution.Reviewed by Cursor Bugbot for commit eb23dca. Bugbot is set up for automated code reviews on this repo. Configure here.