ci: pin base images by digest resolved directly from Docker Hub
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>
This commit is contained in:
1 parent
3334f94cd5
commit
d1547f3418
7 files changed
+201
-15
No files matched your search
@@ -149,6 +149,36 @@ Remaining risks:
|
||||
# /etc/docker/daemon.json: { "registry-mirrors": ["https://192.168.1.40:5000"] } then restart docker
|
||||
```
|
||||
|
||||
## Base-image digest pinning (2026-10-22)
|
||||
|
||||
Gap closed (security review): with the BuildKit mirror on, `FROM almalinux/10-base`
|
||||
was resolved **by tag through the cache**, so a compromised cache could hand out a
|
||||
different image for that tag.
|
||||
|
||||
Now every job that uses the mirror first runs **"Resolve base image digest (Docker
|
||||
Hub, direct)"**:
|
||||
- `.gitea/scripts/resolve-base-image.sh IMAGE:TAG` gets an anonymous pull token from
|
||||
`auth.docker.io` and sends a `HEAD /v2/<repo>/manifests/<tag>` to
|
||||
`registry-1.docker.io` over HTTPS (never the mirror; HEADs don't count against the
|
||||
pull rate limit). It prints `IMAGE@sha256:<index digest>`. Input and output are
|
||||
regex-validated; values reach `run:` only via `env:`.
|
||||
- The job passes `BASE_IMAGE=<that>` as a build arg; the Dockerfiles say
|
||||
`ARG BASE_IMAGE=<tag>` / `FROM ${BASE_IMAGE}`. The mirror can then only serve
|
||||
content-addressed manifests/blobs, which BuildKit verifies against the digest.
|
||||
- **Lookup fails:** `BASE_IMAGE` falls back to the tag, the probe step does **not**
|
||||
enable the registry mirror (`registry-mirror=<not used: Docker Hub direct>`), and
|
||||
the tag is pulled straight from Docker Hub, as before the cache existed. The
|
||||
package-repo cache (`PKG_CACHE`) is unaffected, since its content is GPG-checked.
|
||||
- Freshness is automatic: the digest is looked up on every run, nothing is
|
||||
hard-coded. Local builds without the arg use the tag.
|
||||
- Log lines to look for: `base-image: almalinux/10-base:latest -> almalinux/10-base@sha256:...`
|
||||
and `pkg-cache: repos=https://192.168.1.40 registry-mirror=192.168.1.40:5000`.
|
||||
|
||||
Applied to: Build-and-Push, Build-FPM-Images, Build-Shared-httpd, Build-Shared-OLS
|
||||
(`litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER}`) here, and cloud-node-container.
|
||||
The script is canonical here (`.gitea/scripts/resolve-base-image.sh`); other repos
|
||||
carry byte-identical copies.
|
||||
|
||||
## WHP hosts (`scripts/host/pkgcache-host-repos.sh`)
|
||||
|
||||
`pkgcache-host-repos.sh apply https://192.168.1.40` does the following:
|
||||
|
||||
Reference in new issue
Block a user