diff --git a/pingora-cache/Cargo.toml b/pingora-cache/Cargo.toml index 44a063ef..de02f538 100644 --- a/pingora-cache/Cargo.toml +++ b/pingora-cache/Cargo.toml @@ -74,5 +74,6 @@ default = [] openssl = ["pingora-core/openssl"] boringssl = ["pingora-core/boringssl"] rustls = ["pingora-core/rustls"] +rustls-base = ["pingora-core/rustls-base"] s2n = ["pingora-core/s2n"] trace = ["dep:cf-rustracing", "dep:cf-rustracing-jaeger"] diff --git a/pingora-core/Cargo.toml b/pingora-core/Cargo.toml index 82dcb2f9..6f213cde 100644 --- a/pingora-core/Cargo.toml +++ b/pingora-core/Cargo.toml @@ -26,7 +26,7 @@ pingora-pool = { version = "0.8.0", path = "../pingora-pool" } pingora-error = { version = "0.8.0", path = "../pingora-error" } pingora-timeout = { version = "0.8.0", path = "../pingora-timeout" } pingora-http = { version = "0.8.0", path = "../pingora-http" } -pingora-rustls = { version = "0.8.0", path = "../pingora-rustls", optional = true } +pingora-rustls = { version = "0.8.0", path = "../pingora-rustls", optional = true, default-features = false } pingora-s2n = { version = "0.8.0", path = "../pingora-s2n", optional = true } bstr = { workspace = true } tokio = { workspace = true, features = ["net", "rt-multi-thread", "signal"] } @@ -103,7 +103,9 @@ jemallocator = "0.5" default = [] openssl = ["pingora-openssl", "openssl_derived"] boringssl = ["pingora-boringssl", "openssl_derived"] -rustls = ["pingora-rustls", "any_tls", "dep:x509-parser", "ouroboros"] +rustls = ["rustls_derived", "pingora-rustls/ring"] +rustls-base = ["rustls_derived"] +rustls_derived = ["pingora-rustls", "any_tls", "dep:x509-parser", "ouroboros"] s2n = ["pingora-s2n", "any_tls", "dep:x509-parser", "ouroboros", "lru"] patched_http1 = ["pingora-http/patched_http1"] openssl_derived = ["any_tls"] diff --git a/pingora-core/src/connectors/http/mod.rs b/pingora-core/src/connectors/http/mod.rs index 5a671ef2..7268dbaa 100644 --- a/pingora-core/src/connectors/http/mod.rs +++ b/pingora-core/src/connectors/http/mod.rs @@ -369,12 +369,12 @@ mod tests { // Creates a test connector for integration/unit tests. // For rustls, only ConnectorOptions are used here; the actual dangerous verifier is patched in the TLS connector. fn create_test_connector() -> Connector { - #[cfg(feature = "rustls")] + #[cfg(feature = "rustls_derived")] let custom_transport = { let options = ConnectorOptions::new(1); TransportConnector::new(Some(options)) }; - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "rustls_derived"))] let custom_transport = TransportConnector::new(None); Connector { h1: v1::Connector::new(None), @@ -490,7 +490,7 @@ mod tests { // Both client and server are using custom protocols, but different ones - we should create H1 sessions as fallback. // For RusTLS if there is no agreed protocol, the handshake directly fails, so this won't work // TODO: If no ALPN is matched, rustls should return None instead of failing the handshake. - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "rustls_derived"))] #[tokio::test] async fn test_incompatible_custom_client_custom_upstream() { let port = get_available_port().await; @@ -569,7 +569,7 @@ mod tests { } // Used for disabling certificate/hostname verification in rustls for tests and custom ALPN/self-signed scenarios. -#[cfg(all(test, feature = "rustls"))] +#[cfg(all(test, feature = "rustls_derived"))] pub mod rustls_no_verify { use rustls::client::danger::{ServerCertVerified, ServerCertVerifier}; use rustls::pki_types::{CertificateDer, ServerName}; diff --git a/pingora-core/src/connectors/tls/mod.rs b/pingora-core/src/connectors/tls/mod.rs index 5d75789b..50fc4322 100644 --- a/pingora-core/src/connectors/tls/mod.rs +++ b/pingora-core/src/connectors/tls/mod.rs @@ -24,10 +24,10 @@ mod s2n; #[cfg(feature = "s2n")] pub use s2n::*; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] mod rustls; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] pub use rustls::*; /// OpenSSL considers underscores in hostnames non-compliant. @@ -41,7 +41,7 @@ pub use rustls::*; /// > characters only letters, digits, and hyphen. There are also some /// > restrictions on the length. Labels must be 63 characters or less. /// - https://datatracker.ietf.org/doc/html/rfc1034#section-3.5 -#[cfg(any(feature = "openssl_derived", feature = "rustls"))] +#[cfg(any(feature = "openssl_derived", feature = "rustls_derived"))] pub fn replace_leftmost_underscore(sni: &str) -> Option { // wildcard is only leftmost label if let Some((leftmost, rest)) = sni.split_once('.') { @@ -56,7 +56,7 @@ pub fn replace_leftmost_underscore(sni: &str) -> Option { None } -#[cfg(any(feature = "openssl_derived", feature = "rustls"))] +#[cfg(any(feature = "openssl_derived", feature = "rustls_derived"))] #[cfg(test)] mod tests { use super::*; diff --git a/pingora-core/src/lib.rs b/pingora-core/src/lib.rs index 1e1b5d56..c5c6c8c3 100644 --- a/pingora-core/src/lib.rs +++ b/pingora-core/src/lib.rs @@ -112,7 +112,7 @@ pub use pingora_boringssl as tls; #[cfg(feature = "openssl")] pub use pingora_openssl as tls; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] pub use pingora_rustls as tls; #[cfg(feature = "s2n")] diff --git a/pingora-core/src/listeners/tls/mod.rs b/pingora-core/src/listeners/tls/mod.rs index c345073e..de1892a3 100644 --- a/pingora-core/src/listeners/tls/mod.rs +++ b/pingora-core/src/listeners/tls/mod.rs @@ -18,10 +18,10 @@ mod boringssl_openssl; #[cfg(feature = "openssl_derived")] pub use boringssl_openssl::*; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] mod rustls; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] pub use rustls::*; #[cfg(feature = "s2n")] diff --git a/pingora-core/src/protocols/tls/mod.rs b/pingora-core/src/protocols/tls/mod.rs index 9afdb9df..ea109bdf 100644 --- a/pingora-core/src/protocols/tls/mod.rs +++ b/pingora-core/src/protocols/tls/mod.rs @@ -23,10 +23,10 @@ mod boringssl_openssl; #[cfg(feature = "openssl_derived")] pub use boringssl_openssl::*; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] mod rustls; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] pub use rustls::*; #[cfg(feature = "s2n")] @@ -174,7 +174,7 @@ impl ALPN { } } - #[cfg(feature = "rustls")] + #[cfg(feature = "rustls_derived")] pub(crate) fn to_wire_protocols(&self) -> Vec> { match self { ALPN::H1 => vec![b"http/1.1".to_vec()], diff --git a/pingora-core/src/utils/tls/mod.rs b/pingora-core/src/utils/tls/mod.rs index c345073e..de1892a3 100644 --- a/pingora-core/src/utils/tls/mod.rs +++ b/pingora-core/src/utils/tls/mod.rs @@ -18,10 +18,10 @@ mod boringssl_openssl; #[cfg(feature = "openssl_derived")] pub use boringssl_openssl::*; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] mod rustls; -#[cfg(feature = "rustls")] +#[cfg(feature = "rustls_derived")] pub use rustls::*; #[cfg(feature = "s2n")] diff --git a/pingora-load-balancing/Cargo.toml b/pingora-load-balancing/Cargo.toml index d6f5d41e..8a15062c 100644 --- a/pingora-load-balancing/Cargo.toml +++ b/pingora-load-balancing/Cargo.toml @@ -39,6 +39,7 @@ default = [] openssl = ["pingora-core/openssl", "openssl_derived"] boringssl = ["pingora-core/boringssl", "openssl_derived"] rustls = ["pingora-core/rustls", "any_tls"] +rustls-base = ["pingora-core/rustls-base", "any_tls"] s2n = ["pingora-core/s2n", "any_tls"] openssl_derived = ["any_tls"] any_tls = [] diff --git a/pingora-proxy/Cargo.toml b/pingora-proxy/Cargo.toml index e1cc1cbb..6433b641 100644 --- a/pingora-proxy/Cargo.toml +++ b/pingora-proxy/Cargo.toml @@ -69,6 +69,7 @@ boringssl = [ "openssl_derived", ] rustls = ["pingora-core/rustls", "pingora-cache/rustls", "any_tls"] +rustls-base = ["pingora-core/rustls-base", "pingora-cache/rustls-base", "any_tls"] s2n = ["pingora-core/s2n", "pingora-cache/s2n", "any_tls"] openssl_derived = ["any_tls"] any_tls = [] diff --git a/pingora-rustls/Cargo.toml b/pingora-rustls/Cargo.toml index 51cd00ff..3aada668 100644 --- a/pingora-rustls/Cargo.toml +++ b/pingora-rustls/Cargo.toml @@ -14,13 +14,21 @@ RusTLS async APIs for Pingora. name = "pingora_rustls" path = "src/lib.rs" +[features] +default = ["ring"] +ring = ["dep:ring", "rustls/ring"] + +[package.metadata.cargo-machete] +ignored = ["ring"] + [dependencies] log = "0.4.21" pingora-error = { version = "0.8.0", path = "../pingora-error"} -ring = "0.17.12" -rustls = { version = "0.23.12", features = ["ring"] } +ring = { version = "0.17.12", optional = true } +rustls = { version = "0.23.12", default-features = false } rustls-native-certs = "0.7.1" rustls-pemfile = "2.1.2" rustls-pki-types = "1.7.0" +sha2 = "0.10" tokio-rustls = "0.26.0" no_debug = "3.1.0" diff --git a/pingora-rustls/src/lib.rs b/pingora-rustls/src/lib.rs index deb0c88b..b05f1ca5 100644 --- a/pingora-rustls/src/lib.rs +++ b/pingora-rustls/src/lib.rs @@ -33,13 +33,19 @@ pub use rustls::{ SignatureScheme, Stream, }; -/// Install the default `ring` CryptoProvider for rustls. +/// Install the default CryptoProvider for rustls. /// -/// rustls 0.23+ requires an explicit provider. This function installs `ring` -/// as the process-level default. Safe to call multiple times — subsequent -/// calls are no-ops. +/// rustls 0.23+ requires an explicit provider. The `ring` feature is enabled +/// by default and this function installs `ring` as the process-level default. +/// When `ring` is disabled (e.g. via `rustls-base`), this is a no-op and the +/// consumer must install their own [`CryptoProvider`] before any TLS use. +/// +/// Safe to call multiple times — subsequent calls are no-ops. pub fn install_default_crypto_provider() { - let _ = CryptoProvider::install_default(rustls::crypto::ring::default_provider()); + #[cfg(feature = "ring")] + { + let _ = CryptoProvider::install_default(rustls::crypto::ring::default_provider()); + } } pub use rustls_native_certs::load_native_certs; use rustls_pemfile::Item; @@ -183,6 +189,6 @@ pub fn load_pem_file_private_key(path: &String) -> Result> { } pub fn hash_certificate(cert: &CertificateDer) -> Vec { - let hash = ring::digest::digest(&ring::digest::SHA256, cert.as_ref()); - hash.as_ref().to_vec() + use sha2::Digest; + sha2::Sha256::digest(cert.as_ref()).to_vec() } diff --git a/pingora/Cargo.toml b/pingora/Cargo.toml index 8e30130a..e33f6ee4 100644 --- a/pingora/Cargo.toml +++ b/pingora/Cargo.toml @@ -107,6 +107,20 @@ rustls = [ "any_tls", ] +## Use [rustls](https://crates.io/crates/rustls) for tls without a bundled crypto provider +## +## ⚠️ _Highly Experimental_! ⚠️ Try it, but don't rely on it (yet) +## +## The consumer must install their own [`CryptoProvider`] before any TLS use. +## See `rustls` feature for the default ring-based variant. +rustls-base = [ + "pingora-core/rustls-base", + "pingora-proxy?/rustls-base", + "pingora-cache?/rustls-base", + "pingora-load-balancing?/rustls-base", + "any_tls", +] + #! ### Pingora extensions ## Include the [proxy](crate::proxy) module