Skip to content

Commit

Permalink
fix: remove indirections in listener::tls::Acceptor & TlsSettings
Browse files Browse the repository at this point in the history
  • Loading branch information
hargut committed Sep 10, 2024
1 parent a42eaf1 commit b84ad21
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 126 deletions.
57 changes: 25 additions & 32 deletions pingora-core/src/listeners/tls/boringssl_openssl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,30 @@

//! BoringSSL & OpenSSL listener specific implementation

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");
use std::ops::{Deref, DerefMut};

struct TlsAcc(SslAcceptor);
pub(super) struct TlsAcceptorBuil(SslAcceptorBuilder);
const TLS_CONF_ERR: ErrorType = ErrorType::Custom("TLSConfigError");

#[async_trait]
impl TlsAcceptor for TlsAcc {
fn get_acceptor(&self) -> &dyn Any {
&self.0
}
}
pub struct TlsAcceptorBuil(SslAcceptorBuilder);
pub(super) struct TlsAcc(pub(super) SslAcceptor);

impl TlsAcceptorBuilder for TlsAcceptorBuil {
fn build(self: Box<Self>) -> Box<dyn TlsAcceptor + Send + Sync> {
let builder = (*self).0;
Box::new(TlsAcc(SslAcceptorBuilder::build(builder)))
impl TlsAcceptorBuil {
pub(super) fn build(self) -> TlsAcc {
TlsAcc(SslAcceptorBuilder::build(self.0))
}

fn set_alpn(&mut self, alpn: ALPN) {
pub(super) fn set_alpn(&mut self, alpn: ALPN) {
match alpn {
ALPN::H2H1 => self.0.set_alpn_select_callback(alpn::prefer_h2),
ALPN::H1 => self.0.set_alpn_select_callback(alpn::h1_only),
ALPN::H2 => self.0.set_alpn_select_callback(alpn::h2_only),
}
}

fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result<Self>
pub(super) fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result<Self>
where
Self: Sized,
{
Expand All @@ -65,7 +56,7 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil {
Ok(TlsAcceptorBuil(accept_builder))
}

fn acceptor_with_callbacks() -> Result<Self>
pub(super) fn acceptor_with_callbacks() -> Result<Self>
where
Self: Sized,
{
Expand All @@ -75,24 +66,12 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil {
)?;
Ok(TlsAcceptorBuil(accept_builder))
}

fn as_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any
}
}

impl NativeBuilder for Box<dyn TlsAcceptorBuilder + Send + Sync> {
type Builder = SslAcceptorBuilder;

fn native(&mut self) -> &mut Self::Builder {
self.as_any().downcast_mut::<Self::Builder>().unwrap()
}
}

impl From<SslAcceptorBuilder> for TlsSettings {
fn from(settings: SslAcceptorBuilder) -> Self {
TlsSettings {
accept_builder: Box::new(TlsAcceptorBuil(settings)),
accept_builder: TlsAcceptorBuil(settings),
callbacks: None,
}
}
Expand Down Expand Up @@ -125,3 +104,17 @@ mod alpn {
}
}
}

impl Deref for TlsAcceptorBuil {
type Target = SslAcceptorBuilder;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for TlsAcceptorBuil {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
78 changes: 38 additions & 40 deletions pingora-core/src/listeners/tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,61 +12,53 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::any::Any;

use async_trait::async_trait;
use log::debug;

#[cfg(not(feature = "rustls"))]
use crate::listeners::tls::boringssl_openssl::TlsAcceptorBuil;
#[cfg(feature = "rustls")]
use crate::listeners::tls::rustls::TlsAcceptorBuil;
#[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;
#[cfg(not(feature = "rustls"))]
use crate::{
listeners::tls::boringssl_openssl::{TlsAcc, TlsAcceptorBuil},
protocols::tls::boringssl_openssl::server::{handshake, handshake_with_callback},
};
#[cfg(feature = "rustls")]
use crate::{
listeners::tls::rustls::{TlsAcc, TlsAcceptorBuil},
protocols::tls::rustls::server::{handshake, handshake_with_callback},
};
use log::debug;
use pingora_error::Result;
use std::ops::{Deref, DerefMut};

#[cfg(not(feature = "rustls"))]
pub mod boringssl_openssl;
pub(crate) mod boringssl_openssl;
#[cfg(feature = "rustls")]
pub(crate) mod rustls;

pub struct Acceptor {
ssl_acceptor: Box<dyn TlsAcceptor + Send + Sync>,
tls_acceptor: TlsAcc,
callbacks: Option<TlsAcceptCallbacks>,
}

#[async_trait]
pub trait TlsAcceptor {
fn get_acceptor(&self) -> &dyn Any;
}

/// The TLS settings of a listening endpoint
pub struct TlsSettings {
accept_builder: Box<dyn TlsAcceptorBuilder + Send + Sync>,
accept_builder: TlsAcceptorBuil,
callbacks: Option<TlsAcceptCallbacks>,
}

pub trait TlsAcceptorBuilder: Any {
fn build(self: Box<Self>) -> Box<dyn TlsAcceptor + Send + Sync>;
// NOTE: keeping trait for documentation purpose
// switched to direct implementations to eliminate redirections in within the call-graph
// the below trait needs to be implemented for TlsAcceptorBuil
pub trait TlsAcceptorBuilder {
type TlsAcceptor; // TlsAcc
fn build(self) -> Self::TlsAcceptor;
fn set_alpn(&mut self, alpn: ALPN);
fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result<Self>
where
Self: Sized;
fn acceptor_with_callbacks() -> Result<Self>
where
Self: Sized;
fn as_any(&mut self) -> &mut dyn Any;
}

pub trait NativeBuilder {
type Builder;
fn native(&mut self) -> &mut Self::Builder;
}

impl TlsSettings {
Expand All @@ -75,7 +67,7 @@ impl TlsSettings {
/// Return error if the provided certificate and private key are invalid or not found.
pub fn intermediate(cert_path: &str, key_path: &str) -> Result<Self> {
Ok(TlsSettings {
accept_builder: Box::new(TlsAcceptorBuil::acceptor_intermediate(cert_path, key_path)?),
accept_builder: TlsAcceptorBuil::acceptor_intermediate(cert_path, key_path)?,
callbacks: None,
})
}
Expand All @@ -84,7 +76,7 @@ impl TlsSettings {
/// is needed to provide the certificate during the TLS handshake.
pub fn with_callbacks(callbacks: TlsAcceptCallbacks) -> Result<Self> {
Ok(TlsSettings {
accept_builder: Box::new(TlsAcceptorBuil::acceptor_with_callbacks()?),
accept_builder: TlsAcceptorBuil::acceptor_with_callbacks()?,
callbacks: Some(callbacks),
})
}
Expand All @@ -102,28 +94,34 @@ impl TlsSettings {

pub(crate) fn build(self) -> Acceptor {
Acceptor {
ssl_acceptor: self.accept_builder.build(),
tls_acceptor: self.accept_builder.build(),
callbacks: self.callbacks,
}
}

pub fn get_builder(&mut self) -> &mut Box<dyn TlsAcceptorBuilder + Send + Sync> {
&mut (self.accept_builder)
}
}

impl Acceptor {
pub async fn handshake<S: IO>(&self, stream: S) -> Result<TlsStream<S>> {
debug!("new tls session");
// TODO: be able to offload this handshake in a thread pool
if let Some(cb) = self.callbacks.as_ref() {
handshake_with_callback(self, stream, cb).await
handshake_with_callback(&self.tls_acceptor.0, stream, cb).await
} else {
handshake(self, stream).await
handshake(&self.tls_acceptor.0, stream).await
}
}
}

impl Deref for TlsSettings {
type Target = TlsAcceptorBuil;

fn deref(&self) -> &Self::Target {
&self.accept_builder
}
}

pub(crate) fn inner(&self) -> &dyn Any {
self.ssl_acceptor.get_acceptor()
impl DerefMut for TlsSettings {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.accept_builder
}
}
35 changes: 8 additions & 27 deletions pingora-core/src/listeners/tls/rustls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,26 @@

//! Rustls TLS listener specific implementation

use std::any::Any;
use std::sync::Arc;

use async_trait::async_trait;

use pingora_error::ErrorType::InternalError;
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::tls::{TlsAcceptor, TlsAcceptorBuilder};
use crate::listeners::ALPN;

pub(super) struct TlsAcceptorBuil {
pub struct TlsAcceptorBuil {
alpn_protocols: Option<Vec<Vec<u8>>>,
cert_path: String,
key_path: String,
}

struct TlsAcc {
acceptor: RusTlsAcceptor,
}

#[async_trait]
impl TlsAcceptor for TlsAcc {
fn get_acceptor(&self) -> &dyn Any {
&self.acceptor
}
}
pub(super) struct TlsAcc(pub(super) RusTlsAcceptor);

impl TlsAcceptorBuilder for TlsAcceptorBuil {
fn build(self: Box<Self>) -> Box<dyn TlsAcceptor + Send + Sync> {
impl TlsAcceptorBuil {
pub(super) fn build(self) -> TlsAcc {
let (certs, key) = load_certs_key_file(&self.cert_path, &self.key_path).expect(
format!(
"Failed to load provided certificates \"{}\" or key \"{}\".",
Expand All @@ -68,15 +55,13 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil {
config.alpn_protocols = alpn_protocols;
}

Box::new(TlsAcc {
acceptor: RusTlsAcceptor::from(Arc::new(config)),
})
TlsAcc(RusTlsAcceptor::from(Arc::new(config)))
}
fn set_alpn(&mut self, alpn: ALPN) {
pub(super) fn set_alpn(&mut self, alpn: ALPN) {
self.alpn_protocols = Some(alpn.to_wire_protocols());
}

fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result<Self>
pub(super) fn acceptor_intermediate(cert_path: &str, key_path: &str) -> Result<Self>
where
Self: Sized,
{
Expand All @@ -87,7 +72,7 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil {
})
}

fn acceptor_with_callbacks() -> Result<Self>
pub(super) fn acceptor_with_callbacks() -> Result<Self>
where
Self: Sized,
{
Expand All @@ -101,8 +86,4 @@ impl TlsAcceptorBuilder for TlsAcceptorBuil {
None,
))
}

fn as_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any
}
}
15 changes: 6 additions & 9 deletions pingora-core/src/protocols/tls/boringssl_openssl/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
use std::pin::Pin;

use async_trait::async_trait;
use tokio::io::{AsyncRead, AsyncWrite};

use pingora_error::ErrorType::{TLSHandshakeFailure, TLSWantX509Lookup};
use pingora_error::{OrErr, Result};
use tokio::io::{AsyncRead, AsyncWrite};

use crate::listeners::tls::Acceptor;
use crate::protocols::tls::boringssl_openssl::TlsStream;
use crate::protocols::tls::server::{ResumableAccept, TlsAcceptCallbacks};
use crate::protocols::{Ssl, IO};
Expand Down Expand Up @@ -58,15 +56,14 @@ impl<S: AsyncRead + AsyncWrite + Send + Unpin> ResumableAccept for TlsStream<S>
}
}

fn prepare_tls_stream<S: IO>(acceptor: &Acceptor, io: S) -> Result<TlsStream<S>> {
let ssl_acceptor = acceptor.inner().downcast_ref::<SslAcceptor>().unwrap();
let ssl = ssl_from_acceptor(ssl_acceptor)
fn prepare_tls_stream<S: IO>(acceptor: &SslAcceptor, io: S) -> Result<TlsStream<S>> {
let ssl = ssl_from_acceptor(&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<S: IO>(acceptor: &Acceptor, io: S) -> Result<TlsStream<S>> {
pub(crate) async fn handshake<S: IO>(acceptor: &SslAcceptor, io: S) -> Result<TlsStream<S>> {
let mut stream = prepare_tls_stream(acceptor, io)?;
stream
.accept()
Expand All @@ -76,8 +73,8 @@ pub async fn handshake<S: IO>(acceptor: &Acceptor, io: S) -> Result<TlsStream<S>
}

/// Perform TLS handshake for the given connection with the given configuration and callbacks
pub async fn handshake_with_callback<S: IO>(
acceptor: &Acceptor,
pub(crate) async fn handshake_with_callback<S: IO>(
acceptor: &SslAcceptor,
io: S,
callbacks: &TlsAcceptCallbacks,
) -> pingora_error::Result<TlsStream<S>> {
Expand Down
5 changes: 2 additions & 3 deletions pingora-core/src/protocols/tls/rustls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ use std::task::{Context, Poll};

use pingora_error::ErrorType::{InternalError, TLSHandshakeFailure};
use pingora_error::{OrErr, Result};
use pingora_rustls::TlsStream as RusTlsStream;
use pingora_rustls::{hash_certificate, ServerName, TlsConnector};
use pingora_rustls::{TlsAcceptor, TlsStream as RusTlsStream};
use tokio::io::{self, AsyncRead, AsyncWrite, ReadBuf};
use x509_parser::nom::AsBytes;

use crate::utils::tls::rustls::get_organization_serial;

use crate::listeners::tls::Acceptor;
use crate::protocols::tls::rustls::stream::InnerStream;
use crate::protocols::tls::SslDigest;
use crate::protocols::{Ssl, UniqueID, ALPN};
Expand Down Expand Up @@ -69,7 +68,7 @@ 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<Self> {
pub(crate) async fn from_acceptor(acceptor: &TlsAcceptor, stream: T) -> Result<Self> {
let tls = InnerStream::from_acceptor(acceptor, stream)
.await
.explain_err(TLSHandshakeFailure, |e| format!("tls stream error: {e}"))?;
Expand Down
Loading

0 comments on commit b84ad21

Please sign in to comment.