diff --git a/pingora-core/src/connectors/mod.rs b/pingora-core/src/connectors/mod.rs index a0f0a980..b3078fdf 100644 --- a/pingora-core/src/connectors/mod.rs +++ b/pingora-core/src/connectors/mod.rs @@ -28,10 +28,10 @@ use offload::OffloadRuntime; use pingora_error::{ErrorType::*, OrErr, Result}; use pingora_pool::{ConnectionMeta, ConnectionPool}; -use crate::connectors::tls::{Connector, do_connect}; +use crate::connectors::tls::{do_connect, Connector}; use crate::protocols::Stream; use crate::server::configuration::ServerConf; -use crate::upstreams::peer::{ALPN, Peer}; +use crate::upstreams::peer::{Peer, ALPN}; pub mod http; mod l4; @@ -271,9 +271,6 @@ impl TransportConnector { } } - - - struct PreferredHttpVersion { // TODO: shard to avoid the global lock versions: RwLock>, // diff --git a/pingora-core/src/connectors/tls/boringssl_openssl/mod.rs b/pingora-core/src/connectors/tls/boringssl_openssl/mod.rs index 48408a16..bd41a690 100644 --- a/pingora-core/src/connectors/tls/boringssl_openssl/mod.rs +++ b/pingora-core/src/connectors/tls/boringssl_openssl/mod.rs @@ -19,27 +19,27 @@ use std::sync::{Arc, Once}; use log::debug; -use pingora_error::{Error, OrErr, Result}; use pingora_error::ErrorType::{ConnectTimedout, InternalError}; +use pingora_error::{Error, OrErr, Result}; use crate::connectors::ConnectorOptions; use crate::listeners::ALPN; -use crate::protocols::IO; use crate::protocols::tls::boringssl_openssl::client::handshake; use crate::protocols::tls::TlsStream; +use crate::protocols::IO; use crate::tls::ext::{ add_host, clear_error_stack, ssl_add_chain_cert, ssl_set_groups_list, ssl_set_renegotiate_mode_freely, ssl_set_verify_cert_store, ssl_use_certificate, ssl_use_private_key, ssl_use_second_key_share, }; -use crate::tls::ssl::{SslConnector, SslFiletype, SslMethod, SslVerifyMode, SslVersion}; #[cfg(feature = "boringssl")] use crate::tls::ssl::SslCurve; +use crate::tls::ssl::{SslConnector, SslFiletype, SslMethod, SslVerifyMode, SslVersion}; use crate::tls::x509::store::X509StoreBuilder; use crate::upstreams::peer::Peer; use crate::utils::tls::boringssl_openssl::{der_to_private_key, der_to_x509}; -use super::{Connector, replace_leftmost_underscore, TlsConnectorContext}; +use super::{replace_leftmost_underscore, Connector, TlsConnectorContext}; const CIPHER_LIST: &str = "AES-128-GCM-SHA256\ :AES-256-GCM-SHA384\ @@ -103,7 +103,7 @@ impl TlsConnectorContext for TlsConnectorCtx { fn build_connector(options: Option) -> Connector where - Self: Sized + Self: Sized, { let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); // TODO: make these conf @@ -168,9 +168,9 @@ pub(super) async fn connect( alpn_override: Option, tls_ctx: &Arc, ) -> Result> - where - T: IO, - P: Peer + Send + Sync +where + T: IO, + P: Peer + Send + Sync, { let ctx = tls_ctx.as_any().downcast_ref::().unwrap(); let mut ssl_conf = ctx.0.configure().unwrap(); @@ -193,11 +193,9 @@ pub(super) async fn connect( if let Some(key_pair) = peer.get_client_cert_key() { debug!("setting client cert and key"); let leaf = der_to_x509(&*key_pair.leaf())?; - ssl_use_certificate(&mut ssl_conf, &leaf) - .or_err(InternalError, "invalid client cert")?; + ssl_use_certificate(&mut ssl_conf, &leaf).or_err(InternalError, "invalid client cert")?; let key = der_to_private_key(&*key_pair.key())?; - ssl_use_private_key(&mut ssl_conf, &key) - .or_err(InternalError, "invalid client key")?; + ssl_use_private_key(&mut ssl_conf, &key).or_err(InternalError, "invalid client key")?; let intermediates = key_pair.intermediates(); if !intermediates.is_empty() { @@ -275,4 +273,4 @@ pub(super) async fn connect( }, None => connect_future.await, } -} \ No newline at end of file +} diff --git a/pingora-core/src/connectors/tls/mod.rs b/pingora-core/src/connectors/tls/mod.rs index 8bce8a95..01167850 100644 --- a/pingora-core/src/connectors/tls/mod.rs +++ b/pingora-core/src/connectors/tls/mod.rs @@ -16,8 +16,8 @@ use std::any::Any; use std::net::SocketAddr; use std::sync::Arc; -use pingora_error::{Error, Result}; use pingora_error::ErrorType::ConnectTimedout; +use pingora_error::{Error, Result}; use crate::connectors::l4::connect as l4_connect; #[cfg(not(feature = "rustls"))] @@ -29,7 +29,7 @@ use crate::connectors::tls::rustls::connect as tls_connect; #[cfg(feature = "rustls")] use crate::connectors::tls::rustls::TlsConnectorCtx; use crate::protocols::Stream; -use crate::upstreams::peer::{ALPN, Peer}; +use crate::upstreams::peer::{Peer, ALPN}; use super::ConnectorOptions; @@ -53,14 +53,15 @@ pub(crate) trait TlsConnectorContext { fn as_any(&self) -> &dyn Any; fn build_connector(options: Option) -> Connector - where Self: Sized; + where + Self: Sized; } pub(super) async fn do_connect( peer: &P, bind_to: Option, alpn_override: Option, - tls_ctx: &Arc + tls_ctx: &Arc, ) -> Result { // Create the future that does the connections, but don't evaluate it until // we decide if we need a timeout or not @@ -82,7 +83,7 @@ async fn do_connect_inner( peer: &P, bind_to: Option, alpn_override: Option, - tls_ctx: &Arc + tls_ctx: &Arc, ) -> Result { let stream = l4_connect(peer, bind_to).await?; if peer.tls() { @@ -138,17 +139,24 @@ mod tests { ]; for case in none_cases { - assert!(super::replace_leftmost_underscore(case).is_none(), "{}", case); + assert!( + super::replace_leftmost_underscore(case).is_none(), + "{}", + case + ); } assert_eq!( - Some("bb-b.some.com".to_string()), super::replace_leftmost_underscore("bb_b.some.com") + Some("bb-b.some.com".to_string()), + super::replace_leftmost_underscore("bb_b.some.com") ); assert_eq!( - Some("a-a-a.some.com".to_string()), super::replace_leftmost_underscore("a_a_a.some.com") + Some("a-a-a.some.com".to_string()), + super::replace_leftmost_underscore("a_a_a.some.com") ); assert_eq!( - Some("-.some.com".to_string()), super::replace_leftmost_underscore("_.some.com") + Some("-.some.com".to_string()), + super::replace_leftmost_underscore("_.some.com") ); } -} \ No newline at end of file +} diff --git a/pingora-core/src/connectors/tls/rustls/mod.rs b/pingora-core/src/connectors/tls/rustls/mod.rs index 88e04793..453c0ab7 100644 --- a/pingora-core/src/connectors/tls/rustls/mod.rs +++ b/pingora-core/src/connectors/tls/rustls/mod.rs @@ -19,29 +19,31 @@ use std::sync::Arc; use log::debug; -use pingora_error::{Error, OrErr, Result}; use pingora_error::ErrorType::{ConnectTimedout, InvalidCert}; -use pingora_rustls::{load_ca_file_into_store, load_certs_key_file, load_platform_certs_incl_env_into_store}; +use pingora_error::{Error, OrErr, Result}; +use pingora_rustls::version; use pingora_rustls::CertificateDer; use pingora_rustls::ClientConfig; use pingora_rustls::ClientConfig as RusTlsClientConfig; use pingora_rustls::PrivateKeyDer; use pingora_rustls::RootCertStore; use pingora_rustls::TlsConnector as RusTlsConnector; -use pingora_rustls::version; +use pingora_rustls::{ + load_ca_file_into_store, load_certs_key_file, load_platform_certs_incl_env_into_store, +}; use crate::connectors::ConnectorOptions; use crate::listeners::ALPN; -use crate::protocols::IO; use crate::protocols::tls::rustls::client::handshake; use crate::protocols::tls::TlsStream; +use crate::protocols::IO; use crate::upstreams::peer::Peer; -use super::{Connector, replace_leftmost_underscore, TlsConnectorContext}; +use super::{replace_leftmost_underscore, Connector, TlsConnectorContext}; pub(crate) struct TlsConnectorCtx { config: RusTlsClientConfig, - ca_certs: RootCertStore + ca_certs: RootCertStore, } impl TlsConnectorContext for TlsConnectorCtx { fn as_any(&self) -> &dyn Any { @@ -50,7 +52,7 @@ impl TlsConnectorContext for TlsConnectorCtx { fn build_connector(options: Option) -> Connector where - Self: Sized + Self: Sized, { // NOTE: Rustls only supports TLS 1.2 & 1.3 @@ -88,37 +90,35 @@ impl TlsConnectorContext for TlsConnectorCtx { let config = match certs_key { Some((certs, key)) => { match builder.with_client_auth_cert(certs.clone(), key.clone_key()) { - Ok(config) => { config } + Ok(config) => config, Err(err) => { // TODO: is there a viable alternative to the panic? // falling back to no client auth... does not seem to be reasonable. - panic!("{}", format!("Failed to configure client auth cert/key. Error: {}", err)); + panic!( + "{}", + format!("Failed to configure client auth cert/key. Error: {}", err) + ); } } } - None => { - builder.with_no_client_auth() - } + None => builder.with_no_client_auth(), }; Connector { - ctx: Arc::new(TlsConnectorCtx { - config, - ca_certs - }), + ctx: Arc::new(TlsConnectorCtx { config, ca_certs }), } } } -pub(super) async fn connect( +pub(super) async fn connect( stream: T, peer: &P, alpn_override: Option, - tls_ctx: &Arc + tls_ctx: &Arc, ) -> Result> where T: IO, - P: Peer + Send + Sync + P: Peer + Send + Sync, { let ctx = tls_ctx.as_any().downcast_ref::().unwrap(); let mut config = ctx.config.clone(); @@ -129,7 +129,7 @@ where let key_pair = peer.get_client_cert_key(); let updated_config: Option = match key_pair { - None => { None } + None => None, Some(key_arc) => { debug!("setting client cert and key"); @@ -138,20 +138,34 @@ where cert_chain.push(key_arc.leaf().to_owned()); debug!("adding intermediate certificates to mTLS cert chain"); - key_arc.intermediates().to_owned().iter() + key_arc + .intermediates() + .to_owned() + .iter() .map(|i| i.to_vec()) .for_each(|i| cert_chain.push(i)); - let certs: Vec = cert_chain.into_iter() - .map(|c| c.as_slice().to_owned().into()).collect(); - let private_key: PrivateKeyDer = key_arc.key().as_slice().to_owned().try_into().unwrap(); - - let builder = - ClientConfig::builder_with_protocol_versions(&vec![&version::TLS12, &version::TLS13]) - .with_root_certificates(ctx.ca_certs.clone()); - - let updated_config = builder.with_client_auth_cert(certs, private_key) - .explain_err(InvalidCert, |e| format!("Failed to use peer cert/key to update Rustls config: {:?}",e))?; + let certs: Vec = cert_chain + .into_iter() + .map(|c| c.as_slice().to_owned().into()) + .collect(); + let private_key: PrivateKeyDer = + key_arc.key().as_slice().to_owned().try_into().unwrap(); + + let builder = ClientConfig::builder_with_protocol_versions(&vec![ + &version::TLS12, + &version::TLS13, + ]) + .with_root_certificates(ctx.ca_certs.clone()); + + let updated_config = builder + .with_client_auth_cert(certs, private_key) + .explain_err(InvalidCert, |e| { + format!( + "Failed to use peer cert/key to update Rustls config: {:?}", + e + ) + })?; Some(updated_config) } }; @@ -212,4 +226,4 @@ where }, None => connect_future.await, } -} \ No newline at end of file +} diff --git a/pingora-core/src/lib.rs b/pingora-core/src/lib.rs index 35c81393..a4d131f7 100644 --- a/pingora-core/src/lib.rs +++ b/pingora-core/src/lib.rs @@ -57,10 +57,18 @@ pub use pingora_error::{ErrorType::*, *}; #[cfg(all(not(feature = "rustls"), feature = "boringssl"))] pub use pingora_boringssl as tls; -#[cfg(all(not(feature = "rustls"), not(feature = "boringssl"), feature = "openssl"))] +#[cfg(all( + not(feature = "rustls"), + not(feature = "boringssl"), + feature = "openssl" +))] pub use pingora_openssl as tls; -#[cfg(all(not(feature = "boringssl"), not(feature = "openssl"), feature = "rustls"))] +#[cfg(all( + not(feature = "boringssl"), + not(feature = "openssl"), + feature = "rustls" +))] pub use pingora_rustls as tls; pub mod prelude { diff --git a/pingora-core/src/listeners/mod.rs b/pingora-core/src/listeners/mod.rs index 4637229a..9c5c69a3 100644 --- a/pingora-core/src/listeners/mod.rs +++ b/pingora-core/src/listeners/mod.rs @@ -19,15 +19,15 @@ use std::{fs::Permissions, sync::Arc}; use l4::{ListenerEndpoint, Stream as L4Stream}; pub use l4::{ServerAddress, TcpSocketOptions}; use pingora_error::Result; -pub use tls::{ALPN, TlsSettings}; use tls::Acceptor; +pub use tls::{TlsSettings, ALPN}; -use crate::protocols::{IO, Stream}; +pub use crate::protocols::tls::server::TlsAccept; #[cfg(not(feature = "rustls"))] use crate::protocols::tls::TlsStream as TlsStreamProvider; #[cfg(feature = "rustls")] use crate::protocols::tls::TlsStream as TlsStreamProvider; -pub use crate::protocols::tls::server::TlsAccept; +use crate::protocols::{Stream, IO}; use crate::server::ListenFds; mod l4; @@ -85,7 +85,8 @@ pub(crate) struct UninitializedStream { impl UninitializedStream { pub async fn handshake(self) -> Result { if let Some(tls) = self.tls { - let tls_stream : TlsStreamProvider> = tls.handshake(Box::new(self.l4)).await?; + let tls_stream: TlsStreamProvider> = + tls.handshake(Box::new(self.l4)).await?; Ok(Box::new(tls_stream)) } else { Ok(Box::new(self.l4)) @@ -187,7 +188,7 @@ impl Listeners { mod test { use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; - use tokio::time::{Duration, sleep}; + use tokio::time::{sleep, Duration}; use super::*; diff --git a/pingora-core/src/listeners/tls/boringssl_openssl/mod.rs b/pingora-core/src/listeners/tls/boringssl_openssl/mod.rs index 054b7505..c6b4b9d1 100644 --- a/pingora-core/src/listeners/tls/boringssl_openssl/mod.rs +++ b/pingora-core/src/listeners/tls/boringssl_openssl/mod.rs @@ -14,12 +14,12 @@ //! BoringSSL & OpenSSL listener specific implementation -use core::any::Any; -use async_trait::async_trait; -use pingora_error::{ErrorType, OrErr, Result}; -use crate::listeners::{ALPN, TlsSettings}; use crate::listeners::tls::{NativeBuilder, TlsAcceptor, TlsAcceptorBuilder}; +use crate::listeners::{TlsSettings, ALPN}; use crate::tls::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod}; +use async_trait::async_trait; +use core::any::Any; +use pingora_error::{ErrorType, OrErr, Result}; const TLS_CONF_ERR: ErrorType = ErrorType::Custom("TLSConfigError"); struct TlsAcc(SslAcceptor); @@ -47,7 +47,9 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil { } fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result - where Self: Sized { + where + Self: Sized, + { let mut accept_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).or_err( TLS_CONF_ERR, "fail to create mozilla_intermediate_v5 Acceptor", @@ -64,7 +66,9 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil { } fn acceptor_with_callbacks() -> Result - where Self: Sized { + where + Self: Sized, + { let accept_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).or_err( TLS_CONF_ERR, "fail to create mozilla_intermediate_v5 Acceptor", @@ -96,7 +100,7 @@ impl From for TlsSettings { mod alpn { use crate::protocols::ALPN; - use crate::tls::ssl::{AlpnError, select_next_proto, SslRef}; + use crate::tls::ssl::{select_next_proto, AlpnError, SslRef}; // A standard implementation provided by the SSL lib is used below @@ -120,4 +124,4 @@ mod alpn { _ => Err(AlpnError::ALERT_FATAL), // cannot agree } } -} \ No newline at end of file +} diff --git a/pingora-core/src/listeners/tls/mod.rs b/pingora-core/src/listeners/tls/mod.rs index 08961caa..80cb5547 100644 --- a/pingora-core/src/listeners/tls/mod.rs +++ b/pingora-core/src/listeners/tls/mod.rs @@ -17,19 +17,19 @@ use std::any::Any; use async_trait::async_trait; use log::debug; -use pingora_error::Result; #[cfg(not(feature = "rustls"))] use crate::listeners::tls::boringssl_openssl::TlsAcceptorBuil; #[cfg(feature = "rustls")] use crate::listeners::tls::rustls::TlsAcceptorBuil; -use crate::protocols::IO; -pub use crate::protocols::tls::ALPN; #[cfg(not(feature = "rustls"))] use crate::protocols::tls::boringssl_openssl::server::{handshake, handshake_with_callback}; #[cfg(feature = "rustls")] use crate::protocols::tls::rustls::server::{handshake, handshake_with_callback}; use crate::protocols::tls::server::TlsAcceptCallbacks; use crate::protocols::tls::TlsStream; +pub use crate::protocols::tls::ALPN; +use crate::protocols::IO; +use pingora_error::Result; #[cfg(not(feature = "rustls"))] pub mod boringssl_openssl; @@ -56,9 +56,11 @@ pub trait TlsAcceptorBuilder: Any { fn build(self: Box) -> Box; fn set_alpn(&mut self, alpn: ALPN); fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result - where Self: Sized; + where + Self: Sized; fn acceptor_with_callbacks() -> Result - where Self: Sized; + where + Self: Sized; fn as_any(&mut self) -> &mut dyn Any; } @@ -111,7 +113,10 @@ impl TlsSettings { } impl Acceptor { - pub async fn handshake(&self, stream: Box) -> Result>> { + pub async fn handshake( + &self, + stream: Box, + ) -> Result>> { debug!("new tls session"); // TODO: be able to offload this handshake in a thread pool if let Some(cb) = self.callbacks.as_ref() { diff --git a/pingora-core/src/listeners/tls/rustls/mod.rs b/pingora-core/src/listeners/tls/rustls/mod.rs index 87103df8..debec796 100644 --- a/pingora-core/src/listeners/tls/rustls/mod.rs +++ b/pingora-core/src/listeners/tls/rustls/mod.rs @@ -19,23 +19,23 @@ use std::sync::Arc; use async_trait::async_trait; -use pingora_error::{Error, ErrorSource, ImmutStr, OrErr, Result}; use pingora_error::ErrorType::InternalError; -use pingora_rustls::{TlsAcceptor as RusTlsAcceptor, version}; +use pingora_error::{Error, ErrorSource, ImmutStr, OrErr, Result}; use pingora_rustls::load_certs_key_file; use pingora_rustls::ServerConfig; +use pingora_rustls::{version, TlsAcceptor as RusTlsAcceptor}; -use crate::listeners::ALPN; use crate::listeners::tls::{TlsAcceptor, TlsAcceptorBuilder}; +use crate::listeners::ALPN; pub(super) struct TlsAcceptorBuil { alpn_protocols: Option>>, cert_path: String, - key_path: String + key_path: String, } struct TlsAcc { - acceptor: RusTlsAcceptor + acceptor: RusTlsAcceptor, } #[async_trait] @@ -47,21 +47,29 @@ impl TlsAcceptor for TlsAcc { impl TlsAcceptorBuilder for TlsAcceptorBuil { fn build(self: Box) -> Box { - let (certs, key) = load_certs_key_file(&self.cert_path, &self.key_path) - .expect(format!("Failed to load provided certificates \"{}\" or key \"{}\".", self.cert_path, self.key_path).as_str()); + let (certs, key) = load_certs_key_file(&self.cert_path, &self.key_path).expect( + format!( + "Failed to load provided certificates \"{}\" or key \"{}\".", + self.cert_path, self.key_path + ) + .as_str(), + ); - let mut config = ServerConfig::builder_with_protocol_versions(&vec![&version::TLS12, &version::TLS13]) - .with_no_client_auth() - .with_single_cert(certs, key) - .explain_err(InternalError, |e| format!("Failed to create server listener config: {}", e)) - .unwrap(); + let mut config = + ServerConfig::builder_with_protocol_versions(&vec![&version::TLS12, &version::TLS13]) + .with_no_client_auth() + .with_single_cert(certs, key) + .explain_err(InternalError, |e| { + format!("Failed to create server listener config: {}", e) + }) + .unwrap(); if let Some(alpn_protocols) = self.alpn_protocols { config.alpn_protocols = alpn_protocols; } Box::new(TlsAcc { - acceptor: RusTlsAcceptor::from(Arc::new(config)) + acceptor: RusTlsAcceptor::from(Arc::new(config)), }) } fn set_alpn(&mut self, alpn: ALPN) { @@ -69,25 +77,32 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil { } fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result - where Self: Sized { + where + Self: Sized, + { Ok(TlsAcceptorBuil { alpn_protocols: None, cert_path: cert_path.to_string(), - key_path: key_path.to_string() + key_path: key_path.to_string(), }) } fn acceptor_with_callbacks() -> Result - where Self: Sized { + where + Self: Sized, + { // TODO: verify if/how callback in handshake can be done using Rustls - Err(Error::create(InternalError, - ErrorSource::Internal, - Some(ImmutStr::from("Certificate callbacks are not supported with feature \"rustls\".")), - None + Err(Error::create( + InternalError, + ErrorSource::Internal, + Some(ImmutStr::from( + "Certificate callbacks are not supported with feature \"rustls\".", + )), + None, )) } fn as_any(&mut self) -> &mut dyn Any { self as &mut dyn Any } -} \ No newline at end of file +} diff --git a/pingora-core/src/protocols/l4/stream.rs b/pingora-core/src/protocols/l4/stream.rs index fa0a1685..70ecdb31 100644 --- a/pingora-core/src/protocols/l4/stream.rs +++ b/pingora-core/src/protocols/l4/stream.rs @@ -28,7 +28,10 @@ use tokio::net::{TcpStream, UnixStream}; use crate::protocols::l4::ext::{set_tcp_keepalive, TcpKeepalive}; use crate::protocols::raw_connect::ProxyDigest; -use crate::protocols::{GetProxyDigest, GetSocketDigest, GetTimingDigest, IO, Shutdown, SocketDigest, Ssl, TimingDigest, UniqueID}; +use crate::protocols::{ + GetProxyDigest, GetSocketDigest, GetTimingDigest, Shutdown, SocketDigest, Ssl, TimingDigest, + UniqueID, IO, +}; use crate::upstreams::peer::Tracer; #[derive(Debug)] diff --git a/pingora-core/src/protocols/tls/boringssl_openssl/client.rs b/pingora-core/src/protocols/tls/boringssl_openssl/client.rs index 49d35231..7dbf96d2 100644 --- a/pingora-core/src/protocols/tls/boringssl_openssl/client.rs +++ b/pingora-core/src/protocols/tls/boringssl_openssl/client.rs @@ -16,8 +16,8 @@ use pingora_error::{Error, ErrorType::*, OrErr, Result}; -use crate::protocols::IO; use crate::protocols::tls::boringssl_openssl::TlsStream; +use crate::protocols::IO; use crate::tls::ssl::ConnectConfiguration; /// Perform the TLS handshake for the given connection with the given configuration @@ -32,16 +32,13 @@ pub async fn handshake( let mut stream = TlsStream::new(ssl, io) .explain_err(TLSHandshakeFailure, |e| format!("tls stream error: {e}"))?; - stream.connect().await - .map_err(|e| { - let err_msg = format!("TLS connect() failed: {e}, SNI: {domain}"); - if let Some(context) = e.context { - Error::explain(e.etype, - format!("{}, {}", err_msg, context.as_str())) - } else { - Error::explain(e.etype, err_msg) - } - })?; + stream.connect().await.map_err(|e| { + let err_msg = format!("TLS connect() failed: {e}, SNI: {domain}"); + if let Some(context) = e.context { + Error::explain(e.etype, format!("{}, {}", err_msg, context.as_str())) + } else { + Error::explain(e.etype, err_msg) + } + })?; Ok(stream) } - diff --git a/pingora-core/src/protocols/tls/boringssl_openssl/mod.rs b/pingora-core/src/protocols/tls/boringssl_openssl/mod.rs index 47b35a6f..796f11fc 100644 --- a/pingora-core/src/protocols/tls/boringssl_openssl/mod.rs +++ b/pingora-core/src/protocols/tls/boringssl_openssl/mod.rs @@ -21,11 +21,11 @@ use std::task::{Context, Poll}; use tokio::io::{self, AsyncRead, AsyncWrite, ReadBuf}; use pingora_error::ErrorType::TLSHandshakeFailure; -use pingora_error::{Result, OrErr}; +use pingora_error::{OrErr, Result}; -use crate::protocols::{ALPN, Ssl, UniqueID}; use crate::protocols::tls::boringssl_openssl::stream::InnerStream; use crate::protocols::tls::SslDigest; +use crate::protocols::{Ssl, UniqueID, ALPN}; use crate::tls::hash::MessageDigest; use crate::tls::ssl; use crate::tls::ssl::SslRef; @@ -33,13 +33,13 @@ use crate::utils::tls::boringssl_openssl::{get_x509_organization, get_x509_seria use super::TlsStream; -pub(super) mod stream; pub mod client; pub mod server; +pub(super) mod stream; impl TlsStream where - T: AsyncRead + AsyncWrite + Unpin + Send + T: AsyncRead + AsyncWrite + Unpin + Send, { /// Create a new TLS connection from the given `stream` /// @@ -168,4 +168,4 @@ impl SslDigest { cert_digest, } } -} \ No newline at end of file +} diff --git a/pingora-core/src/protocols/tls/boringssl_openssl/server.rs b/pingora-core/src/protocols/tls/boringssl_openssl/server.rs index 639bbcb9..621b63ef 100644 --- a/pingora-core/src/protocols/tls/boringssl_openssl/server.rs +++ b/pingora-core/src/protocols/tls/boringssl_openssl/server.rs @@ -19,13 +19,13 @@ use std::pin::Pin; use async_trait::async_trait; use tokio::io::{AsyncRead, AsyncWrite}; -use pingora_error::{OrErr, Result}; use pingora_error::ErrorType::{TLSHandshakeFailure, TLSWantX509Lookup}; +use pingora_error::{OrErr, Result}; use crate::listeners::tls::Acceptor; -use crate::protocols::{IO, Ssl}; use crate::protocols::tls::boringssl_openssl::TlsStream; use crate::protocols::tls::server::{ResumableAccept, TlsAcceptCallbacks}; +use crate::protocols::{Ssl, IO}; use crate::tls::ext; use crate::tls::ext::ssl_from_acceptor; use crate::tls::ssl::SslAcceptor; @@ -59,14 +59,17 @@ impl ResumableAccept for TlsStream } fn prepare_tls_stream(acceptor: &Acceptor, io: S) -> Result> { - let ssl_acceptor = acceptor.inner().downcast_ref::().unwrap(); + let ssl_acceptor = acceptor.inner().downcast_ref::().unwrap(); let ssl = ssl_from_acceptor(ssl_acceptor) .explain_err(TLSHandshakeFailure, |e| format!("ssl_acceptor error: {e}"))?; TlsStream::new(ssl, io).explain_err(TLSHandshakeFailure, |e| format!("tls stream error: {e}")) } /// Perform TLS handshake for the given connection with the given configuration -pub async fn handshake(acceptor: &Acceptor, io: Box) -> Result>> { +pub async fn handshake( + acceptor: &Acceptor, + io: Box, +) -> Result>> { let mut stream = prepare_tls_stream(acceptor, io)?; stream .accept() @@ -82,9 +85,7 @@ pub async fn handshake_with_callback( callbacks: &TlsAcceptCallbacks, ) -> pingora_error::Result>> { let mut tls_stream = prepare_tls_stream(acceptor, io)?; - let done = Pin::new(&mut tls_stream) - .start_accept() - .await?; + let done = Pin::new(&mut tls_stream).start_accept().await?; if !done { // safety: we do hold a mut ref of tls_stream let ssl_mut = unsafe { ext::ssl_mut(tls_stream.0.ssl()) }; @@ -101,12 +102,12 @@ pub async fn handshake_with_callback( #[tokio::test] async fn test_async_cert() { - use crate::tls::ssl; - use tokio::io::AsyncReadExt; - use crate::tls::ssl::SslRef; - use crate::listeners::TlsAccept; use crate::listeners::tls::TlsSettings; + use crate::listeners::TlsAccept; use crate::protocols::tls::server::TlsAcceptCallbacks; + use crate::tls::ssl; + use crate::tls::ssl::SslRef; + use tokio::io::AsyncReadExt; struct Callback; #[async_trait] @@ -148,4 +149,4 @@ async fn test_async_cert() { let acceptor = TlsSettings::with_callbacks(cb).unwrap().build(); acceptor.handshake(Box::new(server)).await.unwrap(); -} \ No newline at end of file +} diff --git a/pingora-core/src/protocols/tls/boringssl_openssl/stream.rs b/pingora-core/src/protocols/tls/boringssl_openssl/stream.rs index 9a3a07c9..ef416816 100644 --- a/pingora-core/src/protocols/tls/boringssl_openssl/stream.rs +++ b/pingora-core/src/protocols/tls/boringssl_openssl/stream.rs @@ -14,24 +14,24 @@ //! BoringSSL & OpenSSL TLS stream specific implementation -use std::pin::Pin; -use std::sync::Arc; use async_trait::async_trait; use log::warn; +use std::pin::Pin; +use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite}; use pingora_error::{Error, ErrorType::*, OrErr, Result}; use crate::listeners::ALPN; -use crate::protocols::{GetProxyDigest, GetTimingDigest}; use crate::protocols::digest::{GetSocketDigest, SocketDigest, TimingDigest}; use crate::protocols::raw_connect::ProxyDigest; use crate::protocols::tls::InnerTlsStream; use crate::protocols::tls::SslDigest; -use crate::tls::tokio_ssl::SslStream; +use crate::protocols::{GetProxyDigest, GetTimingDigest}; use crate::tls::error::ErrorStack; use crate::tls::ext; use crate::tls::tokio_ssl; +use crate::tls::tokio_ssl::SslStream; use crate::tls::{ssl, ssl::SslRef, ssl_sys::X509_V_ERR_INVALID_CALL}; #[derive(Debug)] @@ -64,12 +64,8 @@ impl InnerTlsStream for InnerStream async fn connect(&mut self) -> Result<()> { Self::clear_error(); match Pin::new(&mut self.0).connect().await { - Ok(_) => { - Ok(()) - } - Err(err) => { - self.transform_ssl_error(err) - } + Ok(_) => Ok(()), + Err(err) => self.transform_ssl_error(err), } } @@ -77,12 +73,8 @@ impl InnerTlsStream for InnerStream async fn accept(&mut self) -> Result<()> { Self::clear_error(); match Pin::new(&mut self.0).accept().await { - Ok(_) => { - Ok(()) - } - Err(err) => { - self.transform_ssl_error(err) - } + Ok(_) => Ok(()), + Err(err) => self.transform_ssl_error(err), } } @@ -136,8 +128,8 @@ impl InnerStream { } impl GetSocketDigest for InnerStream - where - S: GetSocketDigest, +where + S: GetSocketDigest, { fn get_socket_digest(&self) -> Option> { self.0.get_ref().get_socket_digest() @@ -148,8 +140,8 @@ impl GetSocketDigest for InnerStream } impl GetTimingDigest for InnerStream - where - S: GetTimingDigest, +where + S: GetTimingDigest, { fn get_timing_digest(&self) -> Vec> { self.0.get_ref().get_timing_digest() @@ -157,10 +149,10 @@ impl GetTimingDigest for InnerStream } impl GetProxyDigest for InnerStream - where - S: GetProxyDigest, +where + S: GetProxyDigest, { fn get_proxy_digest(&self) -> Option> { self.0.get_ref().get_proxy_digest() } -} \ No newline at end of file +} diff --git a/pingora-core/src/protocols/tls/mod.rs b/pingora-core/src/protocols/tls/mod.rs index 77203f81..f9b1c06d 100644 --- a/pingora-core/src/protocols/tls/mod.rs +++ b/pingora-core/src/protocols/tls/mod.rs @@ -14,29 +14,30 @@ //! The TLS layer implementations +use async_trait::async_trait; +use pingora_error::Result; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use std::time::{Duration, SystemTime}; -use async_trait::async_trait; use tokio::io::{AsyncRead, AsyncWrite}; -use pingora_error::Result; -use crate::protocols::{GetProxyDigest, GetSocketDigest, GetTimingDigest, IO, SocketDigest, UniqueID}; use crate::protocols::digest::TimingDigest; use crate::protocols::raw_connect::ProxyDigest; +use crate::protocols::{ + GetProxyDigest, GetSocketDigest, GetTimingDigest, SocketDigest, UniqueID, IO, +}; -pub mod server; #[cfg(not(feature = "rustls"))] pub(crate) mod boringssl_openssl; #[cfg(feature = "rustls")] pub(crate) mod rustls; +pub mod server; #[cfg(not(feature = "rustls"))] use boringssl_openssl::stream::InnerStream; #[cfg(feature = "rustls")] use rustls::stream::InnerStream; - /// The TLS connection #[derive(Debug)] pub struct TlsStream { @@ -57,9 +58,7 @@ pub trait InnerTlsStream { fn selected_alpn_proto(&mut self) -> Option; } - -impl GetSocketDigest for Box -{ +impl GetSocketDigest for Box { fn get_socket_digest(&self) -> Option> { (**self).get_socket_digest() } @@ -68,28 +67,24 @@ impl GetSocketDigest for Box } } -impl GetTimingDigest for Box -{ +impl GetTimingDigest for Box { fn get_timing_digest(&self) -> Vec> { vec![] } } -impl GetProxyDigest for Box -{ +impl GetProxyDigest for Box { fn get_proxy_digest(&self) -> Option> { (**self).get_proxy_digest() } } -impl UniqueID for Box -{ +impl UniqueID for Box { fn id(&self) -> i32 { (**self).id() } } - /// The protocol for Application-Layer Protocol Negotiation #[derive(Hash, Clone, Debug)] pub enum ALPN { @@ -155,7 +150,7 @@ impl ALPN { match self { ALPN::H1 => vec![b"http/1.1".to_vec()], ALPN::H2 => vec![b"h2".to_vec()], - ALPN::H2H1 => vec![b"h2".to_vec(), b"http/1.1".to_vec()] + ALPN::H2H1 => vec![b"h2".to_vec(), b"http/1.1".to_vec()], } } @@ -183,7 +178,6 @@ pub struct SslDigest { pub cert_digest: Vec, } - impl GetSocketDigest for TlsStream where S: GetSocketDigest, @@ -231,7 +225,7 @@ impl TlsStream { impl TlsStream where - T: AsyncRead + AsyncWrite + Unpin + Send + T: AsyncRead + AsyncWrite + Unpin + Send, { /// Connect to the remote TLS server as a client pub(crate) async fn connect(&mut self) -> Result<()> { diff --git a/pingora-core/src/protocols/tls/rustls/client.rs b/pingora-core/src/protocols/tls/rustls/client.rs index 9ed65ad3..bb9edc27 100644 --- a/pingora-core/src/protocols/tls/rustls/client.rs +++ b/pingora-core/src/protocols/tls/rustls/client.rs @@ -14,11 +14,11 @@ //! Rustls TLS client specific implementation -use pingora_error::{Error, OrErr, Result}; +use crate::protocols::tls::rustls::TlsStream; +use crate::protocols::IO; use pingora_error::ErrorType::TLSHandshakeFailure; +use pingora_error::{Error, OrErr, Result}; use pingora_rustls::TlsConnector; -use crate::protocols::IO; -use crate::protocols::tls::rustls::TlsStream; // Perform the TLS handshake for the given connection with the given configuration pub async fn handshake( @@ -26,8 +26,11 @@ pub async fn handshake( domain: &str, io: S, ) -> Result> { - let mut stream = TlsStream::from_connector(connector, domain, io).await - .explain_err(TLSHandshakeFailure, |e| format!("tip: tls stream error: {e}"))?; + let mut stream = TlsStream::from_connector(connector, domain, io) + .await + .explain_err(TLSHandshakeFailure, |e| { + format!("tip: tls stream error: {e}") + })?; let handshake_result = stream.connect().await; match handshake_result { @@ -38,4 +41,3 @@ pub async fn handshake( } } } - diff --git a/pingora-core/src/protocols/tls/rustls/mod.rs b/pingora-core/src/protocols/tls/rustls/mod.rs index 5d59603b..4dbae68e 100644 --- a/pingora-core/src/protocols/tls/rustls/mod.rs +++ b/pingora-core/src/protocols/tls/rustls/mod.rs @@ -18,19 +18,19 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; -use tokio::io::{self, AsyncRead, AsyncWrite, ReadBuf}; -use x509_parser::nom::AsBytes; -use pingora_error::{OrErr, Result}; use pingora_error::ErrorType::{InternalError, TLSHandshakeFailure}; -use pingora_rustls::{hash_certificate, ServerName, TlsConnector}; +use pingora_error::{OrErr, Result}; use pingora_rustls::TlsStream as RusTlsStream; +use pingora_rustls::{hash_certificate, ServerName, TlsConnector}; +use tokio::io::{self, AsyncRead, AsyncWrite, ReadBuf}; +use x509_parser::nom::AsBytes; -use crate::utils::tls::rustls::{get_organization_serial}; +use crate::utils::tls::rustls::get_organization_serial; -use crate::listeners::tls::{Acceptor}; -use crate::protocols::{ALPN, Ssl, UniqueID}; +use crate::listeners::tls::Acceptor; use crate::protocols::tls::rustls::stream::InnerStream; use crate::protocols::tls::SslDigest; +use crate::protocols::{Ssl, UniqueID, ALPN}; use super::TlsStream; @@ -49,10 +49,13 @@ where pub async fn from_connector(connector: &TlsConnector, domain: &str, stream: T) -> Result { let server = ServerName::try_from(domain) .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) - .explain_err(InternalError, |e| format!("failed to parse domain: {}, error: {}", domain, e))? + .explain_err(InternalError, |e| { + format!("failed to parse domain: {}, error: {}", domain, e) + })? .to_owned(); - let tls = InnerStream::from_connector(connector, server, stream).await + let tls = InnerStream::from_connector(connector, server, stream) + .await .explain_err(TLSHandshakeFailure, |e| format!("tls stream error: {e}"))?; Ok(TlsStream { @@ -67,7 +70,8 @@ where /// Using RustTLS the stream is only returned after the handshake. /// The caller does therefor not need to perform [`Self::accept()`]. pub(crate) async fn from_acceptor(acceptor: &Acceptor, stream: T) -> Result { - let tls = InnerStream::from_acceptor(acceptor, stream).await + let tls = InnerStream::from_acceptor(acceptor, stream) + .await .explain_err(TLSHandshakeFailure, |e| format!("tls stream error: {e}"))?; Ok(TlsStream { @@ -143,10 +147,8 @@ impl Ssl for TlsStream { if let Some(stream) = st { let proto = stream.get_ref().1.alpn_protocol(); match proto { - None => { None } - Some(raw) => { - ALPN::from_wire_selected(raw) - } + None => None, + Some(raw) => ALPN::from_wire_selected(raw), } } else { None @@ -163,44 +165,36 @@ impl SslDigest { let peer_certificates = session.peer_certificates(); let cipher = match cipher_suite { - Some(suite) => { - match suite.suite().as_str() { - Some(suite_str) => suite_str, - None => "", - } + Some(suite) => match suite.suite().as_str() { + Some(suite_str) => suite_str, + None => "", }, None => "", }; let version = match protocol { - Some(proto) => { - match proto.as_str() { - Some(ver) => ver, - None => "", - } + Some(proto) => match proto.as_str() { + Some(ver) => ver, + None => "", }, None => "", }; let cert_digest = match peer_certificates { - Some(certs) => { - match certs.first() { - Some(cert) => hash_certificate(cert.clone()), - None => vec![], - } + Some(certs) => match certs.first() { + Some(cert) => hash_certificate(cert.clone()), + None => vec![], }, None => vec![], }; let (organization, serial_number) = match peer_certificates { - Some(certs) => { - match certs.first() { - Some(cert) => { - let (organization, serial) = get_organization_serial(cert.as_bytes()); - (organization, Some(serial)) - }, - None => (None, None), + Some(certs) => match certs.first() { + Some(cert) => { + let (organization, serial) = get_organization_serial(cert.as_bytes()); + (organization, Some(serial)) } + None => (None, None), }, None => (None, None), }; @@ -213,4 +207,4 @@ impl SslDigest { cert_digest, } } -} \ No newline at end of file +} diff --git a/pingora-core/src/protocols/tls/rustls/server.rs b/pingora-core/src/protocols/tls/rustls/server.rs index ad67fa58..83a9d7df 100644 --- a/pingora-core/src/protocols/tls/rustls/server.rs +++ b/pingora-core/src/protocols/tls/rustls/server.rs @@ -14,15 +14,15 @@ //! Rustls TLS server specific implementation -use std::pin::Pin; -use async_trait::async_trait; -use log::warn; -use tokio::io::{AsyncRead, AsyncWrite}; +use crate::listeners::tls::Acceptor; use crate::protocols::tls::rustls::TlsStream; use crate::protocols::tls::server::{ResumableAccept, TlsAcceptCallbacks}; -use pingora_error::{ErrorType::*, OrErr, Result}; use crate::protocols::IO; -use crate::listeners::tls::Acceptor; +use async_trait::async_trait; +use log::warn; +use pingora_error::{ErrorType::*, OrErr, Result}; +use std::pin::Pin; +use tokio::io::{AsyncRead, AsyncWrite}; #[async_trait] impl ResumableAccept for TlsStream { @@ -49,12 +49,16 @@ impl ResumableAccept for TlsStream } async fn prepare_tls_stream(acceptor: &Acceptor, io: S) -> Result> { - TlsStream::from_acceptor(acceptor, io).await + TlsStream::from_acceptor(acceptor, io) + .await .explain_err(TLSHandshakeFailure, |e| format!("tls stream error: {e}")) } /// Perform TLS handshake for the given connection with the given configuration -pub async fn handshake(acceptor: &Acceptor, io: Box) -> Result>> { +pub async fn handshake( + acceptor: &Acceptor, + io: Box, +) -> Result>> { let mut stream = prepare_tls_stream(acceptor, io).await?; stream .accept() @@ -71,9 +75,7 @@ pub async fn handshake_with_callback( _callbacks: &TlsAcceptCallbacks, ) -> Result>> { let mut tls_stream = prepare_tls_stream(acceptor, io).await?; - let done = Pin::new(&mut tls_stream) - .start_accept() - .await?; + let done = Pin::new(&mut tls_stream).start_accept().await?; if !done { // TODO: verify if/how callback in handshake can be done using Rustls warn!("Callacks are not supported with feature \"rustls\"."); @@ -92,4 +94,4 @@ pub async fn handshake_with_callback( #[tokio::test] async fn test_async_cert() { todo!("callback support and test for Rustls") -} \ No newline at end of file +} diff --git a/pingora-core/src/protocols/tls/rustls/stream.rs b/pingora-core/src/protocols/tls/rustls/stream.rs index 4c3556d5..349e056a 100644 --- a/pingora-core/src/protocols/tls/rustls/stream.rs +++ b/pingora-core/src/protocols/tls/rustls/stream.rs @@ -12,24 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::fmt::Debug; -use std::sync::Arc; use async_trait::async_trait; -use tokio::io::{AsyncRead, AsyncWrite}; -use pingora_error::{Error, ImmutStr, OrErr, Result}; use pingora_error::ErrorType::{AcceptError, ConnectError, TLSHandshakeFailure}; -use pingora_rustls::{Accept, Connect, ServerName, TlsConnector}; +use pingora_error::{Error, ImmutStr, OrErr, Result}; +use pingora_rustls::NoDebug; use pingora_rustls::TlsAcceptor as RusTlsAcceptor; use pingora_rustls::TlsStream as RusTlsStream; -use pingora_rustls::NoDebug; +use pingora_rustls::{Accept, Connect, ServerName, TlsConnector}; +use std::fmt::Debug; +use std::sync::Arc; +use tokio::io::{AsyncRead, AsyncWrite}; -use crate::listeners::ALPN; use crate::listeners::tls::Acceptor; -use crate::protocols::{GetProxyDigest, GetTimingDigest}; +use crate::listeners::ALPN; use crate::protocols::digest::{GetSocketDigest, SocketDigest, TimingDigest}; use crate::protocols::raw_connect::ProxyDigest; use crate::protocols::tls::InnerTlsStream; use crate::protocols::tls::SslDigest; +use crate::protocols::{GetProxyDigest, GetTimingDigest}; #[derive(Debug)] pub struct InnerStream { @@ -43,7 +43,11 @@ impl InnerStream { /// /// The caller needs to perform [`Self::connect()`] or [`Self::accept()`] to perform TLS /// handshake after. - pub(crate) async fn from_connector(connector: &TlsConnector, server: ServerName<'_>, stream: T) -> Result { + pub(crate) async fn from_connector( + connector: &TlsConnector, + server: ServerName<'_>, + stream: T, + ) -> Result { let connect = connector.connect(server.to_owned(), stream); Ok(InnerStream { accept: None.into(), @@ -53,7 +57,7 @@ impl InnerStream { } pub(crate) async fn from_acceptor(acceptor: &Acceptor, stream: T) -> Result { - let tls_acceptor = acceptor.inner().downcast_ref::().unwrap(); + let tls_acceptor = acceptor.inner().downcast_ref::().unwrap(); let accept = tls_acceptor.accept(stream); Ok(InnerStream { @@ -71,14 +75,18 @@ impl InnerTlsStream for InnerStream let connect = &mut (*self.connect); if let Some(ref mut connect) = connect { - let stream = connect.await + let stream = connect + .await .explain_err(TLSHandshakeFailure, |e| format!("tls connect error: {e}"))?; self.stream = Some(RusTlsStream::Client(stream)); self.connect = None.into(); Ok(()) } else { - Err(Error::explain(ConnectError, ImmutStr::from("TLS connect not available to perform handshake."))) + Err(Error::explain( + ConnectError, + ImmutStr::from("TLS connect not available to perform handshake."), + )) } } @@ -88,14 +96,18 @@ impl InnerTlsStream for InnerStream let accept = &mut (*self.accept); if let Some(ref mut accept) = accept { - let stream = accept.await + let stream = accept + .await .explain_err(TLSHandshakeFailure, |e| format!("tls connect error: {e}"))?; self.stream = Some(RusTlsStream::Server(stream)); self.connect = None.into(); Ok(()) } else { - Err(Error::explain(AcceptError, ImmutStr::from("TLS accept not available to perform handshake."))) + Err(Error::explain( + AcceptError, + ImmutStr::from("TLS accept not available to perform handshake."), + )) } } @@ -107,10 +119,8 @@ impl InnerTlsStream for InnerStream if let Some(stream) = self.stream.as_ref() { let proto = stream.get_ref().1.alpn_protocol(); match proto { - None => { None } - Some(raw) => { - ALPN::from_wire_selected(raw) - } + None => None, + Some(raw) => ALPN::from_wire_selected(raw), } } else { None @@ -118,10 +128,9 @@ impl InnerTlsStream for InnerStream } } - impl GetSocketDigest for InnerStream - where - S: GetSocketDigest, +where + S: GetSocketDigest, { fn get_socket_digest(&self) -> Option> { if let Some(stream) = self.stream.as_ref() { @@ -131,22 +140,32 @@ impl GetSocketDigest for InnerStream } } fn set_socket_digest(&mut self, socket_digest: SocketDigest) { - self.stream.as_mut().unwrap().get_mut().0.set_socket_digest(socket_digest) + self.stream + .as_mut() + .unwrap() + .get_mut() + .0 + .set_socket_digest(socket_digest) } } impl GetTimingDigest for InnerStream - where - S: GetTimingDigest, +where + S: GetTimingDigest, { fn get_timing_digest(&self) -> Vec> { - self.stream.as_ref().unwrap().get_ref().0.get_timing_digest() + self.stream + .as_ref() + .unwrap() + .get_ref() + .0 + .get_timing_digest() } } impl GetProxyDigest for InnerStream - where - S: GetProxyDigest, +where + S: GetProxyDigest, { fn get_proxy_digest(&self) -> Option> { if let Some(stream) = self.stream.as_ref() { @@ -155,4 +174,4 @@ impl GetProxyDigest for InnerStream None } } -} \ No newline at end of file +} diff --git a/pingora-core/src/protocols/tls/server.rs b/pingora-core/src/protocols/tls/server.rs index 948d40fd..c0834af4 100644 --- a/pingora-core/src/protocols/tls/server.rs +++ b/pingora-core/src/protocols/tls/server.rs @@ -22,8 +22,8 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; use pingora_error::Result; -use crate::protocols::{IO, Shutdown}; use crate::protocols::tls::TlsStream; +use crate::protocols::{Shutdown, IO}; #[cfg(not(feature = "rustls"))] use crate::tls::ssl::SslRef; @@ -58,8 +58,7 @@ where } #[async_trait] -impl Shutdown for Box -{ +impl Shutdown for Box { async fn shutdown(&mut self) { match ::shutdown(self).await { Ok(()) => {} @@ -85,4 +84,4 @@ pub trait ResumableAccept { /// /// This function should be called after the certificate is provided. async fn resume_accept(self: Pin<&mut Self>) -> Result<()>; -} \ No newline at end of file +} diff --git a/pingora-core/src/upstreams/mod.rs b/pingora-core/src/upstreams/mod.rs index 663a4973..7352b615 100644 --- a/pingora-core/src/upstreams/mod.rs +++ b/pingora-core/src/upstreams/mod.rs @@ -14,4 +14,4 @@ //! The interface to connect to a remote server -pub mod peer; \ No newline at end of file +pub mod peer; diff --git a/pingora-core/src/upstreams/peer.rs b/pingora-core/src/upstreams/peer.rs index 340311a2..848c47a9 100644 --- a/pingora-core/src/upstreams/peer.rs +++ b/pingora-core/src/upstreams/peer.rs @@ -32,11 +32,11 @@ use std::time::Duration; use crate::protocols::l4::socket::SocketAddr; use crate::protocols::ConnFdReusable; use crate::protocols::TcpKeepalive; -use crate::utils::tls::CertKey; #[cfg(not(feature = "rustls"))] -use crate::utils::tls::boringssl_openssl::{get_organizational_unit, get_not_after}; +use crate::utils::tls::boringssl_openssl::{get_not_after, get_organizational_unit}; #[cfg(feature = "rustls")] -use crate::utils::tls::rustls::{get_organizational_unit, get_not_after}; +use crate::utils::tls::rustls::{get_not_after, get_organizational_unit}; +use crate::utils::tls::CertKey; pub use crate::protocols::tls::ALPN; diff --git a/pingora-core/src/utils/tls/boringssl_openssl/mod.rs b/pingora-core/src/utils/tls/boringssl_openssl/mod.rs index a6b68667..9909fea9 100644 --- a/pingora-core/src/utils/tls/boringssl_openssl/mod.rs +++ b/pingora-core/src/utils/tls/boringssl_openssl/mod.rs @@ -87,13 +87,21 @@ pub fn get_x509_serial(cert: &X509) -> pingora_error::Result { } pub fn der_to_x509(ca: &[u8]) -> pingora_error::Result { - let cert = X509::from_der(&*ca) - .explain_err(InvalidCert, |e| format!("Failed to convert ca certificate in DER form to X509 cert. Error: {:?}", e))?; + let cert = X509::from_der(&*ca).explain_err(InvalidCert, |e| { + format!( + "Failed to convert ca certificate in DER form to X509 cert. Error: {:?}", + e + ) + })?; Ok(cert) } pub fn der_to_private_key(key: &[u8]) -> pingora_error::Result> { - let key = PKey::private_key_from_der(key) - .explain_err(InternalError, |e| format!("Failed to convert private key in DER form to Pkey. Error: {:?}", e))?; + let key = PKey::private_key_from_der(key).explain_err(InternalError, |e| { + format!( + "Failed to convert private key in DER form to Pkey. Error: {:?}", + e + ) + })?; Ok(key) -} \ No newline at end of file +} diff --git a/pingora-core/src/utils/tls/mod.rs b/pingora-core/src/utils/tls/mod.rs index bab25a19..fc4447ad 100644 --- a/pingora-core/src/utils/tls/mod.rs +++ b/pingora-core/src/utils/tls/mod.rs @@ -20,11 +20,15 @@ pub mod boringssl_openssl; #[cfg(feature = "rustls")] pub mod rustls; -use std::hash::{Hash, Hasher}; #[cfg(not(feature = "rustls"))] -use boringssl_openssl::{get_organization, get_serial, get_common_name, get_organizational_unit, get_not_after}; +use boringssl_openssl::{ + get_common_name, get_not_after, get_organization, get_organizational_unit, get_serial, +}; #[cfg(feature = "rustls")] -use rustls::{get_organization, get_serial, get_common_name, get_organizational_unit, get_not_after}; +use rustls::{ + get_common_name, get_not_after, get_organization, get_organizational_unit, get_serial, +}; +use std::hash::{Hash, Hasher}; /// This type contains a list of one or more certificates and an associated private key. The leaf /// certificate should always be first. The certificates and keys are stored in Vec DER encoded @@ -105,4 +109,4 @@ impl Hash for CertKey { } } } -} \ No newline at end of file +} diff --git a/pingora-core/src/utils/tls/rustls/mod.rs b/pingora-core/src/utils/tls/rustls/mod.rs index 7a857858..41520d79 100644 --- a/pingora-core/src/utils/tls/rustls/mod.rs +++ b/pingora-core/src/utils/tls/rustls/mod.rs @@ -14,8 +14,8 @@ //! This module contains various helpers that make it easier to work with X509 certificates. -use x509_parser::prelude::FromDer; use pingora_error::Result; +use x509_parser::prelude::FromDer; pub fn get_organization_serial(cert: &[u8]) -> (Option, String) { let serial = get_serial(cert).expect("Failed to get serial for certificate."); @@ -32,7 +32,9 @@ pub fn get_serial(cert: &[u8]) -> Result { pub fn get_organization(cert: &[u8]) -> Option { let (_, x509cert) = x509_parser::certificate::X509Certificate::from_der(cert) .expect("Failed to parse certificate from DER format."); - x509cert.subject.iter_organization() + x509cert + .subject + .iter_organization() .filter_map(|a| a.as_str().ok()) .map(|a| a.to_string()) .reduce(|cur, next| cur + &next) @@ -42,7 +44,9 @@ pub fn get_organization(cert: &[u8]) -> Option { pub fn get_organizational_unit(cert: &[u8]) -> Option { let (_, x509cert) = x509_parser::certificate::X509Certificate::from_der(cert) .expect("Failed to parse certificate from DER format."); - x509cert.subject.iter_organizational_unit() + x509cert + .subject + .iter_organizational_unit() .filter_map(|a| a.as_str().ok()) .map(|a| a.to_string()) .reduce(|cur, next| cur + &next) @@ -58,9 +62,10 @@ pub fn get_not_after(cert: &[u8]) -> String { pub fn get_common_name(cert: &[u8]) -> Option { let (_, x509cert) = x509_parser::certificate::X509Certificate::from_der(cert) .expect("Failed to parse certificate from DER format."); - x509cert.subject.iter_common_name() + x509cert + .subject + .iter_common_name() .filter_map(|a| a.as_str().ok()) .map(|a| a.to_string()) .reduce(|cur, next| cur + &next) } - diff --git a/pingora-proxy/tests/test_basic.rs b/pingora-proxy/tests/test_basic.rs index 6647d8a7..34e5933a 100644 --- a/pingora-proxy/tests/test_basic.rs +++ b/pingora-proxy/tests/test_basic.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hyper::{Body, body::HttpBody, Client, header::HeaderValue}; +use hyper::{body::HttpBody, header::HeaderValue, Body, Client}; use hyperlocal::{UnixClientExt, Uri}; use reqwest::{header, StatusCode}; @@ -73,9 +73,12 @@ async fn test_h2_to_h1() { .build() .unwrap(); - let res = client.get("https://127.0.0.1:6150") + let res = client + .get("https://127.0.0.1:6150") .header("sni", "openrusty.org") - .send().await.unwrap(); + .send() + .await + .unwrap(); assert_eq!(res.status(), reqwest::StatusCode::OK); assert_eq!(res.version(), reqwest::Version::HTTP_2); @@ -294,7 +297,10 @@ async fn test_simple_proxy_uds_peer() { assert!(is_specified_port(sockaddr.port())); assert_eq!(headers["x-upstream-client-addr"], "unset"); // unnamed UDS - assert_eq!(headers["x-upstream-server-addr"], "/tmp/pingora_nginx_test.sock"); + assert_eq!( + headers["x-upstream-server-addr"], + "/tmp/pingora_nginx_test.sock" + ); let body = res.text().await.unwrap(); assert_eq!(body, "Hello World!\n"); diff --git a/pingora-proxy/tests/utils/cert.rs b/pingora-proxy/tests/utils/cert.rs index ce47a874..0ff149c3 100644 --- a/pingora-proxy/tests/utils/cert.rs +++ b/pingora-proxy/tests/utils/cert.rs @@ -15,10 +15,13 @@ use once_cell::sync::Lazy; use std::fs; -#[cfg(not(feature = "rustls"))] -use pingora_core::tls::{pkey::{PKey, Private}, x509::X509}; #[cfg(feature = "rustls")] use pingora_core::tls::{load_pem_file_ca, load_pem_file_private_key}; +#[cfg(not(feature = "rustls"))] +use pingora_core::tls::{ + pkey::{PKey, Private}, + x509::X509, +}; //pub static ROOT_CERT: Lazy = Lazy::new(|| load_cert("keys/root.crt")); //pub static ROOT_KEY: Lazy> = Lazy::new(|| load_key("keys/root.key")); @@ -47,7 +50,10 @@ fn load_cert(path: &str) -> Vec { fn load_key(path: &str) -> Vec { let path = format!("{}/{path}", super::conf_dir()); let key_bytes = fs::read(path).unwrap(); - PKey::private_key_from_pem(&key_bytes).unwrap().private_key_to_der().unwrap() + PKey::private_key_from_pem(&key_bytes) + .unwrap() + .private_key_to_der() + .unwrap() } #[cfg(feature = "rustls")] diff --git a/pingora-proxy/tests/utils/mock_origin.rs b/pingora-proxy/tests/utils/mock_origin.rs index 5d266096..de13be69 100644 --- a/pingora-proxy/tests/utils/mock_origin.rs +++ b/pingora-proxy/tests/utils/mock_origin.rs @@ -13,21 +13,30 @@ // limitations under the License. use once_cell::sync::Lazy; +use std::path::Path; use std::process; use std::{thread, time}; -use std::path::Path; pub static MOCK_ORIGIN: Lazy = Lazy::new(init); fn init() -> bool { #[cfg(feature = "rustls")] - let src_cert_path = format!("{}/tests/utils/conf/keys/server_rustls.crt", env!("CARGO_MANIFEST_DIR")); + let src_cert_path = format!( + "{}/tests/utils/conf/keys/server_rustls.crt", + env!("CARGO_MANIFEST_DIR") + ); #[cfg(not(feature = "rustls"))] - let src_cert_path = format!("{}/tests/utils/conf/keys/server_boringssl_openssl.crt", env!("CARGO_MANIFEST_DIR")); + let src_cert_path = format!( + "{}/tests/utils/conf/keys/server_boringssl_openssl.crt", + env!("CARGO_MANIFEST_DIR") + ); let mut dst_cert_path = format!("{}/tests/keys/server.crt", env!("CARGO_MANIFEST_DIR")); std::fs::copy(Path::new(&src_cert_path), Path::new(&dst_cert_path)); - dst_cert_path = format!("{}/tests/utils/conf/keys/server.crt", env!("CARGO_MANIFEST_DIR")); + dst_cert_path = format!( + "{}/tests/utils/conf/keys/server.crt", + env!("CARGO_MANIFEST_DIR") + ); std::fs::copy(Path::new(&src_cert_path), Path::new(&dst_cert_path)); // TODO: figure out a way to kill openresty when exiting diff --git a/pingora-rustls/src/lib.rs b/pingora-rustls/src/lib.rs index bb52474e..2f7c6ecb 100644 --- a/pingora-rustls/src/lib.rs +++ b/pingora-rustls/src/lib.rs @@ -18,29 +18,34 @@ use std::fs::File; use std::io::BufReader; use log::{error, warn}; -pub use rustls::{ClientConfig, RootCertStore, ServerConfig, Stream, version}; +pub use no_debug::{Ellipses, NoDebug, WithTypeInfo}; +pub use rustls::{version, ClientConfig, RootCertStore, ServerConfig, Stream}; pub use rustls_native_certs::load_native_certs; use rustls_pemfile::Item; pub use rustls_pki_types::{CertificateDer, PrivateKeyDer, ServerName}; -pub use tokio_rustls::{Accept, Connect, TlsAcceptor, TlsConnector, TlsStream}; pub use tokio_rustls::client::TlsStream as ClientTlsStream; pub use tokio_rustls::server::TlsStream as ServerTlsStream; -pub use no_debug::{NoDebug, WithTypeInfo, Ellipses}; +pub use tokio_rustls::{Accept, Connect, TlsAcceptor, TlsConnector, TlsStream}; fn load_file(path: &String) -> BufReader { let file = File::open(path).expect("io error"); BufReader::new(file) } fn load_pem_file(path: &String) -> Result, std::io::Error> { - let iter: Vec = rustls_pemfile::read_all(&mut load_file(path)).filter_map(|f| { - if f.is_ok() { - Some(f.unwrap()) - } else { - let err = f.err().unwrap(); - warn!("Skipping PEM element in file \"{}\" due to error \"{}\"", path, err); - None - } - }).collect(); + let iter: Vec = rustls_pemfile::read_all(&mut load_file(path)) + .filter_map(|f| { + if f.is_ok() { + Some(f.unwrap()) + } else { + let err = f.err().unwrap(); + warn!( + "Skipping PEM element in file \"{}\" due to error \"{}\"", + path, err + ); + None + } + }) + .collect(); Ok(iter) } @@ -51,12 +56,12 @@ pub fn load_ca_file_into_store(path: &String, cert_store: &mut RootCertStore) { cas.into_iter().for_each(|pem_item| { // only loading certificates, handling a CA file match pem_item { - Item::X509Certificate(content) => { - match cert_store.add(content) { - Ok(_) => {} - Err(err) => { error!("{}", err) } + Item::X509Certificate(content) => match cert_store.add(content) { + Ok(_) => {} + Err(err) => { + error!("{}", err) } - } + }, Item::Pkcs1Key(_) => {} Item::Pkcs8Key(_) => {} Item::Sec1Key(_) => {} @@ -67,7 +72,10 @@ pub fn load_ca_file_into_store(path: &String, cert_store: &mut RootCertStore) { }); } Err(err) => { - error!("Failed to load configured ca file located at \"{}\", error: \"{}\"", path, err); + error!( + "Failed to load configured ca file located at \"{}\", error: \"{}\"", + path, err + ); } } } @@ -82,38 +90,34 @@ pub fn load_platform_certs_incl_env_into_store(ca_certs: &mut RootCertStore) { } } Err(err) => { - error!("Failed to load native platform ca-certificates: \"{:?}\". Continuing without ...", err); + error!( + "Failed to load native platform ca-certificates: \"{:?}\". Continuing without ...", + err + ); } } } -pub fn load_certs_key_file<'a>(cert: &String, key: &String) -> Option<(Vec>, PrivateKeyDer<'a>)> { +pub fn load_certs_key_file<'a>( + cert: &String, + key: &String, +) -> Option<(Vec>, PrivateKeyDer<'a>)> { let certs_file = load_pem_file(cert) .expect(format!("Failed to load configured cert file located at {}.", cert).as_str()); let key_file = load_pem_file(key) .expect(format!("Failed to load configured key file located at {}.", cert).as_str()); let mut certs: Vec> = vec![]; - certs_file.into_iter().for_each(|i| { - match i { - Item::X509Certificate(cert) => { - certs.push(cert) - } - _ => {} - } + certs_file.into_iter().for_each(|i| match i { + Item::X509Certificate(cert) => certs.push(cert), + _ => {} }); let private_key = match key_file.into_iter().next()? { - Item::Pkcs1Key(key) => { - Some(PrivateKeyDer::from(key)) - } - Item::Pkcs8Key(key) => { - Some(PrivateKeyDer::from(key)) - } - Item::Sec1Key(key) => { - Some(PrivateKeyDer::from(key)) - } - _ => { None } + Item::Pkcs1Key(key) => Some(PrivateKeyDer::from(key)), + Item::Pkcs8Key(key) => Some(PrivateKeyDer::from(key)), + Item::Sec1Key(key) => Some(PrivateKeyDer::from(key)), + _ => None, }; if certs.is_empty() || private_key.is_none() { @@ -134,10 +138,8 @@ pub fn load_pem_file_ca(path: &String) -> Vec { } }); match ca { - None => { Vec::new() } - Some(ca) => { - ca.to_vec() - } + None => Vec::new(), + Some(ca) => ca.to_vec(), } } @@ -151,7 +153,6 @@ pub fn load_pem_file_private_key(path: &String) -> Vec { Vec::new() } - pub fn hash_certificate(cert: CertificateDer) -> Vec { let hash = ring::digest::digest(&ring::digest::SHA256, cert.as_ref()); hash.as_ref().to_vec() diff --git a/pingora/examples/server.rs b/pingora/examples/server.rs index 083e6a73..e7924311 100644 --- a/pingora/examples/server.rs +++ b/pingora/examples/server.rs @@ -52,9 +52,9 @@ impl BackgroundService for ExampleBackgroundService { #[cfg(not(feature = "rustls"))] mod boringssl_openssl { + use super::*; use pingora::tls::pkey::{PKey, Private}; use pingora::tls::x509::X509; - use super::*; pub(super) struct DynamicCert { cert: X509, @@ -146,13 +146,16 @@ pub fn main() { let dynamic_cert = boringssl_openssl::DynamicCert::new(&cert_path, &key_path); tls_settings = pingora::listeners::TlsSettings::with_callbacks(dynamic_cert).unwrap(); // by default intermediate supports both TLS 1.2 and 1.3. We force to tls 1.2 just for the demo - tls_settings.get_builder().native() + tls_settings + .get_builder() + .native() .set_max_proto_version(Some(pingora::tls::ssl::SslVersion::TLS1_2)) .unwrap(); } #[cfg(feature = "rustls")] { - tls_settings = pingora::listeners::TlsSettings::intermediate(&cert_path, &key_path).unwrap(); + tls_settings = + pingora::listeners::TlsSettings::intermediate(&cert_path, &key_path).unwrap(); } tls_settings.enable_h2(); echo_service_http.add_tls_with_settings("0.0.0.0:6148", None, tls_settings);