1use std::fs::File;
21use std::io::BufReader;
22use std::path::PathBuf;
23use std::sync::Arc;
24
25use rustls::pki_types::{CertificateDer, PrivateKeyDer};
26use rustls::server::{ServerConfig, WebPkiClientVerifier};
27use rustls::RootCertStore;
28
29#[derive(Debug, Clone)]
31pub struct InternalCertPaths {
32 pub server_crt: PathBuf,
34 pub server_key: PathBuf,
36 pub client_ca_crt: PathBuf,
38}
39
40#[derive(Debug, Clone)]
42pub struct PublicCertPaths {
43 pub server_crt: PathBuf,
45 pub server_key: PathBuf,
47}
48
49#[derive(Clone)]
53pub struct TlsConfig {
54 pub server_config: Arc<ServerConfig>,
57}
58
59impl TlsConfig {
60 pub fn load_internal(paths: &InternalCertPaths) -> Result<Self, anyhow::Error> {
62 let chain = load_certs(&paths.server_crt)?;
63 let key = load_private_key(&paths.server_key)?;
64
65 let mut roots = RootCertStore::empty();
66 for c in load_certs(&paths.client_ca_crt)? {
67 roots.add(c)?;
68 }
69 let client_verifier = WebPkiClientVerifier::builder(Arc::new(roots)).build()?;
70
71 let config = ServerConfig::builder()
72 .with_client_cert_verifier(client_verifier)
73 .with_single_cert(chain, key)?;
74 Ok(Self {
75 server_config: Arc::new(config),
76 })
77 }
78
79 pub fn load_public(paths: &PublicCertPaths) -> Result<Self, anyhow::Error> {
81 let chain = load_certs(&paths.server_crt)?;
82 let key = load_private_key(&paths.server_key)?;
83
84 let config = ServerConfig::builder()
85 .with_no_client_auth()
86 .with_single_cert(chain, key)?;
87 Ok(Self {
88 server_config: Arc::new(config),
89 })
90 }
91}
92
93impl std::fmt::Debug for TlsConfig {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.debug_struct("TlsConfig").finish_non_exhaustive()
96 }
97}
98
99fn load_certs(path: &PathBuf) -> Result<Vec<CertificateDer<'static>>, anyhow::Error> {
100 let f = File::open(path).map_err(|e| anyhow::anyhow!("open {}: {e}", path.display()))?;
101 let mut r = BufReader::new(f);
102 let certs: Vec<_> = rustls_pemfile::certs(&mut r)
103 .collect::<Result<_, _>>()
104 .map_err(|e| anyhow::anyhow!("parse {}: {e}", path.display()))?;
105 if certs.is_empty() {
106 anyhow::bail!("no certificates found in {}", path.display());
107 }
108 Ok(certs)
109}
110
111fn load_private_key(path: &PathBuf) -> Result<PrivateKeyDer<'static>, anyhow::Error> {
112 let f = File::open(path).map_err(|e| anyhow::anyhow!("open {}: {e}", path.display()))?;
113 let mut r = BufReader::new(f);
114 let key = rustls_pemfile::private_key(&mut r)
115 .map_err(|e| anyhow::anyhow!("parse {}: {e}", path.display()))?
116 .ok_or_else(|| anyhow::anyhow!("no private key found in {}", path.display()))?;
117 Ok(key)
118}