Skip to content

fix(docker): upgrade curl and OpenSSL explicitly so a cached layer can't keep old ones - #648

Merged
Medformatik merged 1 commit into
mainfrom
fix/image-cve-cache
Oct 26, 2026
Merged

Medformatik merged 1 commit into
mainfrom
fix/image-cve-cache

Conversation

@Medformatik

Copy link
Copy Markdown
Collaborator

What changed and why

Main's release build for 979850a (run 40362337568) failed the Trivy gate on five images, so nothing was published:

Image Finding Shipped Fixed in
api, ops-agent CVE-2026-92392 (curl/libcurl, HIGH) 8.22.0-r0 8.23.0-r0
data-manager, transitous-runner, transitous-tools CVE-2026-84782 (libssl3, HIGH) 3.0.22-1~deb12u1 3.0.22-1~deb12u2

One cause for both. None of these packages is in a base image. Each comes from the runtime stage's own apk add or apt-get install, which installs the current version only when the step actually runs. The release build passes cache-from: type=registry,ref=ghcr.io/openmapx/<app>:buildcache. An unchanged instruction is restored from that cache, with the packages it held when it was cached. #647 assumed apk add curl always fetches the fixed version; that holds only on a cold build.

A newer base digest can't fix the Debian images either. node:24.22.1-slim and debian:bookworm-slim contain no libssl3; curl, git, Python and ca-certificates pull it in. No official nginx or node Alpine image changes the Alpine side.

The fix, in each runtime stage:

  • api, ops-agent: curl libcurl join the stage's existing apk upgrade --no-cache …, followed by a version floor:
    • The floor reads the installed version with apk query --fields version libcurl and requires apk version -t … 8.23.0-r0 to be at least that version.
    • A missing package fails the floor too.
    • In ops-agent, the upgrade moves after the apk add: apk upgrade of a package that isn't installed yet exits 0 and does nothing, so placed first it would have fixed nothing.
  • data-manager, transitous-runner, transitous-tools: libssl3 openssl are named in the existing apt-get install, with the dpkg --compare-versions … ge 3.0.22-1~deb12u2 floor these Dockerfiles already use for Expat, PCRE2 and Brotli. openssl is installed in all three (ca-certificates depends on it), so it gets the same treatment.
  • web: no change. Its runner stage installs no curl. The published ghcr.io/openmapx/web:latest has neither curl nor libcurl, and its node Alpine base has none.

How this keeps a cached package layer from reintroducing a fixed CVE:

  • The instruction text changes. BuildKit keys a RUN layer's cache on its command text, so naming the package and adding the floor gives the step a new key, and the old cached layer is not reused. The baseline below shows the old layer being reused without this change.
  • The floor fails the build instead of shipping an old version. A stale mirror, or a cached layer carrying a version below it, makes the step fail. Each future fix of this kind is a one-line floor raise that also invalidates the layer, which is what the Debian Dockerfiles' comments already describe.
  • What still applies: a fix published after a layer was cached is caught by Trivy, not prevented by this. The weekly cold refresh (maxColdRefreshAge, 7 days, no-cache: true in docker.yml) rebuilds every image without cache, and raising a floor fixes it at once.

Verification

Each image was built locally like the release builds it: docker buildx build --platform linux/amd64 --pull --cache-from type=registry,ref=ghcr.io/openmapx/<app>:buildcache, read through a temporary, since-deleted Docker config. Each was then scanned with Trivy 0.75.0 at the release gate's settings: vuln, os,library, CRITICAL,HIGH, --ignore-unfixed, .trivyignore.yaml, exit code 1.

Image Package step Installed Trivy gate
api, main's Dockerfile (baseline) CACHED curl/libcurl 8.22.0-r0 fails: CVE-2026-92392 (curl, libcurl)
api ran curl/libcurl 8.23.0-r0 0 findings
ops-agent ran (DONE 19.4s) curl/libcurl 8.23.0-r0, libssl3 3.5.9-r0 0 findings
data-manager ran (DONE 72.2s) libssl3/openssl 3.0.22-1~deb12u2 0 findings
transitous-runner ran (DONE 65.1s) libssl3/openssl 3.0.22-1~deb12u2 0 findings
transitous-tools ran (DONE 56.6s) libssl3/openssl 3.0.22-1~deb12u2 0 findings
  • I tested the Alpine floor on its own: 8.22.0-r0 and a missing package fail it; 8.23.0-r0 and later pass.
  • scripts/check-infrastructure.sh (actionlint, ShellCheck, Hadolint) passes with no new findings. check-dockerfile-sync, check-docker-context-secrets and the Docker release-plan, release-contract and supply-chain tests (82) pass.
  • Not performed:
    • A local web build: its Next.js build ran out of memory under amd64 emulation in Docker Desktop. Web is unchanged, and its published image has no curl.
    • arm64 builds. The release builds linux/amd64 only.
    • A real release run. The next push to main runs one.

Release and upgrade impact

Product impact: ordinary

Only image contents change: the same packages at their fixed versions. The images reach hosts with the next published release.

…n't keep old ones

The release build restored the runtime stages' package layers from the registry build cache, so api and ops-agent kept curl 8.22.0-r0 (CVE-2026-92392) and data-manager, transitous-runner and transitous-tools kept libssl3 3.0.22-1~deb12u1 (CVE-2026-84782). Each stage now names those packages and checks a minimum version after installing them. That changes the cached instruction, so the old layer isn't reused, and it fails the build on a mirror without the fix. In ops-agent the upgrade moves after the install, since apk upgrade skips packages not yet installed.
@Medformatik Medformatik added dependencies Dependency updates and dependency maintenance docker Container images and Docker build or deployment tooling labels Oct 26, 2026
@Medformatik Medformatik self-assigned this Oct 26, 2026
@Medformatik
Medformatik merged commit 028b904 into main Oct 26, 2026
27 checks passed
@Medformatik
Medformatik deleted the fix/image-cve-cache branch October 26, 2026 18:13
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

dependencies Dependency updates and dependency maintenance docker Container images and Docker build or deployment tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant