diff --git a/Cargo.lock b/Cargo.lock index dbb565782e..1d83c46b12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -491,6 +491,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aws-lc-rs" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "aws-runtime" version = "1.5.16" @@ -8111,6 +8134,7 @@ version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -8239,6 +8263,7 @@ version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -9371,6 +9396,7 @@ dependencies = [ "spin-manifest", "spin-outbound-networking-config", "spin-serde", + "spin-tls", "spin-world", "tempfile", "tokio", @@ -10049,6 +10075,7 @@ dependencies = [ "spin-factors-executor", "spin-http", "spin-telemetry", + "spin-tls", "spin-trigger", "spin-world", "terminal", diff --git a/Cargo.toml b/Cargo.toml index c68df9a593..e711c65b51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,9 +172,8 @@ redis = "0.32.5" regex = "1" reqwest = { version = "0.12", features = ["stream", "blocking", "rustls-tls-native-roots"] } rusqlite = "0.34" -# In `rustls` turn off the `aws_lc_rs` default feature and turn on `ring`. -# If both `aws_lc_rs` and `ring` are enabled, a panic at runtime will occur. -rustls = { version = "0.23", default-features = false, features = ["ring", "std", "logging", "tls12"] } +# Spin installs aws-lc-rs as the process-wide rustls crypto provider at startup. +rustls = { version = "0.23", default-features = false, features = ["aws_lc_rs", "std", "logging", "tls12"] } rustls-pki-types = "1.12" rustls-platform-verifier = "0.6" schemars = { version = "1.2", features = ["indexmap2", "semver1"] } diff --git a/crates/factor-outbound-networking/Cargo.toml b/crates/factor-outbound-networking/Cargo.toml index 330b06cc9f..8bef7df98e 100644 --- a/crates/factor-outbound-networking/Cargo.toml +++ b/crates/factor-outbound-networking/Cargo.toml @@ -13,6 +13,7 @@ ip_network = "0.4.1" rustls = { workspace = true } rustls-pki-types = { workspace = true } rustls-platform-verifier = { workspace = true } +spin-tls = { path = "../tls" } serde = { workspace = true } spin-connection-semaphore = { path = "../connection-semaphore" } spin-factor-variables = { path = "../factor-variables" } diff --git a/crates/factor-outbound-networking/src/tls.rs b/crates/factor-outbound-networking/src/tls.rs index f4484865ed..a0cf4f32a5 100644 --- a/crates/factor-outbound-networking/src/tls.rs +++ b/crates/factor-outbound-networking/src/tls.rs @@ -120,7 +120,11 @@ impl TlsClientConfig { ); } - let builder = rustls::ClientConfig::builder(); + let builder = rustls::ClientConfig::builder_with_provider( + spin_tls::get_or_install_default_crypto_provider(), + ) + .with_safe_default_protocol_versions() + .context("failed to configure default TLS protocol versions")?; let builder = if use_platform_roots { let verifier = rustls_platform_verifier::Verifier::new_with_extra_roots( extra_roots, diff --git a/crates/tls/src/lib.rs b/crates/tls/src/lib.rs index b9b283ebba..756d0fb0cd 100644 --- a/crates/tls/src/lib.rs +++ b/crates/tls/src/lib.rs @@ -1,15 +1,21 @@ -use std::sync::Once; +use std::sync::Arc; -static INSTALL_DEFAULT_CRYPTO_PROVIDER: Once = Once::new(); - -/// Install Spin's process-wide rustls crypto provider. +/// Returns the process-wide default rustls crypto provider, installing +/// `aws-lc-rs` if no provider is set. A provider already installed - e.g. +/// by an application embedding Spin's crates - is left in place and used. /// -/// This is idempotent for Spin's own duplicate calls from `main` and the trigger, -/// but fails loudly if something else installed a rustls provider first. -pub fn install_default_crypto_provider() { - INSTALL_DEFAULT_CRYPTO_PROVIDER.call_once(|| { - rustls::crypto::ring::default_provider() - .install_default() - .expect("failed to install rustls ring crypto provider"); - }); +/// Use this to build rustls configs so that TLS paths constructed without +/// going through a Spin entrypoint (direct factor construction, tests, +/// embedders) select the same provider as the rest of the process. +pub fn get_or_install_default_crypto_provider() -> Arc { + if let Some(provider) = rustls::crypto::CryptoProvider::get_default() { + return provider.clone(); + } + + // Ignore Err: it means another thread won the install race. + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + + rustls::crypto::CryptoProvider::get_default() + .expect("a default provider was just installed") + .clone() } diff --git a/crates/trigger-http/Cargo.toml b/crates/trigger-http/Cargo.toml index 78e54daa93..9006b16ae0 100644 --- a/crates/trigger-http/Cargo.toml +++ b/crates/trigger-http/Cargo.toml @@ -21,6 +21,7 @@ pin-project-lite = { workspace = true } rand.workspace = true rustls = { workspace = true } rustls-pki-types = { workspace = true } +spin-tls = { path = "../tls" } serde = { workspace = true } serde_json = { workspace = true } spin-app = { path = "../app" } diff --git a/crates/trigger-http/src/tls.rs b/crates/trigger-http/src/tls.rs index 4b5b2f04dd..3167927c3d 100644 --- a/crates/trigger-http/src/tls.rs +++ b/crates/trigger-http/src/tls.rs @@ -23,10 +23,14 @@ impl TlsConfig { let certs = load_certs(&self.cert_path)?; let private_key = load_key(&self.key_path)?; - let cfg = rustls::ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(certs, private_key) - .map_err(|e| anyhow::anyhow!("{}", e))?; + let cfg = rustls::ServerConfig::builder_with_provider( + spin_tls::get_or_install_default_crypto_provider(), + ) + .with_safe_default_protocol_versions() + .context("failed to configure default TLS protocol versions")? + .with_no_client_auth() + .with_single_cert(certs, private_key) + .map_err(|e| anyhow::anyhow!("{}", e))?; Ok(Arc::new(cfg).into()) } diff --git a/crates/trigger/src/cli.rs b/crates/trigger/src/cli.rs index af860be15e..fa9eeb69af 100644 --- a/crates/trigger/src/cli.rs +++ b/crates/trigger/src/cli.rs @@ -183,7 +183,7 @@ pub struct NoCliArgs; impl, B: RuntimeFactorsBuilder> FactorsTriggerCommand { /// Create a new TriggerExecutorBuilder from this TriggerExecutorCommand. pub async fn run(self) -> Result<()> { - spin_tls::install_default_crypto_provider(); + spin_tls::get_or_install_default_crypto_provider(); // Handle --help-args-only if self.help_args_only { Self::command() diff --git a/src/bin/spin.rs b/src/bin/spin.rs index 95bffef59f..1a88bf9af3 100644 --- a/src/bin/spin.rs +++ b/src/bin/spin.rs @@ -2,7 +2,7 @@ use spin_cli::subprocess::ExitStatusError; #[tokio::main] async fn main() { - spin_tls::install_default_crypto_provider(); + spin_tls::get_or_install_default_crypto_provider(); if let Err(err) = spin_cli::run().await { let code = match err.downcast_ref::() { // If we encounter an `ExitStatusError` it means a subprocess has already