nixfleet_control_plane/
tls.rs1use anyhow::{Context, Result};
4use rustls::ServerConfig;
5use rustls::server::WebPkiClientVerifier;
6use rustls_pki_types::pem::PemObject;
7use rustls_pki_types::{CertificateDer, PrivateKeyDer};
8use std::path::Path;
9use std::sync::Arc;
10
11pub fn build_server_config(
16 cert_path: &Path,
17 key_path: &Path,
18 client_ca_path: Option<&Path>,
19) -> Result<ServerConfig> {
20 let certs: Vec<CertificateDer<'static>> = CertificateDer::pem_file_iter(cert_path)
21 .with_context(|| format!("failed to open cert: {}", cert_path.display()))?
22 .collect::<std::result::Result<Vec<_>, _>>()
23 .context("failed to parse server certificates")?;
24
25 let key = PrivateKeyDer::from_pem_file(key_path)
26 .with_context(|| format!("failed to read private key: {}", key_path.display()))?;
27
28 let builder = if let Some(ca_path) = client_ca_path {
29 let mut root_store = rustls::RootCertStore::empty();
30 for cert in CertificateDer::pem_file_iter(ca_path)
31 .with_context(|| format!("failed to open CA: {}", ca_path.display()))?
32 {
33 root_store.add(cert.context("failed to parse CA cert")?)?;
34 }
35 let verifier = WebPkiClientVerifier::builder(Arc::new(root_store))
36 .allow_unauthenticated()
37 .build()
38 .context("failed to build client verifier")?;
39 ServerConfig::builder().with_client_cert_verifier(verifier)
40 } else {
41 ServerConfig::builder().with_no_client_auth()
42 };
43
44 builder
45 .with_single_cert(certs, key)
46 .context("failed to configure server TLS")
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn build_server_config_missing_cert_fails() {
55 let result = build_server_config(
56 Path::new("/nonexistent/cert.pem"),
57 Path::new("/nonexistent/key.pem"),
58 None,
59 );
60 assert!(result.is_err());
61 }
62}