1use std::path::{Path, PathBuf};
2
3pub mod certs;
4pub mod client;
5pub mod server;
6
7pub use client::build_mtls_client;
8pub use server::build_mtls_acceptor;
9
10pub type MtlsClient = hyper_util::client::legacy::Client<
12 hyper_rustls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>,
13 hyper::body::Incoming,
14>;
15
16pub mod names {
17 pub const ORGANIZATION: &str = "mips-synapse";
18 pub const SERVICE_UNIT: &str = "service";
19 pub const AUTHORITY_NAME: &str = "service";
20 pub const GATEWAY_NAME: &str = "gateway";
21}
22
23pub mod paths {
25 pub const CA_CERT_PEM: &str = "ca-cert.pem";
26 pub const CA_KEY_PEM: &str = "ca-key.pem";
27 pub const CERT: &str = "cert.pem";
28 pub const KEY: &str = "key.pem";
29}
30
31#[derive(Debug, Clone)]
33pub struct MtlsConfig {
34 pub cert_path: PathBuf,
35 pub key_path: PathBuf,
36 pub ca_cert_path: PathBuf,
37}
38
39impl MtlsConfig {
40 pub fn from_dir<P: AsRef<Path>>(dir: P) -> Self {
43 let dir = dir.as_ref();
44 Self {
45 cert_path: dir.join(paths::CERT),
46 key_path: dir.join(paths::KEY),
47 ca_cert_path: dir
49 .parent()
50 .map(|p| p.join(paths::CA_CERT_PEM))
51 .unwrap_or_else(|| dir.join(paths::CA_CERT_PEM)),
52 }
53 }
54
55 pub fn from_default_dir() -> Option<Self> {
57 Self::for_service("gateway")
58 }
59
60 pub fn for_service(service_name: &str) -> Option<Self> {
62 let base_dir = PathBuf::from("./mtls");
63 let service_dir = base_dir.join(service_name);
64
65 let cert_path = service_dir.join(paths::CERT);
66 let key_path = service_dir.join(paths::KEY);
67 let ca_cert_path = base_dir.join(paths::CA_CERT_PEM);
68
69 if cert_path.exists() && key_path.exists() && ca_cert_path.exists() {
70 Some(Self {
71 cert_path,
72 key_path,
73 ca_cert_path,
74 })
75 } else {
76 None
77 }
78 }
79
80 pub fn build_server_acceptor(&self) -> anyhow::Result<tokio_rustls::TlsAcceptor> {
82 server::build_mtls_acceptor(&self.cert_path, &self.key_path, &self.ca_cert_path)
83 }
84
85 pub fn build_client(&self) -> anyhow::Result<MtlsClient> {
87 client::build_mtls_client(&self.cert_path, &self.key_path, &self.ca_cert_path)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_from_dir_paths() {
97 let config = MtlsConfig::from_dir("/mtls/my-service");
98 assert_eq!(config.cert_path, PathBuf::from("/mtls/my-service/cert.pem"));
99 assert_eq!(config.key_path, PathBuf::from("/mtls/my-service/key.pem"));
100 assert_eq!(config.ca_cert_path, PathBuf::from("/mtls/ca-cert.pem"));
101 }
102
103 #[test]
104 fn test_from_dir_no_parent() {
105 let config = MtlsConfig::from_dir("service");
107 assert_eq!(config.cert_path, PathBuf::from("service/cert.pem"));
108 assert_eq!(config.key_path, PathBuf::from("service/key.pem"));
109 assert!(
112 config
113 .ca_cert_path
114 .to_str()
115 .unwrap()
116 .contains("ca-cert.pem")
117 );
118 }
119
120 #[test]
121 fn test_constants() {
122 assert_eq!(paths::CA_CERT_PEM, "ca-cert.pem");
123 assert_eq!(paths::CA_KEY_PEM, "ca-key.pem");
124 assert_eq!(paths::CERT, "cert.pem");
125 assert_eq!(paths::KEY, "key.pem");
126 assert_eq!(names::ORGANIZATION, "mips-synapse");
127 assert_eq!(names::GATEWAY_NAME, "gateway");
128 }
129}