From 5d72e55e70f9d91d422b5e91ce3b855b242c4a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20H=C3=BCning?= Date: Wed, 21 Oct 2026 22:13:50 +0200 Subject: [PATCH] fix(deps): distrust the compromised eMudhra CAs 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) --- CHANGELOG.md | 35 ++++++++++++++++++++++++++++++++++- Cargo.lock | 16 ++++++++-------- deny.toml | 14 ++++++++++++++ 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9170f93..1a471942 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,38 @@ so this log can be regenerated from history (e.g. with `git-cliff`). ## [Unreleased] +## [0.8.1] - 2026-10-21 + +### Upgrading +- knot no longer trusts the `emSign ECC Root CA - G3` root certificate. If your + OIDC provider, S3 endpoint or Postgres server presents a certificate that + chains to it, knot can no longer connect to it. Chrome is removing the same + root. (#43) + +### Security +- **eMudhra CA key compromise** (RUSTSEC-2026-0381, RUSTSEC-2026-0382). The + private keys of eight eMudhra intermediate CAs can be recovered from their + public certificates, so anyone can issue a certificate for any hostname that + knot accepted. This affected knot's connections to the OIDC provider, the S3 + blob backend and Postgres. An attacker in the network path to the OIDC + provider could substitute their own signing keys when knot starts and then + sign in as any user, or capture the client secret and users' authorization + codes at login. knot's trusted roots are compiled into the binary and it + checks neither revocation nor Certificate Transparency, so eMudhra's + revocation and the browser blocks did not protect it, and there is no CA + bundle to update: upgrading is the only fix. `rustls-webpki` 0.103.16 + rejects chains through the eight intermediates and `webpki-roots` 1.0.10 + drops the root. The Postgres change-notification connection was not + affected: it trusts only `sslrootcert`. (#43) +- `rustls` 0.23.46 fixes RUSTSEC-2026-0346, RUSTSEC-2026-0347, + RUSTSEC-2026-0348 and RUSTSEC-2026-0349. (#43) +- `ammonia` 4.1.8 fixes RUSTSEC-2026-0373. knot was not affected: its + sanitizer does not allow `style` attributes. (#43) +- RUSTSEC-2026-0367, RUSTSEC-2026-0368 and RUSTSEC-2026-0369 (`zip` + extraction) are acknowledged in `deny.toml`. knot was not affected: it reads + import archives in memory and never extracts them to disk. The fix needs + zip 9. (#43) + ## [0.8.0] - 2026-10-13 ### Upgrading @@ -559,7 +591,8 @@ First tagged release. Feature-complete for single-workspace teams. - Helm chart with migrate hook, NetworkPolicy, ServiceMonitor, PrometheusRule, and multi-arch (amd64 + arm64) scratch image. -[Unreleased]: https://github.com/opendefensecloud/knot/compare/v0.8.0...HEAD +[Unreleased]: https://github.com/opendefensecloud/knot/compare/v0.8.1...HEAD +[0.8.1]: https://github.com/opendefensecloud/knot/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/opendefensecloud/knot/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/opendefensecloud/knot/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/opendefensecloud/knot/compare/v0.5.0...v0.6.0 diff --git a/Cargo.lock b/Cargo.lock index 86f2ee24..0d74bd90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "ammonia" -version = "4.1.7" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36322ccf536de7b7afd259098615203e4cc5caca1b78482b58334a720c66e40a" +checksum = "2f6660fb4ff692ef4937b1b4e74ab3d378653d8588389587d91f96a296907ea0" dependencies = [ "cssparser", "html5ever", @@ -3517,9 +3517,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.45" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "aws-lc-rs", "log", @@ -3555,9 +3555,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" dependencies = [ "aws-lc-rs", "ring", @@ -5105,9 +5105,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +checksum = "1c1c8e20c8a38fc7d65abfe74eb7280e4aa667e8eec8bc84996d2874810748d9" dependencies = [ "rustls-pki-types", ] diff --git a/deny.toml b/deny.toml index c6dcc852..dbab2860 100644 --- a/deny.toml +++ b/deny.toml @@ -50,6 +50,20 @@ ignore = [ # Drop when: figment moves its Yaml provider off serde_yaml, or knot drops # YAML config files. { id = "RUSTSEC-2024-0449", reason = "unsafe-libyaml unmaintained; via figment's Yaml provider (serde_yaml); parses only the operator's KNOT_CONFIG file; no vulnerability" }, + + # zip < 9 extracts archives unsafely: ZipArchive::extract and + # ZipStreamReader::extract keep SUID/SGID bits, create symlinks whose + # targets lie outside the destination, and apply permissions through them. + # zip is a DIRECT dependency of knot-server (workspace export/import), + # pinned at ^8; the fixes exist only in the 9.0 major, which Renovate raises + # from the Dependency Dashboard rather than on its own. + # Reachability: none. Import opens the upload with ZipArchive::new over an + # in-memory buffer and reads entries by name; export only writes through + # ZipWriter. Nothing calls extract() or writes to the filesystem. + # Drop when: knot-server moves to zip >= 9. + { id = "RUSTSEC-2026-0367", reason = "zip extract() keeps SUID/SGID bits; fix only in zip 9; knot reads archives in memory and never extracts" }, + { id = "RUSTSEC-2026-0368", reason = "zip extract() symlinks escape the destination; fix only in zip 9; knot reads archives in memory and never extracts" }, + { id = "RUSTSEC-2026-0369", reason = "zip extract() chmods through symlinks; fix only in zip 9; knot reads archives in memory and never extracts" }, ] [licenses]