use super::{
config::{Binding, TlsNode},
host::HostMatcher,
};
use std::{io::Cursor, sync::Arc};
use trillium_rustls::{
RustlsAcceptor,
rustls::{
ServerConfig,
crypto::aws_lc_rs,
server::{ClientHello, ResolvesServerCert},
sign::CertifiedKey,
},
};
#[derive(Debug)]
struct SniResolver {
certs: Vec<(HostMatcher, Arc<CertifiedKey>)>,
default: Option<Arc<CertifiedKey>>,
}
impl ResolvesServerCert for SniResolver {
fn resolve(&self, hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
let sni = hello.server_name();
self.certs
.iter()
.find(|(matcher, _)| matcher.matches(sni))
.map(|(_, ck)| Arc::clone(ck))
.or_else(|| self.default.clone())
}
}
fn load_certified_key(tls: &TlsNode) -> Arc<CertifiedKey> {
let cert_pem = std::fs::read(&tls.cert)
.unwrap_or_else(|e| panic!("could not read cert {}: {e}", tls.cert.display()));
let key_pem = std::fs::read(&tls.key)
.unwrap_or_else(|e| panic!("could not read key {}: {e}", tls.key.display()));
let cert_chain = rustls_pemfile::certs(&mut Cursor::new(&cert_pem))
.collect::<Result<Vec<_>, _>>()
.unwrap_or_else(|e| panic!("invalid certificate {}: {e}", tls.cert.display()));
let key_der = rustls_pemfile::private_key(&mut Cursor::new(&key_pem))
.unwrap_or_else(|e| panic!("invalid key {}: {e}", tls.key.display()))
.unwrap_or_else(|| panic!("no private key found in {}", tls.key.display()));
let signing_key = aws_lc_rs::default_provider()
.key_provider
.load_private_key(key_der)
.unwrap_or_else(|e| panic!("unusable private key {}: {e}", tls.key.display()));
Arc::new(CertifiedKey::new(cert_chain, signing_key))
}
pub struct TlsBundle {
pub acceptor: RustlsAcceptor,
#[cfg(feature = "h3")]
pub quic: trillium_quinn::QuicConfig,
}
pub fn build(binding: &Binding) -> Option<TlsBundle> {
let mut certs = Vec::new();
for host in &binding.hosts {
if let Some(tls) = &host.tls {
let certified = load_certified_key(tls);
for pattern in &host.patterns {
certs.push((HostMatcher::parse(pattern), Arc::clone(&certified)));
}
}
}
let default = binding.tls.as_ref().map(load_certified_key);
if certs.is_empty() && default.is_none() {
return None;
}
let resolver: Arc<dyn ResolvesServerCert> = Arc::new(SniResolver { certs, default });
let mut config = ServerConfig::builder_with_provider(Arc::new(aws_lc_rs::default_provider()))
.with_safe_default_protocol_versions()
.expect("crypto provider supports safe default protocol versions")
.with_no_client_auth()
.with_cert_resolver(Arc::clone(&resolver));
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
Some(TlsBundle {
acceptor: RustlsAcceptor::from(config),
#[cfg(feature = "h3")]
quic: trillium_quinn::QuicConfig::from_cert_resolver(resolver),
})
}