diff --git a/Cargo.lock b/Cargo.lock index 742bfbc9..0b908bf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,9 +161,9 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "apalis-core" -version = "0.4.9" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1deb48475efcdece1f23a0553209ee842f264c2a5e9bcc4928bfa6a15a044cde" +checksum = "5dbe998f2a77a65433e3e893f7ffba5b0c4835a9601ccab02aa868d1d3ed71eb" dependencies = [ "async-stream", "async-trait", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "apalis-cron" -version = "0.4.9" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43310b7e0132f9520b09224fb6faafb32eec82a672aa79c09e46b5b488ed505b" +checksum = "9fc57450bd6a857d2370bb5504cf3d7f2a1fb85c7b68bdb7f92f50aac0e26aac" dependencies = [ "apalis-core", "async-stream", @@ -2252,7 +2252,7 @@ dependencies = [ "http 0.2.11", "hyper", "rustls 0.22.2", - "rustls-native-certs 0.7.0", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", @@ -2910,6 +2910,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rustls-pemfile 2.0.0", + "rustls-pki-types", "schemars", "serde", "serde_json", @@ -3053,7 +3054,7 @@ dependencies = [ "once_cell", "opentelemetry", "rustls 0.22.2", - "rustls-native-certs 0.6.3", + "rustls-native-certs", "serde", "serde_json", "serde_urlencoded", @@ -4441,9 +4442,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psl" -version = "2.1.18" +version = "2.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e109d6dd5679c4dce63a1c6bd7b61ed6758368af1ccf0701dfb02c021fbb9d0d" +checksum = "9667155e4837711406c6a5d26be83cdf53932f182d2ce8785529fd2c1a7e9e97" dependencies = [ "psl-types", ] @@ -4804,18 +4805,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile 1.0.4", - "schannel", - "security-framework", -] - [[package]] name = "rustls-native-certs" version = "0.7.0" diff --git a/crates/cli/src/server.rs b/crates/cli/src/server.rs index 50c74d8c..0880274d 100644 --- a/crates/cli/src/server.rs +++ b/crates/cli/src/server.rs @@ -285,11 +285,8 @@ where pub fn build_tls_server_config(config: &HttpTlsConfig) -> Result { let (key, chain) = config.load()?; - let key = rustls::PrivateKey(key); - let chain = chain.into_iter().map(rustls::Certificate).collect(); let mut config = rustls::ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_single_cert(chain, key) .context("failed to build TLS server config")?; diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 84c54875..e8fabed3 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -32,6 +32,7 @@ serde_with = { version = "3.4.0", features = ["hex", "chrono"] } serde_json.workspace = true pem-rfc7468 = "0.7.0" +rustls-pki-types = "1.1.0" rustls-pemfile = "2.0.0" rand.workspace = true rand_chacha = "0.3.1" diff --git a/crates/config/src/sections/http.rs b/crates/config/src/sections/http.rs index 879e6307..1771155a 100644 --- a/crates/config/src/sections/http.rs +++ b/crates/config/src/sections/http.rs @@ -14,7 +14,7 @@ #![allow(deprecated)] -use std::{borrow::Cow, io::Cursor, ops::Deref}; +use std::{borrow::Cow, io::Cursor}; use anyhow::bail; use async_trait::async_trait; @@ -22,6 +22,7 @@ use camino::Utf8PathBuf; use ipnetwork::IpNetwork; use mas_keystore::PrivateKey; use rand::Rng; +use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -197,7 +198,9 @@ impl TlsConfig { /// - a password was provided but the key was not encrypted /// - decoding the certificate chain as PEM /// - the certificate chain is empty - pub fn load(&self) -> Result<(Vec, Vec>), anyhow::Error> { + pub fn load( + &self, + ) -> Result<(PrivateKeyDer<'static>, Vec>), anyhow::Error> { let password = match &self.password { Some(PasswordOrFile::Password(password)) => Some(Cow::Borrowed(password.as_str())), Some(PasswordOrFile::PasswordFile(path)) => { @@ -230,9 +233,7 @@ impl TlsConfig { // Re-serialize the key to PKCS#8 DER, so rustls can consume it let key = key.to_pkcs8_der()?; - // This extracts the Vec out of the Zeroizing by copying it - // XXX: maybe we should keep that zeroizing? - let key = key.deref().clone(); + let key = PrivatePkcs8KeyDer::from(key.to_vec()).into(); let certificate_chain_pem = match &self.certificate { CertificateOrFile::Certificate(pem) => Cow::Borrowed(pem.as_str()), @@ -240,7 +241,9 @@ impl TlsConfig { }; let mut certificate_chain_reader = Cursor::new(certificate_chain_pem.as_bytes()); - let certificate_chain = rustls_pemfile::certs(&mut certificate_chain_reader)?; + let certificate_chain: Result, _> = + rustls_pemfile::certs(&mut certificate_chain_reader).collect(); + let certificate_chain = certificate_chain?; if certificate_chain.is_empty() { bail!("TLS certificate chain is empty (or invalid)") diff --git a/crates/graphql/src/model/mod.rs b/crates/graphql/src/model/mod.rs index ed43a8af..e8eaa596 100644 --- a/crates/graphql/src/model/mod.rs +++ b/crates/graphql/src/model/mod.rs @@ -30,7 +30,7 @@ pub use self::{ compat_sessions::{CompatSession, CompatSsoLogin}, cursor::{Cursor, NodeCursor}, node::{Node, NodeType}, - oauth::{OAuth2Client, OAuth2Consent, OAuth2Session}, + oauth::{OAuth2Client, OAuth2Session}, upstream_oauth::{UpstreamOAuth2Link, UpstreamOAuth2Provider}, users::{User, UserEmail}, viewer::{Anonymous, Viewer, ViewerSession}, diff --git a/crates/http/Cargo.toml b/crates/http/Cargo.toml index a8c7aa00..29b80b3a 100644 --- a/crates/http/Cargo.toml +++ b/crates/http/Cargo.toml @@ -23,7 +23,7 @@ hyper-rustls = { version = "0.25.0", features = ["http1", "http2"], default-feat once_cell = "1.18.0" opentelemetry.workspace = true rustls = { version = "0.22.2", optional = true } -rustls-native-certs = { version = "0.6.3", optional = true } +rustls-native-certs = { version = "0.7.0", optional = true } serde.workspace = true serde_json.workspace = true serde_urlencoded = "0.7.1" diff --git a/crates/http/src/client.rs b/crates/http/src/client.rs index ee23b77c..9719e792 100644 --- a/crates/http/src/client.rs +++ b/crates/http/src/client.rs @@ -75,15 +75,11 @@ async fn tls_roots() -> Result { #[cfg(feature = "webpki-roots")] #[allow(clippy::unused_async)] async fn tls_roots() -> Result { - let mut roots = rustls::RootCertStore::empty(); - roots.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - })); - Ok(roots) + let root_store = rustls::RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), + }; + + Ok(root_store) } #[cfg(feature = "native-roots")] @@ -131,7 +127,6 @@ pub enum NativeRootsLoadError { async fn make_tls_config() -> Result { let roots = tls_roots().await?; let tls_config = rustls::ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(roots) .with_no_client_auth(); diff --git a/crates/listener/examples/demo/main.rs b/crates/listener/examples/demo/main.rs index de02a5c1..ec65132d 100644 --- a/crates/listener/examples/demo/main.rs +++ b/crates/listener/examples/demo/main.rs @@ -24,10 +24,7 @@ use anyhow::Context; use hyper::{service::service_fn, Request, Response}; use mas_listener::{server::Server, shutdown::ShutdownStream, ConnectionInfo}; use tokio::signal::unix::SignalKind; -use tokio_rustls::rustls::{ - server::AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey, RootCertStore, - ServerConfig, -}; +use tokio_rustls::rustls::{server::WebPkiClientVerifier, RootCertStore, ServerConfig}; static CA_CERT_PEM: &[u8] = include_bytes!("./certs/ca.pem"); static SERVER_CERT_PEM: &[u8] = include_bytes!("./certs/server.pem"); @@ -75,27 +72,30 @@ async fn main() -> Result<(), anyhow::Error> { fn load_tls_config() -> Result, anyhow::Error> { let mut ca_cert_reader = BufReader::new(CA_CERT_PEM); - let ca_cert = rustls_pemfile::certs(&mut ca_cert_reader).context("Invalid CA certificate")?; + let ca_cert = rustls_pemfile::certs(&mut ca_cert_reader) + .collect::, _>>() + .context("Invalid CA certificate")?; let mut ca_cert_store = RootCertStore::empty(); - ca_cert_store.add_parsable_certificates(&ca_cert); + ca_cert_store.add_parsable_certificates(ca_cert); let mut server_cert_reader = BufReader::new(SERVER_CERT_PEM); let server_cert: Vec<_> = rustls_pemfile::certs(&mut server_cert_reader) - .context("Invalid server certificate")? - .into_iter() - .map(Certificate) - .collect(); + .collect::, _>>() + .context("Invalid server certificate")?; let mut server_key_reader = BufReader::new(SERVER_KEY_PEM); - let mut server_key = rustls_pemfile::rsa_private_keys(&mut server_key_reader) + let server_key = rustls_pemfile::rsa_private_keys(&mut server_key_reader) + .next() + .context("No RSA private key found")? .context("Invalid server TLS keys")?; - let server_key = PrivateKey(server_key.pop().context("Missing server TLS key")?); - let client_cert_verifier = Arc::new(AllowAnyAnonymousOrAuthenticatedClient::new(ca_cert_store)); + let client_cert_verifier = WebPkiClientVerifier::builder(Arc::new(ca_cert_store)) + .allow_unauthenticated() + .build()?; + let mut config = ServerConfig::builder() - .with_safe_defaults() .with_client_cert_verifier(client_cert_verifier) - .with_single_cert(server_cert, server_key)?; + .with_single_cert(server_cert, server_key.into())?; config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; Ok(Arc::new(config)) diff --git a/crates/listener/src/maybe_tls.rs b/crates/listener/src/maybe_tls.rs index 82788dab..a492489e 100644 --- a/crates/listener/src/maybe_tls.rs +++ b/crates/listener/src/maybe_tls.rs @@ -20,7 +20,10 @@ use std::{ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio_rustls::{ - rustls::{Certificate, ProtocolVersion, ServerConfig, ServerConnection, SupportedCipherSuite}, + rustls::{ + pki_types::CertificateDer, ProtocolVersion, ServerConfig, ServerConnection, + SupportedCipherSuite, + }, TlsAcceptor, }; @@ -31,7 +34,7 @@ pub struct TlsStreamInfo { pub negotiated_cipher_suite: SupportedCipherSuite, pub sni_hostname: Option, pub alpn_protocol: Option>, - pub peer_certificates: Option>, + pub peer_certificates: Option>>, } impl TlsStreamInfo { @@ -98,7 +101,13 @@ impl MaybeTlsStream { let sni_hostname = conn.server_name().map(ToOwned::to_owned); let alpn_protocol = conn.alpn_protocol().map(ToOwned::to_owned); - let peer_certificates = conn.peer_certificates().map(ToOwned::to_owned); + let peer_certificates = conn.peer_certificates().map(|certs| { + certs + .iter() + .cloned() + .map(CertificateDer::into_owned) + .collect() + }); Some(TlsStreamInfo { protocol_version, negotiated_cipher_suite, diff --git a/crates/oidc-client/src/http_service/hyper.rs b/crates/oidc-client/src/http_service/hyper.rs index d1c4f04b..44208d3a 100644 --- a/crates/oidc-client/src/http_service/hyper.rs +++ b/crates/oidc-client/src/http_service/hyper.rs @@ -32,6 +32,10 @@ static MAS_USER_AGENT: HeaderValue = HeaderValue::from_static("mas-oidc-client/0 /// Constructs a [`HttpService`] using [hyper] as a backend. /// +/// # Panics +/// +/// If the native TLS root certificates fail to load +/// /// [hyper]: https://crates.io/crates/hyper #[must_use] pub fn hyper_service() -> HttpService { @@ -41,8 +45,8 @@ pub fn hyper_service() -> HttpService { http.enforce_http(false); let tls_config = rustls::ClientConfig::builder() - .with_safe_defaults() .with_native_roots() + .expect("Failed to load native TLS") .with_no_client_auth(); let https = HttpsConnectorBuilder::new() diff --git a/crates/tasks/Cargo.toml b/crates/tasks/Cargo.toml index d8788149..0d636599 100644 --- a/crates/tasks/Cargo.toml +++ b/crates/tasks/Cargo.toml @@ -13,8 +13,8 @@ workspace = true [dependencies] anyhow.workspace = true -apalis-core = { version = "0.4.7", features = ["extensions", "tokio-comp", "storage"] } -apalis-cron = "0.4.7" +apalis-core = { version = "=0.4.7", features = ["extensions", "tokio-comp", "storage"] } +apalis-cron = "=0.4.7" async-stream = "0.3.5" async-trait = "0.1.74" chrono.workspace = true