From 0ad3afcf0ac6bb30cd36551c890e0bb1709baabc Mon Sep 17 00:00:00 2001 From: Chris An Date: Sun, 25 Oct 2026 13:27:56 +0800 Subject: [PATCH] fix(security): backport emSign distrust to 1.0.3 Update the patched verifier and bundled roots without unrelated dependency changes. Disable the distrusted G3 native root and validate both Docker trust stores. Refs #8819 --- .config/make/tests.mak | 1 + Cargo.lock | 22 ++++++------ Dockerfile | 15 +++++++- Dockerfile.glibc | 14 ++++++++ scripts/check_ca_bundle.sh | 43 ++++++++++++++++++++++ scripts/fixtures/distrusted-emsign-g3.pem | 15 ++++++++ scripts/test_ca_bundle.sh | 44 +++++++++++++++++++++++ 7 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 scripts/check_ca_bundle.sh create mode 100644 scripts/fixtures/distrusted-emsign-g3.pem create mode 100644 scripts/test_ca_bundle.sh diff --git a/.config/make/tests.mak b/.config/make/tests.mak index 00bb7641f..6a6b50e17 100644 --- a/.config/make/tests.mak +++ b/.config/make/tests.mak @@ -27,6 +27,7 @@ script-tests: ## Run shell script tests trap 'status=$$?; printf "ERROR: script-tests failed (exit %s): %s\n" "$$status" "$$BASH_COMMAND" >&2; exit "$$status"' ERR ./scripts/test_build_rustfs_options.sh ./scripts/test_docker_runtime_timezone.sh + sh ./scripts/test_ca_bundle.sh ./scripts/test_entrypoint_credentials.sh ./scripts/test_internode_grpc_ab_bench.sh ./scripts/test_object_batch_bench_enhanced.sh diff --git a/Cargo.lock b/Cargo.lock index eacba54c5..e47e7ea43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6815,7 +6815,7 @@ dependencies = [ "tracing", "twox-hash", "url", - "webpki-roots 1.0.9", + "webpki-roots 1.0.10", ] [[package]] @@ -8524,7 +8524,7 @@ dependencies = [ "tracing", "url", "uuid", - "webpki-roots 1.0.9", + "webpki-roots 1.0.10", ] [[package]] @@ -10136,7 +10136,7 @@ dependencies = [ "tracing", "twox-hash", "uuid", - "webpki-roots 1.0.9", + "webpki-roots 1.0.10", ] [[package]] @@ -10159,7 +10159,7 @@ dependencies = [ "tokio-rustls", "tracing", "uuid", - "webpki-roots 1.0.9", + "webpki-roots 1.0.10", ] [[package]] @@ -11259,9 +11259,9 @@ checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" [[package]] name = "rustls-webpki" -version = "0.103.15" +version = "0.103.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" dependencies = [ "aws-lc-rs", "ring", @@ -13551,9 +13551,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +checksum = "58ec80e73fd8baf57bf0d9615554c75348bda990d6d5bbeab964dd068319fecb" dependencies = [ "rustls-pki-types", ] @@ -13564,14 +13564,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.9", + "webpki-roots 1.0.10", ] [[package]] name = "webpki-roots" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +checksum = "1c1c8e20c8a38fc7d65abfe74eb7280e4aa667e8eec8bc84996d2874810748d9" dependencies = [ "rustls-pki-types", ] diff --git a/Dockerfile b/Dockerfile index 87c637928..cc71644b9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,13 @@ ARG TARGETARCH ARG RELEASE=latest RUN apk add --no-cache ca-certificates curl jq unzip +# Keep the native store safe even if the distribution CA package lags distrust. +COPY scripts/check_ca_bundle.sh /tmp/check_ca_bundle.sh +RUN sed -i '/^mozilla\/emSign_ECC_Root_CA_-_G3.crt$/s/^/!/' /etc/ca-certificates.conf && \ + update-ca-certificates --fresh && \ + sh /tmp/check_ca_bundle.sh /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs && \ + rm /tmp/check_ca_bundle.sh + WORKDIR /build RUN set -eux; \ @@ -98,7 +105,13 @@ RUN apk upgrade --no-cache && \ tzdata \ && test "$(TZ=Asia/Kolkata date +%z)" = "+0530" -COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +# Keep the native store safe even if the distribution CA package lags distrust. +COPY scripts/check_ca_bundle.sh /tmp/check_ca_bundle.sh +RUN sed -i '/^mozilla\/emSign_ECC_Root_CA_-_G3.crt$/s/^/!/' /etc/ca-certificates.conf && \ + update-ca-certificates --fresh && \ + sh /tmp/check_ca_bundle.sh /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs && \ + rm /tmp/check_ca_bundle.sh + COPY --from=build /build/rustfs /usr/bin/rustfs COPY entrypoint.sh /entrypoint.sh diff --git a/Dockerfile.glibc b/Dockerfile.glibc index 463994cae..7d45f7c2c 100644 --- a/Dockerfile.glibc +++ b/Dockerfile.glibc @@ -24,6 +24,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ unzip \ && rm -rf /var/lib/apt/lists/* +# Keep the native store safe even if the distribution CA package lags distrust. +COPY scripts/check_ca_bundle.sh /tmp/check_ca_bundle.sh +RUN sed -i '/^mozilla\/emSign_ECC_Root_CA_-_G3.crt$/s/^/!/' /etc/ca-certificates.conf && \ + update-ca-certificates --fresh && \ + sh /tmp/check_ca_bundle.sh /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs && \ + rm /tmp/check_ca_bundle.sh + WORKDIR /build RUN set -eux; \ @@ -103,6 +110,13 @@ RUN apt-get update && apt-get upgrade -y \ && test "$(TZ=Asia/Kolkata date +%z)" = "+0530" \ && rm -rf /var/lib/apt/lists/* +# Keep the native store safe even if the distribution CA package lags distrust. +COPY scripts/check_ca_bundle.sh /tmp/check_ca_bundle.sh +RUN sed -i '/^mozilla\/emSign_ECC_Root_CA_-_G3.crt$/s/^/!/' /etc/ca-certificates.conf && \ + update-ca-certificates --fresh && \ + sh /tmp/check_ca_bundle.sh /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs && \ + rm /tmp/check_ca_bundle.sh + COPY --from=build /build/rustfs /usr/bin/rustfs COPY entrypoint.sh /entrypoint.sh diff --git a/scripts/check_ca_bundle.sh b/scripts/check_ca_bundle.sh new file mode 100644 index 000000000..95f1b004a --- /dev/null +++ b/scripts/check_ca_bundle.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Reject the distrusted emSign ECC Root CA - G3 in native trust stores. +set -eu + +bundle=${1:?Usage: check_ca_bundle.sh BUNDLE CERT_DIRECTORY} +cert_directory=${2:?Usage: check_ca_bundle.sh BUNDLE CERT_DIRECTORY} +distrusted_sha256=86a1ecba089c4a8d3bbe2734c612ba341d813e043cf9e8a862cd5c57a36bbe6b +scratch=$(mktemp -d) +trap 'rm -rf "$scratch"' EXIT HUP INT TERM + +check_file() { + # Hash DER, not subject text or PEM formatting. Also inspect hashed links: + # native trust consumers may use the directory rather than the bundle. + awk ' + /-----BEGIN CERTIFICATE-----/ { if (inside) exit 1; inside=1; cert=""; next } + /-----END CERTIFICATE-----/ { if (!inside) exit 1; print cert; inside=0; next } + inside { gsub(/[[:space:]]/, ""); cert=cert $0 } + END { if (inside) exit 1 } + ' "$1" > "$scratch/certificates" + while IFS= read -r encoded; do + printf '%s' "$encoded" | base64 -d > "$scratch/certificate.der" + if [ ! -s "$scratch/certificate.der" ]; then + echo "Empty certificate in $1" >&2 + return 1 + fi + digest=$(sha256sum "$scratch/certificate.der") + digest=${digest%% *} + if [ "$digest" = "$distrusted_sha256" ]; then + echo "Distrusted emSign ECC Root CA - G3 found in $1" >&2 + return 1 + fi + done < "$scratch/certificates" +} + +check_file "$bundle" +if [ ! -s "$scratch/certificates" ]; then + echo "CA bundle contains no certificates: $bundle" >&2 + exit 1 +fi +find -L "$cert_directory" -type f > "$scratch/files" +while IFS= read -r file; do + check_file "$file" +done < "$scratch/files" diff --git a/scripts/fixtures/distrusted-emsign-g3.pem b/scripts/fixtures/distrusted-emsign-g3.pem new file mode 100644 index 000000000..060e943bf --- /dev/null +++ b/scripts/fixtures/distrusted-emsign-g3.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- diff --git a/scripts/test_ca_bundle.sh b/scripts/test_ca_bundle.sh new file mode 100644 index 000000000..cb5c6b3f1 --- /dev/null +++ b/scripts/test_ca_bundle.sh @@ -0,0 +1,44 @@ +#!/bin/sh +set -eu +repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +scratch=$(mktemp -d) +trap 'rm -rf "$scratch"' EXIT HUP INT TERM +mkdir "$scratch/certs" +# A public root from webpki-roots 1.0.9, not a private key or active trust anchor. +distrusted="$repo_root/scripts/fixtures/distrusted-emsign-g3.pem" +openssl req -x509 -newkey rsa:2048 -nodes -days 1 -subj /CN=ca-bundle-test \ + -keyout "$scratch/key.pem" -out "$scratch/valid.pem" >/dev/null 2>&1 +cp "$scratch/valid.pem" "$scratch/bundle.pem" +check() { + sh "$repo_root/scripts/check_ca_bundle.sh" "$scratch/bundle.pem" "$scratch/certs" +} +reject() { + if check > "$scratch/output" 2>&1; then + echo "Expected CA rejection: $1" >&2 + exit 1 + fi +} +check +cat "$distrusted" >> "$scratch/bundle.pem" +reject 'G3 after a valid certificate in the bundle' +cp "$scratch/valid.pem" "$scratch/bundle.pem" +cp "$distrusted" "$scratch/certs/renamed-root.pem" +reject 'G3 in directory with an unrelated name' +rm "$scratch/certs/renamed-root.pem" +ln -s "$distrusted" "$scratch/certs/12345678.0" +reject 'G3 through a hashed trust-directory symlink' +rm "$scratch/certs/12345678.0" +# Equivalent PEM encodings must not defeat DER-based detection. +printf '%s\n' '-----BEGIN CERTIFICATE-----' > "$scratch/bundle.pem" +sed '/-----/d' "$distrusted" | tr -d '\n' >> "$scratch/bundle.pem" +printf '\n%s\n' '-----END CERTIFICATE-----' >> "$scratch/bundle.pem" +reject 'G3 with unwrapped PEM body' +printf '%s\n' '-----BEGIN CERTIFICATE-----' > "$scratch/bundle.pem" +reject 'truncated bundle' +printf '%s\n' '-----BEGIN CERTIFICATE-----' '-----END CERTIFICATE-----' > "$scratch/bundle.pem" +reject 'empty PEM body' +: > "$scratch/bundle.pem" +reject 'empty bundle' +cp "$scratch/valid.pem" "$scratch/bundle.pem" +check +printf '%s\n' 'CA bundle regression checks passed'