Closes the base-image supply-chain gap from the pkg-cache security review.
Before: with the BuildKit Docker Hub mirror (192.168.1.40:5000) enabled, FROM almalinux/10-base (and litespeedtech/openlitespeed:<tag> in shared-ols) was resolved by tag through the cache, so a compromised cache could serve a different image.
Now:
New step Resolve base image digest (Docker Hub, direct) runs .gitea/scripts/resolve-base-image.sh IMAGE:TAG: anonymous token from auth.docker.io, HEAD /v2/<repo>/manifests/<tag> on registry-1.docker.io over HTTPS (never the mirror; HEAD does not count against pull limits). Output IMAGE@sha256:<64 hex>, regex-validated; values reach run: via env: only.
Build gets BASE_IMAGE=<image@sha256:...>; Dockerfiles use ARG BASE_IMAGE=<tag> + FROM ${BASE_IMAGE}. The mirror can only serve content-addressed manifests/blobs that BuildKit verifies.
Lookup fails -> BASE_IMAGE = the tag, the probe does not enable the mirror (registry-mirror=<not used: Docker Hub direct>), tag pulled directly from Docker Hub. PKG_CACHE (GPG-checked packages) is unaffected.
Freshness automatic: digest resolved every run, nothing hard-coded. Local builds use the tag.
Tested (local, upstream-fallback path, no PKG_CACHE, BASE_IMAGE=almalinux/10-base@sha256:ed618ca1...): see comment with results. Step logic simulated for success and failure (bogus image -> warning, tag fallback, pinned=false).
Closes the base-image supply-chain gap from the pkg-cache security review.
**Before:** with the BuildKit Docker Hub mirror (192.168.1.40:5000) enabled, `FROM almalinux/10-base` (and `litespeedtech/openlitespeed:<tag>` in shared-ols) was resolved *by tag through the cache*, so a compromised cache could serve a different image.
**Now:**
- New step *Resolve base image digest (Docker Hub, direct)* runs `.gitea/scripts/resolve-base-image.sh IMAGE:TAG`: anonymous token from auth.docker.io, `HEAD /v2/<repo>/manifests/<tag>` on registry-1.docker.io over HTTPS (never the mirror; HEAD does not count against pull limits). Output `IMAGE@sha256:<64 hex>`, regex-validated; values reach `run:` via `env:` only.
- Build gets `BASE_IMAGE=<image@sha256:...>`; Dockerfiles use `ARG BASE_IMAGE=<tag>` + `FROM ${BASE_IMAGE}`. The mirror can only serve content-addressed manifests/blobs that BuildKit verifies.
- Lookup fails -> `BASE_IMAGE` = the tag, the probe does **not** enable the mirror (`registry-mirror=<not used: Docker Hub direct>`), tag pulled directly from Docker Hub. PKG_CACHE (GPG-checked packages) is unaffected.
- Freshness automatic: digest resolved every run, nothing hard-coded. Local builds use the tag.
**Tested (local, upstream-fallback path, no PKG_CACHE, BASE_IMAGE=almalinux/10-base@sha256:ed618ca1...):** see comment with results. Step logic simulated for success and failure (bogus image -> warning, tag fallback, pinned=false).
Canonical script + docs: cloud-apache-container `.gitea/scripts/resolve-base-image.sh`, `docs/pkg-cache.md` ("Base-image digest pinning").
🤖 Generated with [Claude Code](https://claude.com/claude-code)
With the pkg-cache BuildKit mirror on, `FROM almalinux/10-base` was resolved
by tag through the cache, so a compromised cache could substitute the image.
Each job using the mirror now looks up the base tag's digest DIRECTLY at
registry-1.docker.io (.gitea/scripts/resolve-base-image.sh, HTTPS, HEAD on
the manifest, regex-validated) and builds FROM image@sha256:... via the
BASE_IMAGE build-arg, so the mirror can only serve content-addressed blobs.
If the lookup fails, the mirror is NOT used for that job and the tag is pulled
straight from Docker Hub. The digest is resolved every run (no manual bumps);
local builds default to the tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes the base-image supply-chain gap from the pkg-cache security review.
Before: with the BuildKit Docker Hub mirror (192.168.1.40:5000) enabled,
FROM almalinux/10-base(andlitespeedtech/openlitespeed:<tag>in shared-ols) was resolved by tag through the cache, so a compromised cache could serve a different image.Now:
.gitea/scripts/resolve-base-image.sh IMAGE:TAG: anonymous token from auth.docker.io,HEAD /v2/<repo>/manifests/<tag>on registry-1.docker.io over HTTPS (never the mirror; HEAD does not count against pull limits). OutputIMAGE@sha256:<64 hex>, regex-validated; values reachrun:viaenv:only.BASE_IMAGE=<image@sha256:...>; Dockerfiles useARG BASE_IMAGE=<tag>+FROM ${BASE_IMAGE}. The mirror can only serve content-addressed manifests/blobs that BuildKit verifies.BASE_IMAGE= the tag, the probe does not enable the mirror (registry-mirror=<not used: Docker Hub direct>), tag pulled directly from Docker Hub. PKG_CACHE (GPG-checked packages) is unaffected.Tested (local, upstream-fallback path, no PKG_CACHE, BASE_IMAGE=almalinux/10-base@sha256:ed618ca1...): see comment with results. Step logic simulated for success and failure (bogus image -> warning, tag fallback, pinned=false).
Canonical script + docs: cloud-apache-container
.gitea/scripts/resolve-base-image.sh,docs/pkg-cache.md("Base-image digest pinning").🤖 Generated with Claude Code
Local fallback-path builds (no PKG_CACHE, no mirror; docker 29.9.1/buildx 0.38), all EXIT 0, each log shows the digest FROM:
FROM docker.io/almalinux/10-base@sha256:ed618ca107e9...FROM docker.io/litespeedtech/openlitespeed@sha256:6f50ba204e66...Digest from the script =
docker buildx imagetools inspect almalinux/10-base:latestdigest. shellcheck -S warning clean.