Repository navigation
Stop trusting the compromised eMudhra CA certificates - #3999
Merged
Merged
Conversation
The private keys of eight eMudhra (emSign) intermediate CA certificates can be recovered from the public certificates, so anyone can issue a certificate for any domain chaining to "emSign ECC Root CA - G3" or, through Sectigo's cross-signatures, to "AAA Certificate Services". - Upgrade certifi from 2026.4.22 to 2026.10.19 in uv.lock (httpx). - Install ca-certificates in the Cloud Run image so the build always takes Debian's current CA store, and set GRPC_DEFAULT_SSL_ROOTS_FILE_PATH to that bundle so gRPC clients stop using the CA list bundled in grpcio, which has no fixed release. - Raise uv from 0.12.1 to 0.13.4 in the Cloud Run image, the versioning job and the collector deploy workflow. - Raise the observability collector image from otelcol-google 0.160.0 to 0.163.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3999 +/- ##
===========================================
+ Coverage 49.24% 86.00% +36.76%
===========================================
Files 243 259 +16
Lines 14398 15777 +1379
Branches 2343 2628 +285
===========================================
+ Hits 7090 13569 +6479
+ Misses 6682 1456 -5226
- Partials 626 752 +126 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4001
Summary
On 2026-10-20 it was disclosed that the private keys of eight eMudhra (emSign) intermediate CA certificates can be recovered from the public certificates (https://emudhra.we-have-a-problem.com). Anyone can issue a certificate for any domain that chains to "emSign ECC Root CA - G3" or, through Sectigo's cross-signatures, to "AAA Certificate Services", so a client that carries its own CA list (or reads an outdated operating-system store) and does not check revocation can have its HTTPS traffic intercepted. No exploitation has been reported. Browsers and current Windows and macOS already block these certificates; the exposure is in server-side clients like the ones in the Public API.
What in the Public API trusted the affected roots
Established from
uv.lock, the Dockerfiles, the workflows, the application code and the Actions run history onmasterat 392e653:uv.lock) includes emSign ECC Root CA - G3. httpx uses it for the Auth0 token request (libs/gateway_auth.py), the Auth0 Management API client (libs/auth0_management.py), the simulation service client (libs/simulation_entrypoint.py) and Hugging Face downloads (services/economy_comparison.py). Checked: httpx 0.28.1 with certifi 2026.4.22 completed a request tohttps://dv-g3b-emudhra.we-have-a-problem.com/(HTTP 200).python:3.12-slim@sha256:a6e34c59…, which has Debian ca-certificates 20250419 with the affected roots. PyJWT's Auth0 signing-key fetch (libs/user_auth.py) reads this store.gcp/cloud_run/Dockerfilenever installs ca-certificates, so the image only gets a fixed store when the base image happens to carry one.grpc/_cython/_credentials/roots.pem), which contains both emSign ECC Root CA - G3 and AAA Certificate Services. The Google Cloud Logging, Secret Manager and OTLP export clients use it. No grpcio release removes them yet (fixed in source in [security] Update gRPC repo roots. grpc/grpc#43699).push.yml, the collector deploy workflow) uses a compiled-in CA list. Checked: uv 0.12.1 completed the TLS handshake with the emSign test endpoint; uv 0.13.4 rejects it withinvalid peer certificate: UnknownIssuer.Not affected: the Redis cache client trusts only the Memorystore CA from
RUNTIME_CACHE_CA_CERT, and Cloud SQL connections use the instance's own CA.docker/Dockerfile(ghcr.io/policyengine/policyengine) needs no change: today'spython:3.12base has ca-certificates 20250419+deb13u1 andpip installresolves certifi 2026.10.19, so the next build of that image is fixed.Changes
uv.lock: certifi 2026.4.22 → 2026.10.19 (uv lock --upgrade-package certifiwith uv 0.13.4; no other package changed). uv 0.13.4 also raises the lockfilerevisionfrom 3 to 5; uv 0.12.1 still accepts the file withuv lock --locked.gcp/cloud_run/Dockerfile:ca-certificatesin the existingapt-get install, so every build takes Debian's current CA store even when the base image is older;GRPC_DEFAULT_SSL_ROOTS_FILE_PATH=/etc/ssl/certs/ca-certificates.crt, so gRPC loads that bundle instead of the list bundled in grpcio;ghcr.io/astral-sh/uv:0.13.4instead of 0.12.1.gcp/observability/collector/Dockerfileand the config validation step in.github/workflows/deploy-observability-collector.yml: otelcol-google 0.160.0 → 0.163.1..github/workflows/push.yml(versioning job) and.github/workflows/deploy-observability-collector.yml: setup-uvversion0.12.1 → 0.13.4.tests/unit/test_alembic_workflows.pyasserts the versioning job's uv version and is updated to match.changelog.d/emudhra-ca-trust-update.fixed.md.Testing
uv lock --lockedpasses with uv 0.13.4 and with uv 0.12.1.pytest tests/unit/test_alembic_workflows.py tests/unit/test_cloud_run_deploy_scripts.py: 142 passed.make format(no files changed),ruff check tests/unit/test_alembic_workflows.py: passed.python scripts/run_quality_guards.py: passed.docker build -f gcp/cloud_run/Dockerfile .(local, linux/arm64): succeeded. The image has ca-certificates 20250419+deb13u1, certifi 2026.10.19, grpcio 1.80.0, uv 0.13.4 andGRPC_DEFAULT_SSL_ROOTS_FILE_PATH=/etc/ssl/certs/ca-certificates.crt.CERTIFICATE_VERIFY_FAILEDagainstdv-g3b-emudhra.we-have-a-problem.comanddv-g3b-aaa-emudhra.we-have-a-problem.com, and both still reachwww.google.com.GRPC_DEFAULT_SSL_ROOTS_FILE_PATH: pointed at a file holding only ISRG Root X1, a call towww.google.com:443fails withCERTIFICATE_VERIFY_FAILED; with the image's setting, TLS completes tologging.googleapis.com,secretmanager.googleapis.com,telemetry.googleapis.comand arun.apphost (the probe RPC returnsUNIMPLEMENTEDfrom the server).python:3.12-slim@sha256:a6e34c59…,apt-get install --no-install-recommends ca-certificatesupgrades 20250419 to 20250419+deb13u1.otelcol-google:0.163.1 validate --config=…/config.yamlexits 0 (a missing config file exits 1 as a control).Operator actions after merge
Fixes #ISSUE_NUMBERas the first line of this description.ghcr.io/policyengine/policyengine. Watch that run until production is deployed. (The release run already in progress for 392e653 builds from the refreshedpython:3.12-slim, which fixes the operating-system store, but it still ships certifi 2026.4.22 and grpcio's bundled roots.)gcloud run services describe policyengine-api --project policyengine-api --region us-central1 --format='value(status.latestReadyRevisionName,status.traffic)', and compare the revision's image with the one the run pushed. Do the same forpolicyengine-api-staging.gcloud.auth.print-identity-token: Invalid account type for --audiences, run 39305297785), so confirm withgcloud run services describeon the collector service that its latest ready revision uses the image built from 0.163.1. If the workflow does not start, run it withworkflow_dispatch.Environment variables
This PR changes a Dockerfile, a container image and two workflows, so each variable the changed code reads or declares was traced:
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH(new)gcp/cloud_run/DockerfileENV; read by gRPC in the API processENV.deploy_cloud_run_candidate.shsets no variable of that name with--set-env-vars, so the image value applies at runtime. Confirmed in the local build.PYTHONDONTWRITEBYTECODE,PYTHONUNBUFFERED,UV_PROJECT_ENVIRONMENT,PATH,GATEWAY_AUTH_REQUIRED(unchanged)gcp/cloud_run/DockerfileENVENVOBSERVABILITY_PROJECT_ID,OBSERVABILITY_METRIC_LOCATIONconfig.yamlat runtimeproductionenvironment (OBSERVABILITY_PROJECT_IDalso at repository level)env:fromvars.*; the job declaresenvironment: production; passed to the validation container with--envand to the Cloud Run service with--set-env-varsOBSERVABILITY_COLLECTOR_REGION,OBSERVABILITY_COLLECTOR_SERVICE,OBSERVABILITY_COLLECTOR_ARTIFACT_REPOSITORY,OBSERVABILITY_COLLECTOR_IMAGE_NAME,OBSERVABILITY_COLLECTOR_SERVICE_ACCOUNT(unchanged)deploy_observability_collector.shproductionenvironmentenv:fromvars.*GCP_WORKLOAD_IDENTITY_PROVIDER,GCP_DEPLOY_SERVICE_ACCOUNT(unchanged)google-github-actions/authin the collector jobwith:inputsAPP_ID,APP_PRIVATE_KEY(unchanged)actions/create-github-app-tokenin the versioning jobwith:inputsOLD_SIMULATION_GATEWAY_URL,ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION(unchanged)push.ymlworkflow-levelenv:env:The setup-uv steps read no variables of their own beyond the default
github.tokenthat GitHub Actions provides.🤖 Generated with Claude Code