spiffe_rustls/
client.rs

1use crate::error::Result;
2use crate::resolve::MaterialWatcher;
3use crate::types::{authorize_any, AuthorizeSpiffeId};
4use crate::verifier::SpiffeServerCertVerifier;
5use rustls::client::ResolvesClientCert;
6use rustls::ClientConfig;
7use spiffe::{TrustDomain, X509Source};
8use std::sync::Arc;
9
10/// Configuration options for [`ClientConfigBuilder`].
11///
12/// These options control trust bundle selection and server authorization.
13#[derive(Clone)]
14pub struct ClientConfigOptions {
15    /// Trust domain whose bundle is used as the verification root set.
16    pub trust_domain: TrustDomain,
17
18    /// Authorization hook invoked with the server SPIFFE ID.
19    ///
20    /// The hook receives the SPIFFE ID extracted from the server certificate’s
21    /// URI SAN and must return `true` to allow the connection.
22    pub authorize_server: AuthorizeSpiffeId,
23}
24
25impl ClientConfigOptions {
26    /// Creates options that authenticate the server but allow any SPIFFE ID.
27    ///
28    /// This disables authorization while retaining full TLS authentication.
29    /// Use only if authorization is performed at another layer.
30    pub fn allow_any(trust_domain: TrustDomain) -> Self {
31        Self {
32            trust_domain,
33            authorize_server: authorize_any(),
34        }
35    }
36}
37
38/// Builds a [`rustls::ClientConfig`] backed by a live SPIFFE `X509Source`.
39///
40/// The resulting client configuration:
41///
42/// * presents the current SPIFFE X.509 SVID as the client certificate
43/// * validates the server certificate chain against the trust domain bundle
44/// * authorizes the server by SPIFFE ID (URI SAN)
45///
46/// The builder retains an `Arc<X509Source>`. When the underlying SVID or trust
47/// bundle is rotated by the SPIRE agent, **new TLS handshakes automatically use
48/// the updated material**.
49///
50/// ## Authorization
51///
52/// Server authorization is performed by invoking the provided
53/// [`AuthorizeSpiffeId`] hook with the server’s SPIFFE ID extracted from the
54/// certificate’s URI SAN.
55///
56/// Use [`ClientConfigOptions::allow_any`] to disable authorization while
57/// retaining full TLS authentication.
58pub struct ClientConfigBuilder {
59    source: Arc<X509Source>,
60    opts: ClientConfigOptions,
61}
62
63impl ClientConfigBuilder {
64    /// Creates a new builder from an `X509Source` and options.
65    pub fn new(source: Arc<X509Source>, opts: ClientConfigOptions) -> Self {
66        Self { source, opts }
67    }
68
69    /// Builds the `rustls::ClientConfig`.
70    pub async fn build(self) -> Result<ClientConfig> {
71        crate::crypto::ensure_crypto_provider_installed();
72
73        let watcher = MaterialWatcher::new(self.source, self.opts.trust_domain).await?;
74
75        let resolver: Arc<dyn ResolvesClientCert> =
76            Arc::new(resolve_client::SpiffeClientCertResolver {
77                watcher: watcher.clone(),
78            });
79
80        let verifier = Arc::new(SpiffeServerCertVerifier::new(
81            Arc::new(watcher.clone()),
82            self.opts.authorize_server,
83        )?);
84
85        let cfg = ClientConfig::builder()
86            .dangerous()
87            .with_custom_certificate_verifier(verifier)
88            .with_client_cert_resolver(resolver);
89
90        Ok(cfg)
91    }
92}
93
94mod resolve_client {
95    use crate::resolve::MaterialWatcher;
96    use rustls::client::ResolvesClientCert;
97    use rustls::sign::CertifiedKey;
98    use std::sync::Arc;
99
100    #[derive(Clone, Debug)]
101    pub(crate) struct SpiffeClientCertResolver {
102        pub watcher: MaterialWatcher,
103    }
104
105    impl ResolvesClientCert for SpiffeClientCertResolver {
106        fn resolve(
107            &self,
108            _acceptable_issuers: &[&[u8]],
109            _sigschemes: &[rustls::SignatureScheme],
110        ) -> Option<Arc<CertifiedKey>> {
111            Some(self.watcher.current().certified_key.clone())
112        }
113
114        fn has_certs(&self) -> bool {
115            true
116        }
117    }
118}