spiffe_rustls/
server.rs

1use crate::error::Result;
2use crate::resolve::MaterialWatcher;
3use crate::types::{authorize_any, AuthorizeSpiffeId};
4use crate::verifier::SpiffeClientCertVerifier;
5use rustls::server::ResolvesServerCert;
6use rustls::ServerConfig;
7use spiffe::{TrustDomain, X509Source};
8use std::sync::Arc;
9
10/// Configuration options for [`ServerConfigBuilder`].
11///
12/// These options control trust bundle selection and client authorization.
13#[derive(Clone)]
14pub struct ServerConfigOptions {
15    /// Trust domain whose bundle is used as the verification root set.
16    pub trust_domain: TrustDomain,
17
18    /// Authorization hook invoked with the client SPIFFE ID.
19    ///
20    /// The hook receives the SPIFFE ID extracted from the client certificate’s
21    /// URI SAN and must return `true` to allow the connection.
22    pub authorize_client: AuthorizeSpiffeId,
23}
24
25impl std::fmt::Debug for ServerConfigOptions {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.debug_struct("ServerConfigOptions")
28            .field("trust_domain", &self.trust_domain)
29            .field("authorize_client", &"<authorize_fn>")
30            .finish()
31    }
32}
33
34impl ServerConfigOptions {
35    /// Creates options that authenticate clients but allow any SPIFFE ID.
36    ///
37    /// This disables authorization while retaining full TLS authentication.
38    /// Use only if authorization is performed at another layer.
39    pub fn allow_any(trust_domain: TrustDomain) -> Self {
40        Self {
41            trust_domain,
42            authorize_client: authorize_any(),
43        }
44    }
45}
46
47/// Builds a [`rustls::ServerConfig`] backed by a live SPIFFE `X509Source`.
48///
49/// The resulting server configuration:
50///
51/// * presents the current SPIFFE X.509 SVID as the server certificate
52/// * requires and validates client certificates (mTLS)
53/// * authorizes the client by SPIFFE ID (URI SAN)
54#[derive(Debug)]
55pub struct ServerConfigBuilder {
56    source: Arc<X509Source>,
57    opts: ServerConfigOptions,
58}
59
60impl ServerConfigBuilder {
61    /// Creates a new builder from an `X509Source` and options.
62    pub fn new(source: Arc<X509Source>, opts: ServerConfigOptions) -> Self {
63        Self { source, opts }
64    }
65
66    /// Builds the `rustls::ServerConfig`.
67    ///
68    /// ## Errors
69    ///
70    /// Returns an error if:
71    ///
72    /// * the SPIFFE `X509Source` does not currently have an SVID,
73    /// * the trust bundle for the configured trust domain is missing,
74    /// * rustls crypto providers are not installed,
75    /// * or the material watcher cannot be initialized.
76    pub fn build(self) -> Result<ServerConfig> {
77        crate::crypto::ensure_crypto_provider_installed();
78
79        let watcher = MaterialWatcher::new(self.source, self.opts.trust_domain)?;
80
81        let resolver: Arc<dyn ResolvesServerCert> =
82            Arc::new(resolve_server::SpiffeServerCertResolver {
83                watcher: watcher.clone(),
84            });
85
86        let verifier = Arc::new(SpiffeClientCertVerifier::from_watcher(
87            watcher.clone(),
88            self.opts.authorize_client,
89        ));
90
91        let cfg = ServerConfig::builder()
92            .with_client_cert_verifier(verifier)
93            .with_cert_resolver(resolver);
94
95        Ok(cfg)
96    }
97}
98
99mod resolve_server {
100    use crate::resolve::MaterialWatcher;
101    use rustls::server::ResolvesServerCert;
102    use rustls::sign::CertifiedKey;
103    use std::sync::Arc;
104
105    #[derive(Clone, Debug)]
106    pub(crate) struct SpiffeServerCertResolver {
107        pub watcher: MaterialWatcher,
108    }
109
110    impl ResolvesServerCert for SpiffeServerCertResolver {
111        fn resolve(
112            &self,
113            _client_hello: rustls::server::ClientHello<'_>,
114        ) -> Option<Arc<CertifiedKey>> {
115            Some(self.watcher.current().certified_key.clone())
116        }
117    }
118}