From 53a8f7583f9b5ad49750104261512575054a365c Mon Sep 17 00:00:00 2001 From: Feng Ruohang Date: Mon, 9 Nov 2026 09:33:29 +0800 Subject: [PATCH 1/2] fix(docker): make the distroless image trust the same roots as the classic image The distroless variant inherited Debian's CA bundle, which still contains the two roots (Comodo AAA Services root, emSign ECC Root CA - G3) that the classic image blocklists per RHSB-2026-012. Build the same UBI bundle in a throwaway stage and replace the only file under /etc/ssl/certs with it. Pin the busybox and distroless bases by digest like Dockerfile.goreleaser, and gate the shipped file in test-release: the bundle must parse and must not contain either blocklisted subject. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Feng Ruohang --- .github/workflows/test-release.yml | 11 +++++++++++ Dockerfile.distroless | 26 ++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml index e32b338d7..1b3c702a7 100644 --- a/.github/workflows/test-release.yml +++ b/.github/workflows/test-release.yml @@ -482,6 +482,17 @@ jobs: fi done + # Both variants must trust the same roots: the UBI bundle with the + # RHSB-2026-012 blocklist applied, not Debian's bundle. + tar -xOf "${ctx}/rootfs.tar" etc/ssl/certs/ca-certificates.crt > "${ctx}/ca.pem" + openssl crl2pkcs7 -nocrl -certfile "${ctx}/ca.pem" \ + | openssl pkcs7 -print_certs -noout > "${ctx}/ca-subjects.txt" + test "$(grep -c '^subject=' "${ctx}/ca-subjects.txt")" -gt 100 + if grep -E '^subject=.*(AAA Certificate Services|emSign ECC Root CA - G3)' "${ctx}/ca-subjects.txt"; then + echo "distroless CA bundle still trusts a blocklisted root" + exit 1 + fi + # The baked-in healthcheck must drive Docker's health state on its # own, the probe binary must be directly exec-able without any # shell, and SIGTERM must still reach PID 1 (the server binary is diff --git a/Dockerfile.distroless b/Dockerfile.distroless index 03f4f7cc1..b88c27cd2 100644 --- a/Dockerfile.distroless +++ b/Dockerfile.distroless @@ -8,10 +8,30 @@ # seeds fresh volumes from the image-layer mountpoint, no entrypoint # exists to repair ownership at runtime, and 0777 is what keeps every # privilege mode working, --user included. -FROM busybox:1.37.0 AS prep +FROM busybox:1.37.0@sha256:a7dbc319b1438115a207e11930dc4bb3e643dcb3ec0bdd694bb36cd8fad289ec AS prep RUN mkdir -p /prep/data && chmod 0777 /prep/data -FROM gcr.io/distroless/static-debian12:latest +# Debian's bundle in distroless still trusts the roots the classic image +# blocklists. Build the same bundle as Dockerfile.goreleaser so both variants +# trust the same roots; it is architecture-neutral, so build it natively. +FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi9/ubi:latest@sha256:16e9e43d99e181463513cc84a514859b1abb9460f1c4d9e208da4b4e51cd5cc3 AS certs +# Native root blocklist mitigation: https://access.redhat.com/security/vulnerabilities/RHSB-2026-012 +RUN dnf -y install ca-certificates && \ + if [ -d /etc/pki/ca-trust/source/blocklist ]; then \ + blocklist=/etc/pki/ca-trust/source/blocklist; \ + else \ + blocklist=/etc/pki/ca-trust/source/blacklist; \ + fi && \ + trust extract --overwrite --format=pem-bundle \ + --filter='pkcs11:object=Comodo%20AAA%20Services%20root;type=cert' \ + "$blocklist/Comodo_AAA_Services_root.pem" && \ + trust extract --overwrite --format=pem-bundle \ + --filter='pkcs11:object=emSign%20ECC%20Root%20CA%20-%20G3;type=cert' \ + "$blocklist/emSign_ECC_Root_CA_-_G3.pem" && \ + update-ca-trust && \ + cp /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /tmp/ca-certificates.crt + +FROM gcr.io/distroless/static-debian12:latest@sha256:d75cdd72874d4790092fcb1b058493ecf6bb5bf2b2b897045b00ff01d91843f2 LABEL org.opencontainers.image.title="Silo" \ org.opencontainers.image.description="S3-Interface Libre Object Storage (distroless)" \ @@ -28,6 +48,8 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \ MINIO_CONFIG_ENV_FILE=config.env \ HOME=/tmp +# The only file under /etc/ssl/certs, so replacing it is sufficient. +COPY --from=certs /tmp/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --chmod=0755 silo /usr/bin/silo # COPY of a directory copies its contents, not the directory entry, so an # empty /prep/data would arrive as a default root:0755 /data and non-root From 16738c2301f822b792fadba68d2029e8c985c46d Mon Sep 17 00:00:00 2001 From: Feng Ruohang Date: Mon, 9 Nov 2026 09:33:29 +0800 Subject: [PATCH 2/2] build(docker): ship mcli's legal files and keep base digests current Copy LICENSE, NOTICE and CREDITS from the same verified mcli archive as the bundled binary into /licenses/mcli; the server's CREDITS covers only the server's own dependencies. Correct the mcli archive pins to the RELEASE.2026-10-26T00-00-00Z checksums. The release workflow overrides them from the attested checksum file, but a plain local build failed with "Published checksum drift". Add a Dependabot docker update so the digest-pinned bases in Dockerfile.goreleaser and Dockerfile.distroless keep receiving fixes. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Feng Ruohang --- .github/dependabot.yml | 12 ++++++++++++ Dockerfile.goreleaser | 10 +++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..7dce9691e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 + +updates: + # Dockerfile.goreleaser and Dockerfile.distroless pin their bases by digest + # so a release tag rebuilds from the same bytes. Without this the pins would + # silently rot and stop receiving base-image CVE fixes. + - package-ecosystem: docker + directory: / + schedule: + interval: weekly + commit-message: + prefix: build diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser index 5a2fbc284..47fc763f3 100644 --- a/Dockerfile.goreleaser +++ b/Dockerfile.goreleaser @@ -18,8 +18,8 @@ ENV CGO_ENABLED=0 ARG MC_REPO=pgsty/mc ARG MC_VERSION=RELEASE.2026-10-26T00-00-00Z -ARG MC_AMD64_SHA256=54bd131039dad6677f083a75207b28f82818c067c8e65fae1973f51510d268e1 -ARG MC_ARM64_SHA256=9bb294f1912f8c6b9ac08c9a0b8d2964b86306a7c440b987ee5f0570d18de3c5 +ARG MC_AMD64_SHA256=e86295bb11ed59369c0560f030d59abd42fb64a376b1d7e9799ec2e8596ebf64 +ARG MC_ARM64_SHA256=47b1a558f766f26bef5beeedd5300e082bcdec07a5e7d4a16f5f9d168f5275d1 RUN apk add -U --no-cache \ ca-certificates \ @@ -65,7 +65,9 @@ RUN apk add -U --no-cache \ echo "No mc or mcli binary found in archive:"; ls -la /tmp/mcli-extract/; exit 1; \ fi && \ chmod +x /go/bin/mcli && \ - ln -sf mcli /go/bin/mc + ln -sf mcli /go/bin/mc && \ + mkdir -p /go/share/mcli && \ + cp /tmp/mcli-extract/LICENSE /tmp/mcli-extract/NOTICE /tmp/mcli-extract/CREDITS /go/share/mcli/ FROM registry.access.redhat.com/ubi9/ubi:latest@sha256:16e9e43d99e181463513cc84a514859b1abb9460f1c4d9e208da4b4e51cd5cc3 AS certs # Native root blocklist mitigation: https://access.redhat.com/security/vulnerabilities/RHSB-2026-012 @@ -107,6 +109,8 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \ COPY --from=certs /tmp/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY silo /usr/bin/silo COPY --from=build /go/bin/mcli /usr/bin/mcli +# mcli's own legal files, from the same verified archive as the binary. +COPY --from=build /go/share/mcli /licenses/mcli COPY --from=curl-build /go/bin/curl /usr/bin/curl COPY --from=curl-build /go/share/curl /licenses/curl COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh