From acc9cec56390ae14ae031d74961fde77254f447e Mon Sep 17 00:00:00 2001 From: sohier Date: Tue, 20 Oct 2026 21:35:33 +0000 Subject: [PATCH] More certs --- Dockerfile.tmpl | 17 ++++++++++++++ kaggle_requirements.txt | 5 ++++ tests/test_certifi.py | 52 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 826fe5b5..876104a2 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -16,6 +16,7 @@ RUN cat /kaggle_requirements.txt >> /requirements.txt # GHSA-hq85-5cpj-mr62: uv < 0.13.4 vendors a CA bundle that trusts emSign intermediates with compromised keys. # Upgrade it first so the patched uv performs all subsequent installs. # The base image's uv may not be the pip-managed one, so point every uv on PATH at the new binary. +# Remove once the base image ships uv >= 0.13.4 itself. RUN uv pip install --system --no-cache "uv>=0.13.4" && \ NEW_UV="$(python -c 'import uv; print(uv.find_uv_bin())')" && \ for old in $(which -a uv); do [ "$old" -ef "$NEW_UV" ] || ln -sf "$NEW_UV" "$old"; done @@ -63,6 +64,22 @@ RUN sed -i "s/httpredir.debian.org/debian.uchicago.edu/" /etc/apt/sources.list & apt-get install -y graphviz && pip install graphviz && \ /tmp/clean-layer.sh +# GHSA-6q48-6c7w-9gw2: emSign ECC Root CA - G3 issued intermediates with compromised keys. +# Ubuntu's noble-security ca-certificates 20260816ubuntu0.24.04.1 blocklists it in the system store. +# The Java keystore only drops certificates when rebuilt from scratch. +# Remove once the base image ships that ca-certificates and a cacerts keystore without the G3 root. +RUN apt-get install -y --only-upgrade ca-certificates && \ + rm -f /etc/ssl/certs/java/cacerts && \ + update-ca-certificates --fresh && \ + /tmp/clean-layer.sh + +# gcloud (including the gsutil and bq it wraps), gRPC, and Node vendor unpatched bundles, so point them at the system store. +# No upstream fix exists for these yet. Remove each one once the base image ships a release whose bundle drops the G3 root. +# gcloud ignores the base image's installation properties file (it can't locate its SDK root), so use the env var form. +ENV CLOUDSDK_CORE_CUSTOM_CA_CERTS_FILE=/etc/ssl/certs/ca-certificates.crt \ + GRPC_DEFAULT_SSL_ROOTS_FILE_PATH=/etc/ssl/certs/ca-certificates.crt \ + NODE_OPTIONS=--use-openssl-ca + # patches/keras_internal.py is not installed right now. tensorflow_decision_forests # is no longer in the base image and its latest release (1.12.0) ships no cp313 # wheel, so there is no tensorflow_decision_forests/keras/ to patch. diff --git a/kaggle_requirements.txt b/kaggle_requirements.txt index b325d320..595bd557 100644 --- a/kaggle_requirements.txt +++ b/kaggle_requirements.txt @@ -16,6 +16,7 @@ boto3 catboost category-encoders # GHSA-6q48-6c7w-9gw2: older bundles trust emSign intermediates with compromised keys. +# Remove once the base image ships >=2026.10.19 itself. certifi>=2026.10.19 cesium comm @@ -111,6 +112,7 @@ pdf2image # Remove once the base image ships >=12.2 itself. pillow>=12.2 # pip < 26.2.2 always loads its vendored certifi, so it needs the GHSA-6q48-6c7w-9gw2 fix separately. +# Remove once the base image ships >=26.2.2 itself. pip>=26.2.2 plotly-express pudb @@ -151,6 +153,9 @@ transformers>=5.0.0 urllib3>=2.7 universal-pathlib vtk +# wandb < 0.31.0 embeds a CA bundle with the GHSA-6q48-6c7w-9gw2 emSign root in its Rust binaries. +# Remove once the base image ships >=0.31.0 itself. +wandb>=0.31.0 wavio xvfbwrapper ydata-profiling diff --git a/tests/test_certifi.py b/tests/test_certifi.py index bc14e924..6a8d76e4 100644 --- a/tests/test_certifi.py +++ b/tests/test_certifi.py @@ -1,10 +1,16 @@ +import glob +import os +import ssl import subprocess import unittest import certifi import pip +import wandb from packaging.version import Version +SYSTEM_BUNDLE = "/etc/ssl/certs/ca-certificates.crt" + class TestCertifi(unittest.TestCase): # GHSA-6q48-6c7w-9gw2: emSign ECC Root CA - G3 intermediates have compromised keys. @@ -20,3 +26,49 @@ def test_pip_vendored_certifi_patched(self): def test_uv_vendored_certs_patched(self): version = subprocess.check_output(["uv", "--version"], text=True).split()[1] self.assertGreaterEqual(Version(version), Version("0.13.4")) + + # Covers both the merged bundle and the per-certificate files OpenSSL looks up by hash. + def test_system_store_patched(self): + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + for path in [SYSTEM_BUNDLE, *glob.glob("/etc/ssl/certs/*.pem")]: + context.load_verify_locations(cafile=path) + common_names = { + value + for cert in context.get_ca_certs() + for rdn in cert["subject"] + for key, value in rdn + if key == "commonName" + } + # The other emSign roots are unaffected and should still be trusted. + self.assertIn("emSign Root CA - G1", common_names) + self.assertNotIn("emSign ECC Root CA - G3", common_names) + + def test_java_keystore_patched(self): + listing = subprocess.check_output( + ["keytool", "-list", "-v", "-cacerts", "-storepass", "changeit"], text=True + ) + self.assertIn("emSign Root CA - G1", listing) + self.assertNotIn("emSign ECC Root CA - G3", listing) + + # gcloud, gRPC, and Node vendor unpatched bundles, so they must use the system store. + # Remove each of these alongside its workaround in Dockerfile.tmpl. + def test_gcloud_uses_system_store(self): + path = subprocess.check_output( + ["gcloud", "config", "get", "core/custom_ca_certs_file"], text=True + ).strip() + self.assertEqual(SYSTEM_BUNDLE, path) + + def test_grpc_uses_system_store(self): + self.assertEqual(SYSTEM_BUNDLE, os.environ.get("GRPC_DEFAULT_SSL_ROOTS_FILE_PATH")) + + def test_node_uses_system_store(self): + self.assertIn("--use-openssl-ca", os.environ.get("NODE_OPTIONS", "")) + # Node refuses to start if NODE_OPTIONS contains a flag it doesn't accept. + subprocess.check_call(["node", "-e", ""]) + + def test_wandb_binaries_patched(self): + bin_dir = os.path.join(os.path.dirname(wandb.__file__), "bin") + for name in os.listdir(bin_dir): + with open(os.path.join(bin_dir, name), "rb") as f: + # Not assertNotIn: its failure message would dump the whole binary. + self.assertFalse(b"emSign ECC Root CA - G3" in f.read(), name)