use async_trait::async_trait;
use openssl::ssl::{SniError, SslRef};
use pingora::listeners::TlsAccept;
use pingora::tls::ext;
use pingora::tls::ssl::NameType;
use crate::stores::{self};
pub struct ServerName(pub String);
#[derive(Debug, Clone)]
pub struct CertStore {}
impl CertStore {
pub fn new() -> Self {
CertStore {}
}
#[allow(clippy::unnecessary_wraps)]
pub fn sni_callback(ssl_ref: &mut SslRef) -> Result<(), SniError> {
let servername = ssl_ref.servername(NameType::HOST_NAME).unwrap_or("");
tracing::debug!("Received SNI: {}", servername);
Ok(())
}
}
#[async_trait]
impl TlsAccept for CertStore {
async fn certificate_callback(&self, ssl: &mut pingora::tls::ssl::SslRef) {
let host_name = ssl
.servername(NameType::HOST_NAME)
.unwrap_or_default()
.to_string();
let Some(cert) = stores::global::get_store()
.get_certificate(&host_name)
.await
else {
tracing::info!("No certificate found for host: {:?}", host_name);
return;
};
if let Err(err) = ext::ssl_use_private_key(ssl, &cert.key) {
tracing::error!("Failed to use private key for host {host_name:?}: {err}");
return;
}
if let Err(err) = ext::ssl_use_certificate(ssl, &cert.leaf) {
tracing::error!("Failed to use certificate for host {host_name:?}: {err}");
return;
}
for chain in &cert.chain {
if let Err(err) = ext::ssl_add_chain_cert(ssl, chain) {
tracing::error!("Failed to add chain certificate for host {host_name:?}: {err}");
}
}
}
async fn handshake_complete_callback(
&self,
ssl: &pingora::tls::ssl::SslRef,
) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
super::handshakes::completed(ssl);
let name = ssl.servername(NameType::HOST_NAME)?;
Some(std::sync::Arc::new(ServerName(name.to_lowercase())))
}
}