From 9edc0b393a281812e91c5c355c7be0663897d772 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 6 Dec 2023 01:39:13 +0000 Subject: [PATCH] feat(tls): add crate feature for rustls native root certs (#506) --- .cargo/config.toml | 18 ++++++++-------- actix-tls/CHANGES.md | 3 ++- actix-tls/Cargo.toml | 11 ++++++++-- actix-tls/src/connect/mod.rs | 15 ++++++++++--- actix-tls/src/connect/rustls_0_20.rs | 32 +++++++++++++++++++++++----- actix-tls/src/connect/rustls_0_21.rs | 32 +++++++++++++++++++++++----- justfile | 4 ++-- 7 files changed, 88 insertions(+), 27 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index a114083f..bade4d02 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -6,20 +6,20 @@ ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocaptur # just check the library (without dev deps) ci-check-min = "hack --workspace check --no-default-features" -ci-check-lib = "hack --workspace --feature-powerset --exclude-features=io-uring check" -ci-check-lib-linux = "hack --workspace --feature-powerset check" +ci-check-lib = "hack --workspace --feature-powerset --depth=3 --exclude-features=io-uring check" +ci-check-lib-linux = "hack --workspace --feature-powerset --depth=3 check" # check everything -ci-check = "hack --workspace --feature-powerset --exclude-features=io-uring check --tests --examples" -ci-check-linux = "hack --workspace --feature-powerset check --tests --examples" +ci-check = "hack --workspace --feature-powerset --depth=3 --exclude-features=io-uring check --tests --examples" +ci-check-linux = "hack --workspace --feature-powerset --depth=3 check --tests --examples" # tests avoiding io-uring feature -ci-test = "hack --feature-powerset --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture" -ci-test-rustls-020 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_21 test --lib --tests --no-fail-fast -- --nocapture" -ci-test-rustls-021 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture" +ci-test = "hack --feature-powerset --depth=3 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture" +ci-test-rustls-020 = "hack --feature-powerset --depth=3 --exclude-features=io-uring,rustls-0_21 test --lib --tests --no-fail-fast -- --nocapture" +ci-test-rustls-021 = "hack --feature-powerset --depth=3 --exclude-features=io-uring,rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture" # tests avoiding io-uring feature on Windows -ci-test-win = "hack --feature-powerset --depth=2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture" +ci-test-win = "hack --feature-powerset --depth=3 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture" # test with io-uring feature -ci-test-linux = "hack --feature-powerset --exclude-features=rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture" +ci-test-linux = "hack --feature-powerset --depth=3 --exclude-features=rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture" diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md index 25e8e56e..aa0d668f 100644 --- a/actix-tls/CHANGES.md +++ b/actix-tls/CHANGES.md @@ -2,7 +2,8 @@ ## Unreleased -- Added support to `http` crate version `1.0`. +- Add `rustls-0_21-native-roots` and `rustls-0_20-native-roots` crate features which utilize the `rustls-native-certs` crate to enable a `native_roots_cert_store()` functions in each rustls-based `connect` module. +- Implement `Host` for `http::Uri` (`http` crate version `1`). ## 3.1.1 diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml index 3b8d3ae8..43235227 100755 --- a/actix-tls/Cargo.toml +++ b/actix-tls/Cargo.toml @@ -47,10 +47,14 @@ openssl = ["tls-openssl", "tokio-openssl"] rustls = ["rustls-0_20"] # use rustls v0.20 impls -rustls-0_20 = ["tokio-rustls-023", "webpki-roots-022"] +rustls-0_20 = ["rustls-0_20-webpki-roots"] +rustls-0_20-webpki-roots = ["tokio-rustls-023", "webpki-roots-022"] +rustls-0_20-native-roots = ["tokio-rustls-023", "dep:rustls-native-certs"] # use rustls v0.21 impls -rustls-0_21 = ["tokio-rustls-024", "webpki-roots-025"] +rustls-0_21 = ["rustls-0_21-webpki-roots"] +rustls-0_21-webpki-roots = ["tokio-rustls-024", "webpki-roots-025"] +rustls-0_21-native-roots = ["tokio-rustls-024", "dep:rustls-native-certs"] # use native-tls impls native-tls = ["tokio-native-tls"] @@ -88,6 +92,9 @@ rustls-webpki-0101 = { package = "rustls-webpki", version = "0.101.4" } tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", optional = true } webpki-roots-025 = { package = "webpki-roots", version = "0.25", optional = true } +# native root certificates for both rustls impls +rustls-native-certs = { version = "0.6", optional = true } + # native-tls tokio-native-tls = { version = "0.3", optional = true } diff --git a/actix-tls/src/connect/mod.rs b/actix-tls/src/connect/mod.rs index 79cbb295..2e069c02 100644 --- a/actix-tls/src/connect/mod.rs +++ b/actix-tls/src/connect/mod.rs @@ -27,14 +27,23 @@ mod uri; #[cfg(feature = "openssl")] pub mod openssl; -#[cfg(feature = "rustls-0_20")] +#[cfg(any( + feature = "rustls-0_20-webpki-roots", + feature = "rustls-0_20-native-roots", +))] pub mod rustls_0_20; #[doc(hidden)] -#[cfg(feature = "rustls-0_20")] +#[cfg(any( + feature = "rustls-0_20-webpki-roots", + feature = "rustls-0_20-native-roots", +))] pub use rustls_0_20 as rustls; -#[cfg(feature = "rustls-0_21")] +#[cfg(any( + feature = "rustls-0_21-webpki-roots", + feature = "rustls-0_21-native-roots", +))] pub mod rustls_0_21; #[cfg(feature = "native-tls")] diff --git a/actix-tls/src/connect/rustls_0_20.rs b/actix-tls/src/connect/rustls_0_20.rs index 4547854e..52e73028 100644 --- a/actix-tls/src/connect/rustls_0_20.rs +++ b/actix-tls/src/connect/rustls_0_20.rs @@ -17,7 +17,7 @@ use actix_utils::future::{ok, Ready}; use futures_core::ready; use tokio_rustls::{ client::TlsStream as AsyncTlsStream, - rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore}, + rustls::{client::ServerName, ClientConfig, RootCertStore}, Connect as RustlsConnect, TlsConnector as RustlsTlsConnector, }; use tokio_rustls_023 as tokio_rustls; @@ -25,17 +25,38 @@ use tokio_rustls_023 as tokio_rustls; use crate::connect::{Connection, Host}; pub mod reexports { - //! Re-exports from `rustls` and `webpki_roots` that are useful for connectors. + //! Re-exports from the `rustls` v0.20 ecosystem that are useful for connectors. pub use tokio_rustls_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig}; + #[cfg(feature = "rustls-0_20-webpki-roots")] pub use webpki_roots_022::TLS_SERVER_ROOTS; } -/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store. -pub fn webpki_roots_cert_store() -> RootCertStore { +/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store. +/// +/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors. +#[cfg(feature = "rustls-0_20-native-roots")] +pub fn native_roots_cert_store() -> io::Result { let mut root_certs = RootCertStore::empty(); + + for cert in rustls_native_certs::load_native_certs()? { + root_certs + .add(&tokio_rustls_023::rustls::Certificate(cert.0)) + .unwrap(); + } + + Ok(root_certs) +} + +/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store. +#[cfg(feature = "rustls-0_20-webpki-roots")] +pub fn webpki_roots_cert_store() -> RootCertStore { + use tokio_rustls_023::rustls; + + let mut root_certs = RootCertStore::empty(); + for cert in webpki_roots_022::TLS_SERVER_ROOTS.0 { - let cert = OwnedTrustAnchor::from_subject_spki_name_constraints( + let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( cert.subject, cert.spki, cert.name_constraints, @@ -43,6 +64,7 @@ pub fn webpki_roots_cert_store() -> RootCertStore { let certs = vec![cert].into_iter(); root_certs.add_server_trust_anchors(certs); } + root_certs } diff --git a/actix-tls/src/connect/rustls_0_21.rs b/actix-tls/src/connect/rustls_0_21.rs index cc0d6de3..7c3ab24b 100644 --- a/actix-tls/src/connect/rustls_0_21.rs +++ b/actix-tls/src/connect/rustls_0_21.rs @@ -17,7 +17,7 @@ use actix_utils::future::{ok, Ready}; use futures_core::ready; use tokio_rustls::{ client::TlsStream as AsyncTlsStream, - rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore}, + rustls::{client::ServerName, ClientConfig, RootCertStore}, Connect as RustlsConnect, TlsConnector as RustlsTlsConnector, }; use tokio_rustls_024 as tokio_rustls; @@ -25,17 +25,38 @@ use tokio_rustls_024 as tokio_rustls; use crate::connect::{Connection, Host}; pub mod reexports { - //! Re-exports from `rustls` and `webpki_roots` that are useful for connectors. + //! Re-exports from the `rustls` v0.21 ecosystem that are useful for connectors. pub use tokio_rustls_024::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig}; + #[cfg(feature = "rustls-0_21-webpki-roots")] pub use webpki_roots_025::TLS_SERVER_ROOTS; } -/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store. -pub fn webpki_roots_cert_store() -> RootCertStore { +/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store. +/// +/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors. +#[cfg(feature = "rustls-0_21-native-roots")] +pub fn native_roots_cert_store() -> io::Result { let mut root_certs = RootCertStore::empty(); + + for cert in rustls_native_certs::load_native_certs()? { + root_certs + .add(&tokio_rustls_024::rustls::Certificate(cert.0)) + .unwrap(); + } + + Ok(root_certs) +} + +/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store. +#[cfg(feature = "rustls-0_21-webpki-roots")] +pub fn webpki_roots_cert_store() -> RootCertStore { + use tokio_rustls_024::rustls; + + let mut root_certs = RootCertStore::empty(); + for cert in webpki_roots_025::TLS_SERVER_ROOTS { - let cert = OwnedTrustAnchor::from_subject_spki_name_constraints( + let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( cert.subject, cert.spki, cert.name_constraints, @@ -43,6 +64,7 @@ pub fn webpki_roots_cert_store() -> RootCertStore { let certs = vec![cert].into_iter(); root_certs.add_trust_anchors(certs); } + root_certs } diff --git a/justfile b/justfile index 72331f9e..86bd8412 100644 --- a/justfile +++ b/justfile @@ -7,8 +7,8 @@ doc: # Document crates in workspace and watch for changes. doc-watch: - RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl --open - cargo watch -- RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl + RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls-0_20,rustls-0_21,rustls-0_20-native-roots,rustls-0_21-native-roots,openssl --open + cargo watch -- RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls-0_20,rustls-0_21,rustls-0_20-native-roots,rustls-0_21-native-roots,openssl # Check for unintentional external type exposure on all crates in workspace. check-external-types-all toolchain="+nightly":