diff --git a/Cargo.lock b/Cargo.lock index a77cc606..4e6c5714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -858,6 +858,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -999,6 +1005,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.4.0" @@ -2072,16 +2088,6 @@ dependencies = [ "hashbrown 0.14.3", ] -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "byteorder", - "num-traits", -] - [[package]] name = "headers" version = "0.3.9" @@ -2271,7 +2277,6 @@ dependencies = [ "http 0.2.11", "hyper", "rustls 0.22.2", - "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", @@ -2722,6 +2727,26 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.27" @@ -3181,7 +3206,6 @@ name = "mas-http" version = "0.8.0" dependencies = [ "anyhow", - "axum", "bytes", "futures-util", "headers", @@ -3190,10 +3214,9 @@ dependencies = [ "hyper", "hyper-rustls", "mas-tower", - "once_cell", "opentelemetry", "rustls 0.22.2", - "rustls-native-certs", + "rustls-platform-verifier", "serde", "serde_json", "serde_urlencoded", @@ -3203,7 +3226,6 @@ dependencies = [ "tower-http", "tracing", "tracing-opentelemetry", - "webpki-roots 0.26.0", ] [[package]] @@ -3403,10 +3425,10 @@ dependencies = [ "mas-keystore", "mime", "oauth2-types", - "once_cell", "rand 0.8.5", "rand_chacha 0.3.1", "rustls 0.22.2", + "rustls-platform-verifier", "serde", "serde_json", "serde_urlencoded", @@ -3749,6 +3771,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -4986,6 +5019,33 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" +[[package]] +name = "rustls-platform-verifier" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c35b9a497e588f1fb2e1d18a0d46a6d057710f34c3da7084b27353b319453cc" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.22.2", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.102.1", + "security-framework", + "security-framework-sys", + "webpki-roots 0.26.0", + "winapi", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e217e7fdc8466b5b35d30f8c0a30febd29173df4a3a0c2115d306b9c4117ad" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -5174,6 +5234,7 @@ dependencies = [ "core-foundation", "core-foundation-sys", "libc", + "num-bigint", "security-framework-sys", ] @@ -6194,7 +6255,6 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "hdrhistogram", "indexmap 1.9.3", "pin-project", "pin-project-lite", diff --git a/Cargo.toml b/Cargo.toml index e5f70682..f1d7f075 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,12 @@ features = ["derive"] [workspace.dependencies.http] version = "0.2.11" +# Hyper Rustls support +[workspace.dependencies.hyper-rustls] +version = "0.25.0" +features = ["http1", "http2"] +default-features = false + # Templates [workspace.dependencies.minijinja] version = "1.0.12" @@ -86,6 +92,14 @@ version = "1.0.12" [workspace.dependencies.rand] version = "0.8.5" +# TLS stack +[workspace.dependencies.rustls] +version = "0.22.2" + +# Use platform-specific verifier for TLS +[workspace.dependencies.rustls-platform-verifier] +version = "0.2.0" + # JSON Schema generation [workspace.dependencies.schemars] version = "0.8.16" @@ -105,6 +119,11 @@ features = ["preserve_order"] [workspace.dependencies.thiserror] version = "1.0.57" +# Tower services +[workspace.dependencies.tower] +version = "0.4.13" +features = ["util"] + # Logging and tracing [workspace.dependencies.tracing] version = "0.1.40" diff --git a/crates/axum-utils/Cargo.toml b/crates/axum-utils/Cargo.toml index f64fa7ed..90396712 100644 --- a/crates/axum-utils/Cargo.toml +++ b/crates/axum-utils/Cargo.toml @@ -31,7 +31,7 @@ serde_urlencoded = "0.7.1" serde_json.workspace = true thiserror.workspace = true tokio = "1.35.1" -tower = { version = "0.4.13", features = ["util"] } +tower.workspace = true tracing.workspace = true url.workspace = true ulid.workspace = true @@ -44,7 +44,3 @@ mas-jose.workspace = true mas-keystore.workspace = true mas-storage.workspace = true mas-templates.workspace = true - -[features] -native-roots = ["mas-http/native-roots"] -webpki-roots = ["mas-http/webpki-roots"] diff --git a/crates/axum-utils/src/http_client_factory.rs b/crates/axum-utils/src/http_client_factory.rs index cb9a0f08..78a812ac 100644 --- a/crates/axum-utils/src/http_client_factory.rs +++ b/crates/axum-utils/src/http_client_factory.rs @@ -14,8 +14,8 @@ use axum::body::Full; use mas_http::{ - make_traced_connector, BodyToBytesResponseLayer, Client, ClientInitError, ClientLayer, - ClientService, HttpService, TracedClient, TracedConnector, + make_traced_connector, BodyToBytesResponseLayer, Client, ClientLayer, ClientService, + HttpService, TracedClient, TracedConnector, }; use tower::{ util::{MapErrLayer, MapRequestLayer}, @@ -28,18 +28,20 @@ pub struct HttpClientFactory { client_layer: ClientLayer, } +impl Default for HttpClientFactory { + fn default() -> Self { + Self::new() + } +} + impl HttpClientFactory { /// Constructs a new HTTP client factory - /// - /// # Errors - /// - /// Returns an error if the client factory failed to initialise, which can - /// happen when it fails to load the system's CA certificates. - pub async fn new() -> Result { - Ok(Self { - traced_connector: make_traced_connector().await?, + #[must_use] + pub fn new() -> Self { + Self { + traced_connector: make_traced_connector(), client_layer: ClientLayer::new(), - }) + } } /// Constructs a new HTTP client diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index b6a0eaa3..84f1bb29 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -24,12 +24,12 @@ itertools = "0.12.1" listenfd = "1.0.1" rand.workspace = true rand_chacha = "0.3.1" -rustls = "0.22.2" +rustls.workspace = true serde_json.workspace = true serde_yaml = "0.9.30" sqlx = { version = "0.7.3", features = ["runtime-tokio-rustls", "postgres"] } tokio = { version = "1.35.1", features = ["full"] } -tower = "0.4.13" +tower.workspace = true tower-http = { version = "0.4.4", features = ["fs"] } url.workspace = true zeroize = "1.7.0" @@ -57,7 +57,7 @@ mas-data-model.workspace = true mas-email.workspace = true mas-graphql.workspace = true mas-handlers = { workspace = true } -mas-http = { workspace = true, features = ["axum", "client"] } +mas-http = { workspace = true, features = ["client"] } mas-i18n.workspace = true mas-iana.workspace = true mas-keystore.workspace = true @@ -75,18 +75,13 @@ mas-tower.workspace = true oauth2-types.workspace = true [features] -default = ["webpki-roots", "policy-cache"] +default = ["policy-cache"] # Features used for the prebuilt binaries -dist = ["policy-cache", "native-roots", "mas-config/dist"] +dist = ["policy-cache", "mas-config/dist"] # Features used in the Docker image -docker = ["native-roots", "mas-config/docker"] +docker = ["mas-config/docker"] # Enable wasmtime compilation cache policy-cache = ["mas-policy/cache"] - -# Use the native root certificates -native-roots = ["mas-http/native-roots", "mas-handlers/native-roots"] -# Use the webpki root certificates -webpki-roots = ["mas-http/webpki-roots", "mas-handlers/webpki-roots"] diff --git a/crates/cli/src/commands/debug.rs b/crates/cli/src/commands/debug.rs index d435b01c..b5c9a2c7 100644 --- a/crates/cli/src/commands/debug.rs +++ b/crates/cli/src/commands/debug.rs @@ -67,7 +67,7 @@ impl Options { #[tracing::instrument(skip_all)] pub async fn run(self, root: &super::Options) -> anyhow::Result<()> { use Subcommand as SC; - let http_client_factory = HttpClientFactory::new().await?; + let http_client_factory = HttpClientFactory::new(); match self.subcommand { SC::Http { show_headers, diff --git a/crates/cli/src/commands/doctor.rs b/crates/cli/src/commands/doctor.rs index fe0e54e6..5df0e1a2 100644 --- a/crates/cli/src/commands/doctor.rs +++ b/crates/cli/src/commands/doctor.rs @@ -41,7 +41,7 @@ impl Options { let config: RootConfig = root.load_config()?; // We'll need an HTTP client - let http_client_factory = HttpClientFactory::new().await?; + let http_client_factory = HttpClientFactory::new(); let base_url = config.http.public_base.as_str(); let issuer = config.http.issuer.as_ref().map(url::Url::as_str); let issuer = issuer.unwrap_or(base_url); diff --git a/crates/cli/src/commands/server.rs b/crates/cli/src/commands/server.rs index f4673637..4ecf5466 100644 --- a/crates/cli/src/commands/server.rs +++ b/crates/cli/src/commands/server.rs @@ -146,7 +146,7 @@ impl Options { ) .await?; - let http_client_factory = HttpClientFactory::new().await?; + let http_client_factory = HttpClientFactory::new(); let homeserver_connection = SynapseConnection::new( config.matrix.homeserver.clone(), diff --git a/crates/cli/src/commands/worker.rs b/crates/cli/src/commands/worker.rs index 96ccc8ec..e1564a41 100644 --- a/crates/cli/src/commands/worker.rs +++ b/crates/cli/src/commands/worker.rs @@ -55,7 +55,7 @@ impl Options { let mailer = mailer_from_config(&config.email, &templates)?; mailer.test_connection().await?; - let http_client_factory = HttpClientFactory::new().await?; + let http_client_factory = HttpClientFactory::new(); let conn = SynapseConnection::new( config.matrix.homeserver.clone(), config.matrix.endpoint.clone(), diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 67494e6e..fad847b4 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -77,7 +77,7 @@ async fn try_main() -> anyhow::Result<()> { telemetry_config.sentry.dsn.as_deref(), sentry::ClientOptions { transport: Some(Arc::new(HyperTransportFactory::new( - mas_http::make_untraced_client().await?, + mas_http::make_untraced_client(), ))), traces_sample_rate: 1.0, auto_session_tracking: true, @@ -99,9 +99,7 @@ async fn try_main() -> anyhow::Result<()> { }); // Setup OpenTelemetry tracing and metrics - let tracer = telemetry::setup(&telemetry_config) - .await - .context("failed to setup OpenTelemetry")?; + let tracer = telemetry::setup(&telemetry_config).context("failed to setup OpenTelemetry")?; let telemetry_layer = tracer.map(|tracer| { tracing_opentelemetry::layer() diff --git a/crates/cli/src/telemetry.rs b/crates/cli/src/telemetry.rs index b5b755f6..731fcb3a 100644 --- a/crates/cli/src/telemetry.rs +++ b/crates/cli/src/telemetry.rs @@ -43,7 +43,7 @@ use url::Url; static METER_PROVIDER: OnceCell = OnceCell::const_new(); static PROMETHEUS_REGISTRY: OnceCell = OnceCell::const_new(); -pub async fn setup(config: &TelemetryConfig) -> anyhow::Result> { +pub fn setup(config: &TelemetryConfig) -> anyhow::Result> { global::set_error_handler(|e| tracing::error!("{}", e))?; let propagator = propagator(&config.tracing.propagators); @@ -52,9 +52,7 @@ pub async fn setup(config: &TelemetryConfig) -> anyhow::Result> { mas_http::set_propagator(&propagator); global::set_text_map_propagator(propagator); - let tracer = tracer(&config.tracing.exporter) - .await - .context("Failed to configure traces exporter")?; + let tracer = tracer(&config.tracing.exporter).context("Failed to configure traces exporter")?; init_meter(&config.metrics.exporter).context("Failed to configure metrics exporter")?; @@ -86,13 +84,9 @@ fn propagator(propagators: &[Propagator]) -> impl TextMapPropagator { TextMapCompositePropagator::new(propagators) } -async fn http_client() -> anyhow::Result { - let client = mas_http::make_untraced_client() - .await - .context("Failed to build HTTP client used by telemetry exporter")?; - let client = - opentelemetry_http::hyper::HyperClient::new_with_timeout(client, Duration::from_secs(30)); - Ok(client) +fn http_client() -> impl opentelemetry_http::HttpClient + 'static { + let client = mas_http::make_untraced_client(); + opentelemetry_http::hyper::HyperClient::new_with_timeout(client, Duration::from_secs(30)) } fn stdout_tracer_provider() -> TracerProvider { @@ -133,12 +127,12 @@ fn jaeger_agent_tracer_provider(host: &str, port: u16) -> anyhow::Result, password: Option<&str>, ) -> anyhow::Result { - let http_client = http_client().await?; + let http_client = http_client(); let mut pipeline = opentelemetry_jaeger::new_collector_pipeline() .with_service_name(env!("CARGO_PKG_NAME")) .with_trace_config(trace_config()) @@ -160,8 +154,8 @@ async fn jaeger_collector_tracer_provider( Ok(tracer_provider) } -async fn zipkin_tracer(collector_endpoint: &Option) -> anyhow::Result { - let http_client = http_client().await?; +fn zipkin_tracer(collector_endpoint: &Option) -> anyhow::Result { + let http_client = http_client(); let mut pipeline = opentelemetry_zipkin::new_pipeline() .with_http_client(http_client) @@ -179,7 +173,7 @@ async fn zipkin_tracer(collector_endpoint: &Option) -> anyhow::Result anyhow::Result> { +fn tracer(config: &TracingExporterConfig) -> anyhow::Result> { let tracer_provider = match config { TracingExporterConfig::None => return Ok(None), TracingExporterConfig::Stdout => stdout_tracer_provider(), @@ -195,13 +189,10 @@ async fn tracer(config: &TracingExporterConfig) -> anyhow::Result endpoint, username, password, - }) => { - jaeger_collector_tracer_provider(endpoint, username.as_deref(), password.as_deref()) - .await? - } + }) => jaeger_collector_tracer_provider(endpoint, username.as_deref(), password.as_deref())?, TracingExporterConfig::Zipkin { collector_endpoint } => { // The Zipkin exporter already creates a tracer and installs it - return Ok(Some(zipkin_tracer(collector_endpoint).await?)); + return Ok(Some(zipkin_tracer(collector_endpoint)?)); } }; diff --git a/crates/graphql/Cargo.toml b/crates/graphql/Cargo.toml index 1359b28e..198e9150 100644 --- a/crates/graphql/Cargo.toml +++ b/crates/graphql/Cargo.toml @@ -21,7 +21,7 @@ serde.workspace = true thiserror.workspace = true tokio = { version = "1.35.1", features = ["sync"] } tracing.workspace = true -tower = { version = "0.4.13", features = ["util"] } +tower.workspace = true ulid.workspace = true url.workspace = true diff --git a/crates/handlers/Cargo.toml b/crates/handlers/Cargo.toml index b1e8df97..fdd7d80d 100644 --- a/crates/handlers/Cargo.toml +++ b/crates/handlers/Cargo.toml @@ -28,7 +28,7 @@ sentry = { version = "0.31.8", default-features = false } # Web server hyper = { version = "0.14.27", features = ["full"] } -tower = "0.4.13" +tower.workspace = true tower-http = { version = "0.4.4", features = ["cors"] } axum = "0.6.20" axum-macros = "0.3.8" @@ -90,9 +90,3 @@ oauth2-types.workspace = true insta = "1.34.0" tracing-subscriber.workspace = true cookie_store = "0.20.0" - -[features] -# Use the native root certificates -native-roots = ["mas-axum-utils/native-roots", "mas-http/native-roots"] -# Use the webpki root certificates -webpki-roots = ["mas-axum-utils/webpki-roots", "mas-http/webpki-roots"] diff --git a/crates/handlers/src/test_utils.rs b/crates/handlers/src/test_utils.rs index 2c78d30d..93c8f757 100644 --- a/crates/handlers/src/test_utils.rs +++ b/crates/handlers/src/test_utils.rs @@ -152,7 +152,7 @@ impl TestState { let homeserver_connection = Arc::new(MockHomeserverConnection::new("example.com")); - let http_client_factory = HttpClientFactory::new().await?; + let http_client_factory = HttpClientFactory::new(); let site_config = SiteConfig { tos_uri: Some("https://example.com/tos".parse().unwrap()), diff --git a/crates/handlers/src/upstream_oauth2/authorize.rs b/crates/handlers/src/upstream_oauth2/authorize.rs index 1464c484..d8171f96 100644 --- a/crates/handlers/src/upstream_oauth2/authorize.rs +++ b/crates/handlers/src/upstream_oauth2/authorize.rs @@ -44,7 +44,6 @@ pub(crate) enum RouteError { Internal(Box), } -impl_from_error_for_route!(mas_http::ClientInitError); impl_from_error_for_route!(mas_oidc_client::error::DiscoveryError); impl_from_error_for_route!(mas_oidc_client::error::AuthorizationError); impl_from_error_for_route!(mas_storage::RepositoryError); diff --git a/crates/handlers/src/upstream_oauth2/callback.rs b/crates/handlers/src/upstream_oauth2/callback.rs index 2b695844..c8ca954a 100644 --- a/crates/handlers/src/upstream_oauth2/callback.rs +++ b/crates/handlers/src/upstream_oauth2/callback.rs @@ -102,7 +102,6 @@ pub(crate) enum RouteError { } impl_from_error_for_route!(mas_storage::RepositoryError); -impl_from_error_for_route!(mas_http::ClientInitError); impl_from_error_for_route!(mas_oidc_client::error::DiscoveryError); impl_from_error_for_route!(mas_oidc_client::error::JwksError); impl_from_error_for_route!(mas_oidc_client::error::TokenAuthorizationCodeError); diff --git a/crates/http/Cargo.toml b/crates/http/Cargo.toml index 4af8c898..87f2c3f2 100644 --- a/crates/http/Cargo.toml +++ b/crates/http/Cargo.toml @@ -12,44 +12,38 @@ repository.workspace = true workspace = true [dependencies] -axum = { version = "0.6.20", optional = true } bytes = "1.5.0" futures-util = "0.3.30" headers = "0.3.9" http.workspace = true http-body = "0.4.5" hyper = "0.14.27" -hyper-rustls = { version = "0.25.0", features = ["http1", "http2"], default-features = false, optional = true } -once_cell = "1.19.0" +hyper-rustls = { workspace = true, optional = true } opentelemetry.workspace = true -rustls = { version = "0.22.2", optional = true } -rustls-native-certs = { version = "0.7.0", optional = true } +rustls = { workspace = true, optional = true } +rustls-platform-verifier = { workspace = true, optional = true } serde.workspace = true serde_json.workspace = true serde_urlencoded = "0.7.1" thiserror.workspace = true -tokio = { version = "1.35.1", features = ["sync", "parking_lot"], optional = true } -tower = { version = "0.4.13", features = ["util"] } +tower.workspace = true tower-http = { version = "0.4.4", features = ["cors"] } tracing.workspace = true tracing-opentelemetry.workspace = true -webpki-roots = { version = "0.26.0", optional = true } -mas-tower.workspace = true +mas-tower = { workspace = true, optional = true } [dev-dependencies] anyhow.workspace = true tokio = { version = "1.35.1", features = ["macros", "rt"] } [features] -axum = ["dep:axum"] -native-roots = ["dep:rustls-native-certs"] -webpki-roots = ["dep:webpki-roots"] client = [ + "dep:mas-tower", "dep:rustls", "hyper/tcp", "dep:hyper-rustls", - "dep:tokio", + "dep:rustls-platform-verifier", "tower/limit", "tower-http/timeout", "tower-http/follow-redirect", diff --git a/crates/http/src/client.rs b/crates/http/src/client.rs index 265d4328..c5b77d1c 100644 --- a/crates/http/src/client.rs +++ b/crates/http/src/client.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::convert::Infallible; - use hyper::client::{ connect::dns::{GaiResolver, Name}, HttpConnector, @@ -24,143 +22,21 @@ use mas_tower::{ DurationRecorderLayer, DurationRecorderService, FnWrapper, InFlightCounterLayer, InFlightCounterService, TraceLayer, TraceService, }; -use thiserror::Error; use tower::Layer; use tracing::Span; -#[cfg(all(not(feature = "webpki-roots"), not(feature = "native-roots")))] -compile_error!("enabling the 'client' feature requires also enabling the 'webpki-roots' or the 'native-roots' features"); - -#[cfg(all(feature = "webpki-roots", feature = "native-roots"))] -compile_error!("'webpki-roots' and 'native-roots' features are mutually exclusive"); - -#[cfg(feature = "native-roots")] -static NATIVE_TLS_ROOTS: tokio::sync::OnceCell = - tokio::sync::OnceCell::const_new(); - -#[cfg(feature = "native-roots")] -fn load_tls_roots_blocking() -> Result { - let mut roots = rustls::RootCertStore::empty(); - let certs = rustls_native_certs::load_native_certs()?; - for cert in certs { - roots.add(cert)?; - } - - if roots.is_empty() { - return Err(NativeRootsLoadError::Empty); - } - - Ok(roots) -} - -#[cfg(feature = "native-roots")] -async fn tls_roots() -> Result { - NATIVE_TLS_ROOTS - .get_or_try_init(|| async move { - // Load the TLS config once in a blocking task because loading the system - // certificates can take a long time (~200ms) on macOS - let span = tracing::info_span!("load_tls_roots"); - let roots = tokio::task::spawn_blocking(|| { - let _span = span.entered(); - load_tls_roots_blocking() - }) - .await??; - Ok(roots) - }) - .await - .cloned() -} - -#[cfg(feature = "webpki-roots")] -#[allow(clippy::unused_async)] -async fn tls_roots() -> Result { - let root_store = rustls::RootCertStore { - roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), - }; - - Ok(root_store) -} - -#[cfg(feature = "native-roots")] -#[derive(Error, Debug)] -#[error(transparent)] -pub enum NativeRootsInitError { - RootsLoadError(#[from] NativeRootsLoadError), - - JoinError(#[from] tokio::task::JoinError), -} - -#[derive(Error, Debug, Clone)] -pub enum ClientInitError { - #[cfg(feature = "native-roots")] - #[error(transparent)] - TlsRootsInit(std::sync::Arc), -} - -#[cfg(feature = "native-roots")] -impl From for ClientInitError { - fn from(inner: NativeRootsInitError) -> Self { - Self::TlsRootsInit(std::sync::Arc::new(inner)) - } -} - -impl From for ClientInitError { - fn from(e: Infallible) -> Self { - match e {} - } -} - -#[cfg(feature = "native-roots")] -#[derive(Error, Debug)] -pub enum NativeRootsLoadError { - #[error("could not load root certificates")] - Io(#[from] std::io::Error), - - #[error("invalid root certificate")] - Rustls(#[from] rustls::Error), - - #[error("no root certificate loaded")] - Empty, -} - -async fn make_tls_config() -> Result { - let roots = tls_roots().await?; - let tls_config = rustls::ClientConfig::builder() - .with_root_certificates(roots) - .with_no_client_auth(); - - Ok(tls_config) -} - pub type UntracedClient = hyper::Client; pub type TracedClient = hyper::Client; /// Create a basic Hyper HTTP & HTTPS client without any tracing -/// -/// # Errors -/// -/// Returns an error if it failed to load the TLS certificates -pub async fn make_untraced_client() -> Result, ClientInitError> +#[must_use] +pub fn make_untraced_client() -> UntracedClient where B: http_body::Body + Send + 'static, B::Data: Send, { - let https = make_untraced_connector().await?; - Ok(Client::builder().build(https)) -} - -/// Create a basic Hyper HTTP & HTTPS client which traces DNS requests -/// -/// # Errors -/// -/// Returns an error if it failed to load the TLS certificates -pub async fn make_traced_client() -> Result, ClientInitError> -where - B: http_body::Body + Send, - B::Data: Send, -{ - let https = make_traced_connector().await?; - Ok(Client::builder().build(https)) + let https = make_untraced_connector(); + Client::builder().build(https) } pub type TraceResolver = @@ -169,11 +45,8 @@ pub type UntracedConnector = HttpsConnector>; pub type TracedConnector = HttpsConnector>>; /// Create a traced HTTP and HTTPS connector -/// -/// # Errors -/// -/// Returns an error if it failed to load the TLS certificates -pub async fn make_traced_connector() -> Result +#[must_use] +pub fn make_traced_connector() -> TracedConnector where { let in_flight_counter = InFlightCounterLayer::new("dns.resolve.active_requests"); @@ -190,16 +63,16 @@ where let resolver = (in_flight_counter, duration_recorder, trace_layer).layer(GaiResolver::new()); - let tls_config = make_tls_config().await?; - Ok(make_connector(resolver, tls_config)) + let tls_config = rustls_platform_verifier::tls_config(); + make_connector(resolver, tls_config) } -async fn make_untraced_connector() -> Result +fn make_untraced_connector() -> UntracedConnector where { let resolver = GaiResolver::new(); - let tls_config = make_tls_config().await?; - Ok(make_connector(resolver, tls_config)) + let tls_config = rustls_platform_verifier::tls_config(); + make_connector(resolver, tls_config) } fn make_connector( diff --git a/crates/http/src/ext.rs b/crates/http/src/ext.rs index 675945e4..d4bc8aa6 100644 --- a/crates/http/src/ext.rs +++ b/crates/http/src/ext.rs @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::ops::RangeBounds; +use std::{ops::RangeBounds, sync::OnceLock}; use http::{header::HeaderName, Request, StatusCode}; -use once_cell::sync::OnceCell; use tower::Service; use tower_http::cors::CorsLayer; @@ -25,7 +24,7 @@ use crate::layers::{ json_request::JsonRequest, json_response::JsonResponse, }; -static PROPAGATOR_HEADERS: OnceCell> = OnceCell::new(); +static PROPAGATOR_HEADERS: OnceLock> = OnceLock::new(); /// Notify the CORS layer what opentelemetry propagators are being used. This /// helps whitelisting headers in CORS requests. diff --git a/crates/http/src/lib.rs b/crates/http/src/lib.rs index 0e9cdf12..916208fd 100644 --- a/crates/http/src/lib.rs +++ b/crates/http/src/lib.rs @@ -26,8 +26,8 @@ mod service; #[cfg(feature = "client")] pub use self::{ client::{ - make_traced_client, make_traced_connector, make_untraced_client, Client, ClientInitError, - TracedClient, TracedConnector, UntracedClient, UntracedConnector, + make_traced_connector, make_untraced_client, Client, TracedClient, TracedConnector, + UntracedClient, UntracedConnector, }, layers::client::{ClientLayer, ClientService}, }; diff --git a/crates/matrix-synapse/Cargo.toml b/crates/matrix-synapse/Cargo.toml index 48e5f531..a0e6aacc 100644 --- a/crates/matrix-synapse/Cargo.toml +++ b/crates/matrix-synapse/Cargo.toml @@ -16,7 +16,7 @@ anyhow.workspace = true async-trait = "0.1.77" http.workspace = true serde.workspace = true -tower = { version = "0.4.13", features = ["util"] } +tower.workspace = true tracing.workspace = true url.workspace = true diff --git a/crates/oidc-client/Cargo.toml b/crates/oidc-client/Cargo.toml index 4d2139bf..5927fec0 100644 --- a/crates/oidc-client/Cargo.toml +++ b/crates/oidc-client/Cargo.toml @@ -18,6 +18,7 @@ hyper = [ "dep:hyper", "dep:hyper-rustls", "dep:rustls", + "dep:rustls-platform-verifier", "dep:tower-http", "tower/limit", ] @@ -32,7 +33,6 @@ futures-util = "0.3.30" headers = "0.3.9" http.workspace = true language-tags = "0.3.2" -once_cell = "1.19.0" mime = "0.3.17" rand.workspace = true serde.workspace = true @@ -40,8 +40,7 @@ serde_json.workspace = true serde_urlencoded = "0.7.1" serde_with = "3.5.1" thiserror.workspace = true -tokio = { version = "1.35.1", features = ["rt", "macros", "rt-multi-thread"] } -tower = { version = "0.4.13", features = ["full"] } +tower.workspace = true tracing.workspace = true url.workspace = true @@ -53,16 +52,15 @@ oauth2-types.workspace = true # Default http service http-body = { version = "0.4.5", optional = true } -rustls = { version = "0.22.2", optional = true } -[dependencies.hyper-rustls] -version = "0.25.0" -features = ["http1", "http2", "rustls-native-certs"] -default-features = false -optional = true +rustls = { workspace = true, optional = true } +rustls-platform-verifier = { workspace = true, optional = true } +hyper-rustls = { workspace = true, optional = true } + [dependencies.hyper] version = "0.14.27" -features = ["client", "http1", "http2", "stream", "runtime" ] +features = ["client", "http1", "http2", "stream", "runtime"] optional = true + [dependencies.tower-http] version = "0.4.4" features = ["follow-redirect", "set-header", "timeout", "map-request-body", "util"] @@ -73,4 +71,5 @@ assert_matches = "1.5.0" bitflags = "2.4.2" mas-keystore.workspace = true rand_chacha = "0.3.1" +tokio = { version = "1.35.1", features = ["rt", "macros", "rt-multi-thread"] } wiremock = "0.5.22" diff --git a/crates/oidc-client/src/http_service/hyper.rs b/crates/oidc-client/src/http_service/hyper.rs index 44208d3a..ee9f0ad3 100644 --- a/crates/oidc-client/src/http_service/hyper.rs +++ b/crates/oidc-client/src/http_service/hyper.rs @@ -21,7 +21,7 @@ use std::time::Duration; use http::{header::USER_AGENT, HeaderValue}; use http_body::Full; use hyper::client::{connect::dns::GaiResolver, HttpConnector}; -use hyper_rustls::{ConfigBuilderExt, HttpsConnectorBuilder}; +use hyper_rustls::HttpsConnectorBuilder; use mas_http::BodyToBytesResponseLayer; use tower::{BoxError, ServiceBuilder}; use tower_http::{timeout::TimeoutLayer, ServiceBuilderExt}; @@ -44,10 +44,7 @@ pub fn hyper_service() -> HttpService { let mut http = HttpConnector::new_with_resolver(resolver); http.enforce_http(false); - let tls_config = rustls::ClientConfig::builder() - .with_native_roots() - .expect("Failed to load native TLS") - .with_no_client_auth(); + let tls_config = rustls_platform_verifier::tls_config(); let https = HttpsConnectorBuilder::new() .with_tls_config(tls_config) diff --git a/crates/tasks/Cargo.toml b/crates/tasks/Cargo.toml index bd7b1d7f..c498d7a1 100644 --- a/crates/tasks/Cargo.toml +++ b/crates/tasks/Cargo.toml @@ -25,7 +25,7 @@ rand_chacha = "0.3.1" sqlx = { version = "0.7.3", features = ["runtime-tokio-rustls", "postgres"] } thiserror.workspace = true tokio = { version = "1.35.1", features = ["rt"] } -tower = "0.4.13" +tower.workspace = true tracing.workspace = true tracing-opentelemetry.workspace = true opentelemetry.workspace = true diff --git a/crates/tower/Cargo.toml b/crates/tower/Cargo.toml index 34f227f2..00cb825f 100644 --- a/crates/tower/Cargo.toml +++ b/crates/tower/Cargo.toml @@ -15,7 +15,7 @@ workspace = true http.workspace = true tracing.workspace = true tracing-opentelemetry.workspace = true -tower = "0.4.13" +tower.workspace = true tokio = { version = "1.35.1", features = ["time"] } opentelemetry.workspace = true opentelemetry-http = "0.10.0"