use crate::authorizer::Authorizer;
use crate::error::Result;
use crate::policy::TrustDomainPolicy;
use crate::resolve::MaterialWatcher;
use crate::verifier::SpiffeServerCertVerifier;
use rustls::client::ResolvesClientCert;
use rustls::ClientConfig;
use spiffe::X509Source;
use std::sync::Arc;
type ClientConfigCustomizer = Box<dyn FnOnce(&mut ClientConfig) + Send>;
pub struct ClientConfigBuilder {
source: Arc<X509Source>,
authorizer: Arc<dyn Authorizer>,
trust_domain_policy: TrustDomainPolicy,
alpn_protocols: Vec<Vec<u8>>,
config_customizer: Option<ClientConfigCustomizer>,
}
impl std::fmt::Debug for ClientConfigBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClientConfigBuilder")
.field("source", &"<Arc<X509Source>>")
.field("authorizer", &"<Arc<dyn Authorizer>>")
.field("trust_domain_policy", &self.trust_domain_policy)
.field("alpn_protocols", &self.alpn_protocols)
.field("config_customizer", &self.config_customizer.is_some())
.finish()
}
}
impl ClientConfigBuilder {
pub fn new(source: X509Source) -> Self {
Self {
source: Arc::new(source),
authorizer: Arc::new(crate::authorizer::any()),
trust_domain_policy: TrustDomainPolicy::default(),
alpn_protocols: Vec::new(),
config_customizer: None,
}
}
#[must_use]
pub fn authorize<A: Authorizer>(mut self, authorizer: A) -> Self {
self.authorizer = Arc::new(authorizer);
self
}
#[must_use]
pub fn trust_domain_policy(mut self, policy: TrustDomainPolicy) -> Self {
self.trust_domain_policy = policy;
self
}
#[must_use]
pub fn with_alpn_protocols<I, P>(mut self, protocols: I) -> Self
where
I: IntoIterator<Item = P>,
P: AsRef<[u8]>,
{
self.alpn_protocols = protocols.into_iter().map(|p| p.as_ref().to_vec()).collect();
self
}
#[must_use]
pub fn with_config_customizer<F>(mut self, customizer: F) -> Self
where
F: FnOnce(&mut ClientConfig) + Send + 'static,
{
self.config_customizer = Some(Box::new(customizer));
self
}
pub fn build(self) -> Result<ClientConfig> {
crate::crypto::ensure_crypto_provider_installed();
let watcher = MaterialWatcher::spawn(self.source)?;
let resolver: Arc<dyn ResolvesClientCert> =
Arc::new(resolve_client::SpiffeClientCertResolver {
watcher: watcher.clone(),
});
let verifier = Arc::new(SpiffeServerCertVerifier::new(
Arc::new(watcher),
self.authorizer,
self.trust_domain_policy,
));
let mut cfg = ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(verifier)
.with_client_cert_resolver(resolver);
cfg.alpn_protocols = self.alpn_protocols;
if let Some(customizer) = self.config_customizer {
customizer(&mut cfg);
}
Ok(cfg)
}
}
mod resolve_client {
use crate::resolve::MaterialWatcher;
use rustls::client::ResolvesClientCert;
use rustls::sign::CertifiedKey;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub(crate) struct SpiffeClientCertResolver {
pub watcher: MaterialWatcher,
}
impl ResolvesClientCert for SpiffeClientCertResolver {
fn resolve(
&self,
_acceptable_issuers: &[&[u8]],
_sigschemes: &[rustls::SignatureScheme],
) -> Option<Arc<CertifiedKey>> {
Some(Arc::clone(&self.watcher.current().certified_key))
}
fn has_certs(&self) -> bool {
true
}
}
}