Skip to content

fix(deps): distrust the compromised eMudhra CAs (0.8.1) - #43

Merged
christianhuening merged 1 commit into
mainfrom
fix/emudhra-ca-distrust
Oct 21, 2026
Merged

christianhuening merged 1 commit into
mainfrom
fix/emudhra-ca-distrust

Conversation

@christianhuening

Copy link
Copy Markdown
Contributor

Summary

Security-only 0.8.1. The private keys of eight eMudhra intermediate CAs are public (incident), and knot's outbound TLS trusted chains through them: the roots are compiled into the binary, and rustls checks neither revocation nor CT, so eMudhra's revocation and the browser blocks don't reach knot. This also clears the other advisories that turned cargo deny red on main.

 Cargo.lock
-rustls-webpki 0.103.14   # RUSTSEC-2026-0382: accepts chains through the leaked intermediates
+rustls-webpki 0.103.16
-webpki-roots  1.0.9      # RUSTSEC-2026-0381: ships emSign ECC Root CA - G3
+webpki-roots  1.0.10
-rustls        0.23.45    # RUSTSEC-2026-0346..0349
+rustls        0.23.46
-ammonia       4.1.7      # RUSTSEC-2026-0373 (not reachable: no `style` attrs); stays on 4.1
+ammonia       4.1.8

The lockfile has a single rustls-webpki, and every rustls client verifies through it:

knot-server
├── OIDC (openidconnect → reqwest)       exposed: forged JWKS at startup → sign in as anyone;
│                                                 client secret + auth codes at each login
├── S3 blob backend (rust-s3)            exposed
├── main Postgres pool (sqlx-core)       exposed: sqlx seeds webpki-roots even when sslrootcert is set
└── PgBus (tokio-postgres-rustls)        not exposed: empty root store + sslrootcert only

deny.toml acknowledges RUSTSEC-2026-0367/0368/0369 (zip extract() symlink/SUID issues). The fix is only in zip 9, and knot never extracts: import reads the archive in memory by entry name. CHANGELOG.md gets the 0.8.1 section.

Evidence

  • Before (main @ 9a1fedb): cargo deny check advisories → advisories FAILED with RUSTSEC-2026-0381, -0382, -0346, -0347, -0348, -0349, -0373, -0367, -0368, -0369
    After: cargo deny check → advisories ok, bans ok, licenses ok, sources ok
  • make lint: clean
  • cargo nextest run --workspace --all-features: 441 passed, 2 skipped. Vitest: 405 passed.
  • contributors_integration::revoked_editor_is_closed_with_4403_and_its_edits_are_dropped is flaky, but not because of this change: 15/30 failures on main, 11/30 on this branch, and it already failed main CI on 2026-10-13 (run 38418308210). If CI trips on it, re-run.

Merge Danger

Door: two-way

Reverting restores the old lockfile. Publishing the v0.8.1 image afterwards is the part that can't be taken back.

Blast Radius: outbound TLS

webpki-roots 1.0.10 drops emSign ECC Root CA - G3, so a deployment whose OIDC provider, S3 endpoint or Postgres presents a chain to that root stops connecting (noted under Upgrading). No knot code changes.

Follow-ups, not in this PR: sqlx trusting the public roots alongside sslrootcert for the main pool (PgBus already does it right), and the zip 9 migration.

🤖 Generated with Claude Code

The private keys of eight eMudhra intermediate CAs can be recovered from
their public certificates. knot's outbound TLS (OIDC, the S3 blob backend
and the sqlx Postgres pool) trusted chains through them: webpki-roots 1.0.9
ships emSign ECC Root CA - G3, which cross-signs their issuer, and rustls
checks neither revocation nor Certificate Transparency.

- rustls-webpki 0.103.16 rejects the intermediates' keys
  (RUSTSEC-2026-0382).
- webpki-roots 1.0.10 drops emSign ECC Root CA - G3 (RUSTSEC-2026-0381).
- rustls 0.23.46 fixes RUSTSEC-2026-0346 through RUSTSEC-2026-0349.
- ammonia 4.1.8 fixes RUSTSEC-2026-0373. Not reachable: the public share
  page uses ammonia's default builder, which strips style attributes. Kept
  on 4.1 rather than 4.2.4, which would pull a new html5ever stack into a
  patch release.
- RUSTSEC-2026-0367, -0368 and -0369 (zip extract() symlink and SUID
  handling) are ignored in deny.toml with path, reachability and drop
  condition. The fix is only in zip 9, and knot never extracts: import
  reads the archive in memory by entry name.

Adds the 0.8.1 changelog section.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@christianhuening
christianhuening merged commit 124ce90 into main Oct 21, 2026
5 checks passed
@christianhuening
christianhuening deleted the fix/emudhra-ca-distrust branch October 21, 2026 20:45
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.

1 participant