diff --git a/Dockerfile.django-alpine b/Dockerfile.django-alpine index 5235fa5ec8e..13db4877ca9 100644 --- a/Dockerfile.django-alpine +++ b/Dockerfile.django-alpine @@ -57,6 +57,12 @@ RUN \ && \ rm -rf /var/cache/apk/* && \ true +# Upgrade the system trust store in its own layer. Release builds reuse the layer +# above from the build cache, so an OS security fix to ca-certificates (here the +# eMudhra key compromise, 2026-10-20) would otherwise never reach the image. +# Bump CA_REFRESH to force this layer to rebuild for the next trust-store fix. +ARG CA_REFRESH=2026-10-20 +RUN echo "trust store refresh ${CA_REFRESH}" && apk upgrade --no-cache ca-certificates-bundle ca-certificates RUN \ --mount=from=build,src=/tmp/wheels,target=/tmp/wheels \ --mount=from=build,src=/app/requirements.txt,target=/app/requirements.txt \ @@ -65,6 +71,9 @@ RUN \ --no-index \ --find-links=/tmp/wheels \ -r ./requirements.txt +# Fail the build if either trust store still carries the root distrusted after the +# eMudhra key compromise (2026-10-20), so a cached or stale layer can't ship it. +RUN python3 -c "import ssl, sys, certifi; bad = [f for f in ('/etc/ssl/certs/ca-certificates.crt', certifi.where()) if (lambda ctx: ctx.load_verify_locations(cafile=f) or any(dict(x[0] for x in c['subject']).get('commonName') == 'emSign ECC Root CA - G3' for c in ctx.get_ca_certs()))(ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT))]; sys.exit('distrusted root emSign ECC Root CA - G3 still in: ' + ', '.join(bad) if bad else 0)" COPY \ docker/entrypoint-celery-beat.sh \ diff --git a/Dockerfile.django-debian b/Dockerfile.django-debian index 62552b82cce..6011dcb446d 100644 --- a/Dockerfile.django-debian +++ b/Dockerfile.django-debian @@ -70,6 +70,18 @@ RUN \ apt-get clean && \ rm -rf /var/lib/apt/lists && \ true +# Upgrade the system trust store in its own layer. Release builds reuse the layer +# above from the build cache, so an OS security fix to ca-certificates (here the +# eMudhra key compromise, 2026-10-20) would otherwise never reach the image. +# Bump CA_REFRESH to force this layer to rebuild for the next trust-store fix. +ARG CA_REFRESH=2026-10-20 +RUN \ + echo "trust store refresh ${CA_REFRESH}" && \ + apt-get -y update && \ + apt-get -y install --no-install-recommends --only-upgrade ca-certificates && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists && \ + true RUN \ --mount=from=build,src=/tmp/wheels,target=/tmp/wheels \ --mount=from=build,src=/app/requirements.txt,target=/app/requirements.txt \ @@ -79,6 +91,9 @@ RUN \ --find-links=/tmp/wheels \ -r ./requirements.txt && \ apt-get -y purge --auto-remove git +# Fail the build if either trust store still carries the root distrusted after the +# eMudhra key compromise (2026-10-20), so a cached or stale layer can't ship it. +RUN python3 -c "import ssl, sys, certifi; bad = [f for f in ('/etc/ssl/certs/ca-certificates.crt', certifi.where()) if (lambda ctx: ctx.load_verify_locations(cafile=f) or any(dict(x[0] for x in c['subject']).get('commonName') == 'emSign ECC Root CA - G3' for c in ctx.get_ca_certs()))(ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT))]; sys.exit('distrusted root emSign ECC Root CA - G3 still in: ' + ', '.join(bad) if bad else 0)" COPY \ docker/entrypoint-celery-beat.sh \