Skip to content

CI: harden pip against eMudhra/emSign CA incident - #413

Merged
leofang merged 9 commits into
NVIDIA:mainfrom
leofang:ci/emudhra-ca-incident
Nov 3, 2026
Merged

leofang merged 9 commits into
NVIDIA:mainfrom
leofang:ci/emudhra-ca-incident

Conversation

@leofang

@leofang leofang commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Summary

Responds to the eMudhra/emSign CA incident (advisory, CVE-2026-115557 for CPython / CVE-2026-111943 for the Windows host trust store). Moves the hardening one layer up from the hash-pinned pip bootstrap used in the first revision of this PR: setup-python now resolves a CPython whose ssl module blocks the compromised intermediates, and the composite only has to upgrade pip from there. Matches the shape of cupy/cupy#10438 and NVIDIA/cuda-python#3270 / #3275.

This repo has no Dockerfiles, no pixi.lock, no requirements*.txt, and no conda environment files, so the entire pip surface lives in GitHub Actions workflows and in-tree bash/PowerShell scripts.

Changes

  • .github/actions/update-pip (replaces harden-pip): a shell: python guard asserts sys.version_info is at or above the per-series patched floor (3.11.18, 3.12.16, 3.13.18, 3.14.10, 3.15.2), then pip install --upgrade pip. Adapted from cupy/cupy@main (comment-only edits + SPDX header added).
  • .github/actions/upgrade-ca-certificates: new composite action adapted from NVIDIA/cuda-python@main (comment-only edits dropping cuda-python-internal references, plus a # Adapted from attribution line). Carries the disclosure's impacted-intermediates.json byte-identical (SHA-256 f67b3abe9b4879f4e91874ed041beccd71257f1db41c588c724ccf12aa90c59e). Upgrades ca-certificates on GHA-hosted Linux and adds 16 compromised intermediates + Sectigo cross-signs to Cert:\LocalMachine\Disallowed on GHA-hosted Windows. No-op on self-hosted runners (per runner.environment).
  • .github/actions/install_unix_deps: refresh apt install ca-certificates, then verify closed-form that the stock package has emSign ECC Root CA - G3 removed. Fingerprint-matches each cert in /etc/ssl/certs/ca-certificates.crt against the G3 SHA-256 anchor extracted from certifi 2026.7.22; fails the job if G3 is still trusted or the bundle cannot be parsed. Replaces an earlier explicit distrust block that targeted the wrong root (eMudhra emSign Root CA - G1, which is not among the compromised anchors — only emSign Root TLS CA - G3, emSign Root CS CA - G3, and AAA Certificate Services cross-signs are, per impacted-intermediates.json). Both iterations originated from @juenglin's review of this PR.
  • setup-python sites:
    • Single-version: build-wheel.yml host → ~3.14.10 (lowest floor that resolves on both Linux and Windows via actions/python-versions); build-docs.yml → ~3.12.16.
    • Matrix: test-wheel-{linux,windows}.yml, build-llvm.yml win-arm64, and the four satellite test-*-linux.yml get check-latest: true so the manifest's newest patch per series resolves instead of a stale tool-cache entry.
  • build-llvm.yml (manylinux jobs): prepend $(dirname $PY) to GITHUB_PATH before update-pip, so the composite's shell: python picks up the manylinux interpreter rather than the runner's system python.
  • build-wheel.yml: drop the CIBW_MANYLINUX_X86_64_IMAGE and CIBW_MANYLINUX_AARCH64_IMAGE overrides so cibuildwheel 4.3.4 uses its own bundled SHA-pinned manylinux images instead of the floating quay.io/pypa/manylinux_2_28_* tags. Flagged by @juenglin.
  • publish.yaml: run upgrade-ca-certificates in each of the three ubuntu-latest jobs after actions/checkout, before any PyPI publish or gh release upload.
  • ci/windows-llvm-container-build.ps1: comment reference updated from harden-pip to update-pip; the hash-pinned bootstrap stays in place because the Windows devcontainer has no setup-python driving its Python.

Residual exposure

Windows 3.11 / 3.12 / 3.13 lanes in test-wheel-windows.yml will fail update-pip's floor assertion. actions/python-versions publishes no eMudhra-fixed Windows build for those three series (Windows ceilings today: 3.11.9, 3.12.10, 3.13.17 — all below the CVE-2026-115557 floor). Tracked upstream at actions/setup-python#1383. The 3.14 and 3.15 Windows lanes resolve cleanly to 3.14.10 and 3.15.2.

The update-pip step itself carries continue-on-error scoped to those three matrix.PY_VER values, so the job reports green with an annotation on the failed Update pip step rather than blocking merge; the downstream install / CTK / pytest steps still fail the job hard if anything else breaks on the unpatched interpreter. Pip is already at 26.2.2 by that point because setup-python's own "Install and upgrade Pip" sub-step runs ahead of our composite, so skipping update-pip does not leave an older pip on PATH. Drop the override once that issue ships patched Windows builds.

Partial compensating controls on affected Windows lanes:

  • upgrade-ca-certificates is a no-op on self-hosted Windows (runner.environment != 'github-hosted'), so the OS trust store refresh for test-wheel-windows.yml depends on nv-gha-runners-team-owned image management.
  • pip 26.2.2+ via truststore continues to protect pip install against the chain, independent of the ssl module patch.

Not covered

  • Externally-owned actions still on mutable refs: nv-gha-runners/setup-proxy-cache@main, nv-gha-runners/get-pr-info@main, aws-actions/configure-aws-credentials@v6. Not pip-related; pinning them is a separate hygiene PR.
  • simulator-test.yaml pixi v0.63.2 is below the advisory floor (0.82.1+). Orphan workflow (not referenced from ci.yaml), manual-dispatch only. Deferred.
  • rapidsai/devcontainers:26.06-cuda13.0-cl14.44 (Windows devcontainer used by build-llvm.yml's Windows path) and the external quay.io/pypa/manylinux_2_28_* images are externally owned; mitigation flows through update-pip's floor assertion inside the container.
  • github.com clones of LLVM, sccache, and miniforge, and developer.download.nvidia.com CTK redistrib downloads — chains unaffected by eMudhra.

Addressed from @juenglin's reviews

  1. install_unix_deps apt ca-certificates CA refresh — Ralf's first review (review of 4f45dec..aa1224b) flagged the apt install as a no-op on 22.04 and asked for an explicit distrust; the follow-up review on 3972fcf retracted that after verifying the stock 20260816ubuntu0.22.04.1 package already removes emSign ECC Root CA - G3. The committed logic verifies closed-form that G3 is absent from /etc/ssl/certs/ca-certificates.crt instead of distrusting an unrelated root. Verified in ubuntu:22.04 under bash --noprofile --norc -xeuo pipefail across three scenarios (stock package → pass; pre-fix certifi 2026.7.22 G3 PEM re-injected via /usr/local/share/ca-certificates → fail; unparseable bundle → fail).
  2. ci/tools/run-expensive-tests venv's pip is not hardened — resolved transitively: the venv now inherits from a setup-python-provided interpreter at or above 3.11.18/3.12.16/3.13.18/3.14.10/3.15.2, whose bundled ensurepip wheel is already pip 26.2.2+.
  3. CIBW_MANYLINUX_*_IMAGE overrides — dropped from build-wheel.yml so cibuildwheel 4.3.4's bundled SHA-pinned images are used.
  4. simulator-test.yaml pixi v0.63.2 — deferred (see Not covered); Ralf confirmed OK in the follow-up review.
  5. Earlier description claims that update-pip and upgrade-ca-certificates were byte-identical to upstream — corrected to "adapted from" (both differ in comments only, and update-pip also carries an SPDX header). impacted-intermediates.json remains byte-identical.
  6. Stale cross-reference comments — removed from update-pip/action.yml (the file no longer claims the floors also live in ci/versions.yml, which does not carry them) and from ci/windows-llvm-container-build.ps1 (which no longer says to keep its pinned pip URL in sync with update-pip/action.yml, since the composite no longer carries a URL).

Companion PRs

Test plan

  • All touched YAML files parse cleanly.
  • .github/actions/update-pip/action.yml differs from cupy/cupy@main only in the SPDX header and a comment edit (verified with diff -u).
  • .github/actions/upgrade-ca-certificates/action.yml and emudhra_intermediates.ps1 differ from NVIDIA/cuda-python@main only in comments; impacted-intermediates.json is byte-identical (SHA-256 verified).
  • cibuildwheel SHA b9e566a2b284570af229f6c2a33673410d0389a3 is v4.3.4 upstream.
  • gh-action-pypi-publish SHA 7245906fb61fa33a13554f9d5fb856fe61758086 is v1.14.3 upstream.
  • install_unix_deps G3-absence check passes on fresh ubuntu:22.04 (120 certs checked, G3 absent) and fails closed when the pre-fix certifi 2026.7.22 G3 PEM is re-injected into the trust store or when the bundle is unparseable.
  • CI: build-llvm succeeds across linux-64, linux-aarch64, win-64, win-arm64 with the manylinux PATH prepend and check-latest: true for the win-arm64 host Python.
  • CI: build-wheel succeeds without CIBW_MANYLINUX_*_IMAGE overrides (cibuildwheel 4.3.4 bundled images). Host runs on ~3.14.10 on both Linux and Windows.
  • CI: build-docs succeeds on ~3.12.16 after upgrade-ca-certificates.
  • CI: test-wheel-linux + 4 satellite test-*-linux.yml + test-numbast-mlir-linux.yml succeed with install_unix_deps doing the apt refresh + G3 fingerprint check inside the ubuntu containers.
  • CI: test-wheel-windows 3.14 and 3.14t / 3.15 and 3.15t lanes succeed. 3.11 / 3.12 / 3.13 lanes expected to fail update-pip's guard (step-scoped continue-on-error, downstream still fails hard) pointing at actions/setup-python#1383.
  • publish.yaml is validated on next release dispatch (not exercisable from a PR run).

-- Leo's bot

Responds to the eMudhra/emSign CA incident
(https://discuss.python.org/t/109576). Older pip releases trust the
compromised intermediates via their vendored certifi, and apt's
ca-certificates in Ubuntu test containers can be stale enough to keep
the roots trusted at the OS level too.

- Add .github/actions/harden-pip composite action (hash-pinned pip 26.2.2
  bootstrap + upgrade), copied from cupy/cupy and NVIDIA/cuda-python.
- Wire it into every workflow with an in-tree pip install: build-llvm
  (3 jobs), build-wheel, build-docs, test-wheel-{linux,windows}, and the
  four satellite test workflows. The pin is also inlined in
  ci/windows-llvm-container-build.ps1, which runs inside the Windows
  devcontainer where composite actions can't reach it.
- Bump pypa/cibuildwheel to v4.3.4 (SHA b9e566a2); SHA-pin
  pypa/gh-action-pypi-publish to v1.14.3 (7245906f).
- Modify .github/actions/install_unix_deps to always refresh
  ca-certificates before the dependent-exes short-circuit fires, so
  Ubuntu test containers' system trust stores stay current regardless of
  which deps the caller asked for.

Not covered: externally-owned actions still on mutable refs
(nv-gha-runners/setup-proxy-cache@main, nv-gha-runners/get-pr-info@main,
aws-actions/configure-aws-credentials@v6), the simulator-test workflow
(uses pixi + rapidsai/ci-conda, externally managed, not referenced from
ci.yaml), and the GitHub-hosted miniforge/sccache/LLVM-source clones
(github.com's chain is unaffected).
@copy-pr-bot

copy-pr-bot Bot commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@leofang

leofang commented Oct 27, 2026

Copy link
Copy Markdown
Member Author

/ok to test 4f45dec

@leofang

leofang commented Oct 27, 2026

Copy link
Copy Markdown
Member Author

cc @juenglin for vis

@leofang
leofang marked this pull request as ready for review October 27, 2026 16:35
@github-actions

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review.

Walkthrough

important: The pull request adds a composite action that installs pip 26.2.2 from a SHA-256-pinned URL before upgrading pip. It adds the action to CI workflows, changes Unix dependency setup, updates the Windows LLVM build script, and revises two workflow action references.

Changes: Pip hardening and CI updates

Layer / File(s) Summary
Pinned pip bootstrap and Unix dependency setup
.github/actions/harden-pip/action.yml, .github/actions/install_unix_deps/action.yml, ci/windows-llvm-container-build.ps1
The composite action and Windows build script install pip 26.2.2 from a SHA-256-pinned URL before upgrading pip. Unix dependency setup configures sudo, updates apt, and installs ca-certificates before checking requested executables.
CI workflow integration and action references
.github/workflows/build-docs.yml, .github/workflows/build-llvm.yml, .github/workflows/build-wheel.yml, .github/workflows/test-*.yml, .github/workflows/publish.yaml
Documentation, LLVM, wheel, and test workflows invoke the local harden-pip action. LLVM jobs run it on cache misses; Linux LLVM jobs pass their selected Python interpreter. The wheel workflow updates pypa/cibuildwheel to v4.3.4, and the publishing workflow pins its action to a commit SHA.

Suggested reviewers: atmnp

Change: Other

Merge Risk: Minimal · previous review aa122

No actionable merge-blocking risk remains in the reviewed scope.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 4f45dec and aa1224b.

Files selected for processing: 1
  • ci/windows-llvm-container-build.ps1

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


Review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between bd28aef and 4f45dec.

Files selected for processing: 13
  • .github/actions/harden-pip/action.yml
  • .github/actions/install_unix_deps/action.yml
  • .github/workflows/build-docs.yml
  • .github/workflows/build-llvm.yml
  • .github/workflows/build-wheel.yml
  • .github/workflows/publish.yaml
  • .github/workflows/test-cccl-compute-mlir-linux.yml
  • .github/workflows/test-cudf-source-linux.yml
  • .github/workflows/test-expensive-linux.yml
  • .github/workflows/test-nvmath-python-mlir-linux.yml
  • .github/workflows/test-wheel-linux.yml
  • .github/workflows/test-wheel-windows.yml
  • ci/windows-llvm-container-build.ps1

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

Comment thread ci/windows-llvm-container-build.ps1 Resolved
PowerShell 5.1's $ErrorActionPreference = 'Stop' only applies to cmdlets;
native commands like python.exe fall through without terminating. If the
hash-pinned pip 26.2.2 install silently fails, the next upgrade line runs
with the vulnerable pip and a later successful command overwrites
$LASTEXITCODE, so the script and workflow both report success. Exit
immediately on nonzero $LASTEXITCODE after each pip call to close that gap.

Reported by CodeRabbit on PR NVIDIA#413.
@leofang

leofang commented Oct 27, 2026

Copy link
Copy Markdown
Member Author

/ok to test aa1224b

@juenglin juenglin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review findings

1. install_unix_deps: the apt install ca-certificates is a no-op today and leaves the compromised CA trusted

In the PR's CI run, apt reports ca-certificates is already the newest version (20260816ubuntu0.22.04.1). That package predates the incident, and Ubuntu 22.04 has not published a fixed build yet, so upgrading it does nothing. The compromised emSign ECC Root CA - G3 (and the other emSign roots) remain in the system trust bundle. The apt install line is worth keeping so that a future Ubuntu 22.04 security update is picked up automatically, but it needs to be paired with an explicit distrust step that works today.

Distrusting a cert on Debian/Ubuntu means prefixing its line in /etc/ca-certificates.conf with ! and then running update-ca-certificates. The grep -qxF guard makes the whole block a no-op when the entry is already absent or already deactivated:

# Explicitly distrust the compromised eMudhra/emSign root CAs.
# Prefixing with '!' in ca-certificates.conf deactivates the cert;
# the grep guard makes this a no-op when already absent.
# Verify the exact filenames against the installed package with:
#   dpkg -L ca-certificates | grep emSign
_changed=0
for cert in \
    "mozilla/emSign_ECC_Root_CA_-_G3.crt" \
    "mozilla/emSign_Root_CA_-_G1.crt"; do
    if grep -qxF "${cert}" /etc/ca-certificates.conf 2>/dev/null; then
        SUDO sed -i "s|^${cert}$|!${cert}|" /etc/ca-certificates.conf
        echo "Distrusted: ${cert}"
        _changed=1
    fi
done
if [[ ${_changed} -eq 1 ]]; then
    SUDO update-ca-certificates
fi

This should go right after the SUDO apt install -y ca-certificates call. Note: the cert filenames above are my best reconstruction from the Mozilla bundle naming convention — please verify them with dpkg -L ca-certificates | grep -i emSign on an affected container before committing.

2. ci/tools/run-expensive-tests: the venv's pip is not hardened

run-expensive-tests creates a fresh venv and then upgrades pip using the venv's own bundled pip, which may predate 26.2.2:

python -m venv "${VENV_DIR}"
PIP="${VENV_DIR}/bin/pip"
"${PIP}" install --upgrade pip

The hardened pip installed by the harden-pip action is not inherited by venvs; each new venv gets a copy from the creating interpreter's bundled wheels. Replace the upgrade step with the same hash-pinned bootstrap:

"${PIP}" install --disable-pip-version-check \
  "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"
"${PIP}" install --disable-pip-version-check --upgrade pip

The PR's CI run happened to pass because the runner carried Python 3.13.18, whose bundled pip is already 26.2.2. A job on an older Python would silently undo the fix.

3. build-wheel.yml: the CIBW_MANYLINUX_*_IMAGE overrides bypass the fixed images that cibuildwheel 4.3.4 pins

The bump to cibuildwheel 4.3.4 is correct, but the workflow hard-codes untagged image references:

CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux_2_28_x86_64
CIBW_MANYLINUX_AARCH64_IMAGE: quay.io/pypa/manylinux_2_28_aarch64

Those resolve to :latest at pull time, bypassing the specific SHA-pinned images that 4.3.4 ships with. The PR description's statement that "the bump picks up the fixed ones" is only true if these overrides are removed or replaced with digest-pinned references (image@sha256:...). The simplest fix is to drop both lines and let cibuildwheel use its own bundled image pins.

4. simulator-test.yaml: pixi v0.63.2 is below the advisory floor

pixi-version: "v0.63.2" — the advisory requires 0.82.1 or later. I understand this workflow is not wired into ci.yaml, but it is triggerable via manual dispatch, which is enough exposure to warrant a bump.

5. Description inaccuracies

  • "copied byte-for-byte" / "byte-identical in semantics": the action in this PR differs from the cuda-python and cupy copies in at least three ways: it adds an unpinned pip install --upgrade pip second step (the others do not), it uses shell: bash instead of bash --noprofile --norc -xeuo pipefail {0}, and it uses the env-var name PYTHON where cuda-python uses HARDEN_PIP_PYTHON.
  • "the bump picks up the fixed ones" for cibuildwheel images: see point 3.

Follow-up on review of NVIDIA#413 (and matching cupy/cupy#10438 +
NVIDIA/cuda-python#3270 + #3275). The hash-pinned pip bootstrap moves
one layer up: setup-python resolves a CPython whose ssl module blocks
the compromised eMudhra/emSign CAs (CVE-2026-115557), and the composite
only has to upgrade pip from there.

- Rename .github/actions/harden-pip -> update-pip. New body: shell:python
  guard asserts sys.version_info is at or above the per-series patched
  floor (3.11.18, 3.12.16, 3.13.18, 3.14.10, 3.15.2), then pip install
  -U pip. Copied from cupy/cupy@main.
- Add .github/actions/upgrade-ca-certificates composite (plus
  emudhra_intermediates.ps1 and impacted-intermediates.json, verbatim
  SHA-verified from NVIDIA/cuda-python@main). Upgrades
  ca-certificates on GHA-hosted Linux; distrusts 16 compromised
  intermediates on GHA-hosted Windows. No-op on self-hosted.
- install_unix_deps: pair the apt ca-certificates install with an
  explicit distrust via /etc/ca-certificates.conf + update-ca-certificates.
  The apt install stays so a future Ubuntu 22.04 security update is
  picked up automatically, but today it is a no-op there and the roots
  remain trusted without the explicit distrust (Juengling, review of NVIDIA#413).
- Every setup-python call: tilde-range floor on single-version sites
  (build-wheel.yml host -> ~3.14.10, build-docs.yml -> ~3.12.16),
  check-latest: true on matrix sites so the newest patch resolves.
- build-llvm.yml: prepend the manylinux Python's bin dir to PATH so the
  update-pip action's shell: python picks it up.
- build-wheel.yml: drop CIBW_MANYLINUX_X86_64_IMAGE and
  _AARCH64_IMAGE overrides so cibuildwheel 4.3.4 uses its own bundled
  SHA-pinned manylinux images instead of the floating quay.io tags
  (Juengling, review of NVIDIA#413).
- publish.yaml: run upgrade-ca-certificates in each ubuntu-latest job
  after actions/checkout, before any PyPI or gh release upload.

Windows 3.11/3.12/3.13 wheel-test lanes will fail update-pip's guard
because actions/python-versions ships no eMudhra-fixed Windows build
for those three series (3.11.9, 3.12.10, 3.13.17 are the ceilings);
tracked upstream at actions/setup-python#1383.
The disclosure's impacted-intermediates.json is committed unmodified
(SHA-256 f67b3abe9b4879f4e91874ed041beccd71257f1db41c588c724ccf12aa90c59e)
so the SHA stays verifiable against the eMudhra disclosure. Mirrors
cuda-python's end-of-file-fixer exclude on the same file.
@leofang

leofang commented Nov 1, 2026

Copy link
Copy Markdown
Member Author

/ok to test d092e8a

* `build-docs.yml`: run `actions/checkout` before the local
  `upgrade-ca-certificates` composite; the composite file does not exist
  on the runner until checkout has populated the repo.
* `test-wheel-windows.yml`: scope `continue-on-error` to the Windows
  3.11/3.12/3.13 matrix entries while
  actions/setup-python#1383 (missing patched
  Windows binaries for the 3.11.18 / 3.12.16 / 3.13.18 security releases)
  is open. Other Windows and non-Windows lanes still fail hard.
Narrows the tolerance introduced by the previous commit from the whole
Windows test job to just the `update-pip` composite step. The downstream
install, CTK fetch, and pytest steps still fail the job if they break on
an unpatched Windows interpreter — the earlier job-level override would
have swept any such breakage under the rug.
@leofang

leofang commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

/ok to test 3972fcf

@juenglin juenglin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the rework, @leofang. Items 2, 3, and 5 from my earlier review look addressed, and deferring 4 is fine with me. But I got item 1 wrong, and the distrust block I suggested should come out again.

1. install_unix_deps: drop the distrust block (my earlier diagnosis was wrong)

I claimed that Ubuntu 22.04's ca-certificates 20260816ubuntu0.22.04.1 predates the incident and still trusts emSign ECC Root CA - G3. That's not true. The date in the version is the Mozilla bundle date, and the package already removes G3:

  • In a fresh ubuntu:22.04 container with that package, G3 has no entry in /etc/ca-certificates.conf, no file under /usr/share/ca-certificates/mozilla/, and is not among the 120 certs in /etc/ssl/certs/ca-certificates.crt (checked by SHA-256 fingerprint).
  • The CI logs for 3972fcf agree: the grep for mozilla/emSign_ECC_Root_CA_-_G3.crt never matches, and the only thing the block does is Distrusted: mozilla/emSign_Root_CA_-_G1.crt (0 added, 1 removed).
  • emSign Root CA - G1 was an unverified guess on my part. It isn't part of the incident, and Ubuntu keeps it trusted.

So the block is a no-op for G3 and distrusts an unrelated root. I'd replace it with a fail-closed check, so the job verifies the stock package's fix instead of assuming it. upgrade-ca-certificates can't be reused here because it is a no-op on self-hosted runners. This uses the same fingerprint and parsing approach:

        SUDO apt update
        SUDO apt install -y ca-certificates

        # Fail closed if the compromised emSign ECC Root CA - G3 is still in the
        # system bundle. On Ubuntu it is the only trusted anchor for the
        # compromised eMudhra intermediates. See https://discuss.python.org/t/109576.
        EMSIGN_G3_FP="86:A1:EC:BA:08:9C:4A:8D:3B:BE:27:34:C6:12:BA:34:1D:81:3E:04:3C:F9:E8:A8:62:CD:5C:57:A3:6B:BE:6B"
        CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt"
        fps="$(awk '/-----BEGIN CERTIFICATE-----/{cert=""} {cert=cert $0 "\n"}
                    /-----END CERTIFICATE-----/{print cert | "openssl x509 -noout -fingerprint -sha256"; close("openssl x509 -noout -fingerprint -sha256")}' \
               "${CA_BUNDLE}")"
        if [[ -z "${fps}" ]]; then
          echo "::error::No certificates parsed from ${CA_BUNDLE}"
          exit 1
        fi
        if grep -qiF "${EMSIGN_G3_FP}" <<< "${fps}"; then
          echo "::error::emSign ECC Root CA - G3 is still trusted (ca-certificates $(dpkg-query -W -f='${Version}' ca-certificates))"
          exit 1
        fi
        echo "emSign ECC Root CA - G3 absent from ${CA_BUNDLE} ($(grep -c . <<< "${fps}") certs checked)."

This replaces everything from the _ca_changed=0 line through the update-ca-certificates call. openssl is always present at that point because Ubuntu's ca-certificates depends on it.

I tested it in ubuntu:22.04 under the action's bash --noprofile --norc -xeuo pipefail shell:

  • Stock package: exits 0 and prints that G3 is absent, with 120 certs checked.
  • G3 re-added through /usr/local/share/ca-certificates (PEM taken from the pre-fix certifi 2026.7.22, fingerprint matches the constant): exits 1 with emSign ECC Root CA - G3 is still trusted.
  • Unparseable bundle: exits 1 with No certificates parsed.

Description and comment nits

  • The install_unix_deps bullet and item 1 under "Addressed from @juenglin's review" say 22.04 has no fixed ca-certificates. Please update both to match the above. That claim came from my review.
  • update-pip is described as byte-identical to cupy/cupy@main, and upgrade-ca-certificates as byte-identical to NVIDIA/cuda-python@main. Both differ in comments only (and the SPDX header for update-pip), so "adapted from" would be more accurate.
  • "Residual exposure" says the Windows 3.11–3.13 lanes report red. They report green: step-level continue-on-error makes the job succeed, and the failure only shows as an annotation. I checked the 3.11.9 log: setup-python had already upgraded pip from 24.0 to 26.2.2 there, so skipping the Upgrade pip step doesn't leave an old pip behind.
  • Two comments point to things that don't exist in this repo. update-pip/action.yml says the patch floors also appear in ci/versions.yml, and ci/windows-llvm-container-build.ps1 says to keep its pinned URL in sync with update-pip/action.yml, which no longer contains a URL.

Ralf (@juenglin) pointed out that my earlier `install_unix_deps` block
was targeting the wrong roots:

* On a fresh `ubuntu:22.04` image, the stock `ca-certificates
  20260816ubuntu0.22.04.1` already removes `emSign ECC Root CA - G3`
  from both `/etc/ca-certificates.conf` and the shipped PEM store, so the
  G3 line in the loop was dead code.
* The G1 line, meanwhile, was deactivating `emSign Root CA - G1` — an
  eMudhra root that is *not* among the compromised anchors. Every
  intermediate in `impacted-intermediates.json` chains to one of
  `emSign Root TLS CA - G3`, `emSign Root CS CA - G3`, or
  `AAA Certificate Services` (Comodo cross-signs). G1 is unrelated.

The replacement verifies the stock package's own fix instead: parse
`/etc/ssl/certs/ca-certificates.crt` with `openssl x509 -fingerprint
-sha256` and fail closed if the G3 SHA-256 fingerprint (checked against
the pre-fix `certifi==2026.7.22` PEM) is still present. The three
tested scenarios all behave:

* stock `ubuntu:22.04` → "G3 absent, 120 certs checked" and `exit 0`.
* G3 re-injected via `/usr/local/share/ca-certificates` from the pre-fix
  certifi PEM → `::error::emSign ECC Root CA - G3 is still trusted` and
  `exit 1`.
* empty / unparseable bundle → `::error::No certificates parsed` and
  `exit 1`.

Also drops two stale comments Ralf flagged:
* `update-pip/action.yml`: the "patch floors appear in ci/versions.yml"
  claim; the floor values no longer live there.
* `windows-llvm-container-build.ps1`: the "keep the URL in sync with
  update-pip/action.yml" line; update-pip no longer carries any URL.
@leofang
leofang requested a review from juenglin November 3, 2026 02:21
@leofang

leofang commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

/ok to test a51017c

@juenglin juenglin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@leofang

leofang commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Thanks, Ralf!

@atmnp this is ready!

@atmnp
atmnp self-requested a review November 3, 2026 16:43
@leofang
leofang merged commit aadb8df into NVIDIA:main Nov 3, 2026
347 of 349 checks passed
@leofang
leofang deleted the ci/emudhra-ca-incident branch November 3, 2026 16:58
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Doc Preview CI
Preview removed because the pull request was closed or merged.

atmnp added a commit that referenced this pull request Nov 10, 2026
Close the Windows interpreter gap left by #413. setup-python cannot
provide the patched maintenance releases without upstream Windows
installers, as confirmed in actions/setup-python#1383.

Add setup-hardened-python using SHA-pinned setup-uv and checksum-verified
python-build-standalone distributions. Create a venv and verify the
interpreter before upgrading pip.

Share the security-release guard with update-pip and the Windows LLVM
container installer. Replace NuGet provisioning and its pinned pip
bootstrap, and remove the tolerated guard failures in Windows tests.

Keep cibuildwheel for wheel builds and pass the host interpreter
explicitly. Track the nested setup-uv action pin with Dependabot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants