1use serde::{Deserialize, Serialize};
4use std::{collections::HashMap, fmt, time::Duration};
5
6use super::ConfigError;
7
8#[cfg(feature = "config-openssl")]
9pub mod openssl;
10
11pub trait SslStore<C, S> {
13 fn get_file_certificates(path: &std::path::Path) -> Result<Vec<C>, ConfigError>;
15
16 fn get_store(&self) -> Result<S, ConfigError>;
26
27 fn get_certs(&self) -> Result<HashMap<String, C>, ConfigError>;
41}
42
43#[derive(Default, Debug, Clone, PartialEq, Deserialize, Serialize)]
45#[serde(untagged)]
46pub enum Store {
47 #[default]
49 System,
50 File {
52 path: String,
54 },
55 Cert {
57 certs: Vec<String>,
59 },
60}
61
62impl fmt::Display for Store {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 match self {
65 Store::System => write!(f, "System store"),
66 Store::File { path } => write!(f, "Store cert path [{path}]"),
67 Store::Cert { certs: _ } => write!(f, "Store cert list"),
68 }?;
69
70 #[cfg(feature = "config-openssl")]
71 {
72 writeln!(f, ":")?;
73 let certs: HashMap<String, ::openssl::x509::X509> =
74 self.get_certs().unwrap_or_default();
75 for (name, cert) in certs {
76 if f.alternate() {
77 writeln!(f, "{name}:\n{cert:#?}")?;
78 } else {
79 writeln!(f, "{name}")?;
80 }
81 }
82 }
83
84 Ok(())
85 }
86}
87
88pub trait SslConfigContext<C, S> {
90 fn init_tls_client_context(&self) -> Result<C, ConfigError>;
103
104 fn init_tls_server_context(&self, host: Option<&str>) -> Result<S, ConfigError>;
116}
117
118#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
188pub struct SslConfig {
189 store: Option<Store>,
191 pkcs12: Option<String>,
193 cert: Option<String>,
195 key: Option<String>,
197 passphrase: Option<String>,
199 #[serde(default)]
200 alpn: Vec<String>,
202 #[serde(skip_serializing)]
203 #[serde(default = "SslConfig::default_modern_security")]
204 pub modern_security: bool,
206 #[serde(skip_serializing)]
207 #[serde(default = "SslConfig::default_ssl_timeout")]
208 pub ssl_timeout: u64,
210}
211
212impl SslConfig {
213 fn default_modern_security() -> bool {
214 true
215 }
216
217 fn default_ssl_timeout() -> u64 {
218 3000
219 }
220
221 pub fn new_pkcs12(pkcs12_path: String) -> SslConfig {
224 SslConfig {
225 store: None,
226 pkcs12: Some(pkcs12_path),
227 cert: None,
228 key: None,
229 passphrase: None,
230 alpn: Vec::default(),
231 modern_security: Self::default_modern_security(),
232 ssl_timeout: Self::default_ssl_timeout(),
233 }
234 }
235
236 pub fn new_cert_key(
239 cert_path: String,
240 key_path: String,
241 passphrase: Option<String>,
242 ) -> SslConfig {
243 SslConfig {
244 store: None,
245 pkcs12: None,
246 cert: Some(cert_path),
247 key: Some(key_path),
248 passphrase,
249 alpn: Vec::default(),
250 modern_security: Self::default_modern_security(),
251 ssl_timeout: Self::default_ssl_timeout(),
252 }
253 }
254
255 pub fn new_self_cert(cert_path: String) -> SslConfig {
258 SslConfig {
259 store: None,
260 pkcs12: None,
261 cert: Some(cert_path),
262 key: None,
263 passphrase: None,
264 alpn: Vec::default(),
265 modern_security: Self::default_modern_security(),
266 ssl_timeout: Self::default_ssl_timeout(),
267 }
268 }
269
270 pub fn get_ssl_timeout(&self) -> Duration {
272 Duration::from_millis(self.ssl_timeout)
273 }
274
275 pub fn set_store(&mut self, store: Store) {
277 self.store = Some(store);
278 }
279
280 pub fn set_alpn(&mut self, alpn: Vec<String>) {
282 self.alpn = alpn;
283 }
284}
285
286impl Default for SslConfig {
287 fn default() -> SslConfig {
288 SslConfig {
289 store: None,
290 pkcs12: None,
291 cert: None,
292 key: None,
293 passphrase: None,
294 alpn: Vec::default(),
295 modern_security: Self::default_modern_security(),
296 ssl_timeout: Self::default_ssl_timeout(),
297 }
298 }
299}
300
301#[cfg(feature = "config-openssl")]
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_store() {
308 let inline_store_le_x1_x2 = Store::Cert {
309 certs: vec![
310 "-----BEGIN CERTIFICATE-----
311MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
312TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
313cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
314WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
315ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
316MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
317h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
3180TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
319A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
320T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
321B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
322B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
323KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
324OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
325jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
326qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
327rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
328HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
329hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
330ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3313BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
332NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
333ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
334TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
335jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
336oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
3374RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
338mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
339emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
340-----END CERTIFICATE-----"
341 .to_string(),
342 "-----BEGIN CERTIFICATE-----
343MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
344CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
345R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
346MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
347ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
348EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
349+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
350ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
351AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
352zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
353tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
354/q4AaOeMSQ+2b1tbFfLn
355-----END CERTIFICATE-----"
356 .to_string(),
357 ],
358 };
359 assert!(format!("{inline_store_le_x1_x2}").contains("ISRG Root X"));
360
361 let config_store_le_x1_x2: Store = serde_yaml::from_str(
362 "certs:
363 - |
364 -----BEGIN CERTIFICATE-----
365 MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
366 TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
367 cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
368 WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
369 ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
370 MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
371 h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
372 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
373 A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
374 T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
375 B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
376 B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
377 KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
378 OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
379 jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
380 qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
381 rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
382 HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
383 hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
384 ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
385 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
386 NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
387 ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
388 TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
389 jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
390 oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
391 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
392 mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
393 emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
394 -----END CERTIFICATE-----
395 - |
396 -----BEGIN CERTIFICATE-----
397 MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
398 CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
399 R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
400 MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
401 ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
402 EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
403 +1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
404 ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
405 AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
406 zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
407 tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
408 /q4AaOeMSQ+2b1tbFfLn
409 -----END CERTIFICATE-----",
410 )
411 .unwrap();
412 assert!(format!("{config_store_le_x1_x2}").contains("ISRG Root X"));
413
414 let config_store_file: Store = serde_yaml::from_str("path: \"/opt\"").unwrap();
415 assert_eq!(
416 Store::File {
417 path: "/opt".to_string()
418 },
419 config_store_file
420 );
421 }
422
423 #[test]
424 fn test_tls_server_context() {
425 let ssl_config = SslConfig::default();
426 let ssl_acceptor = ssl_config.init_tls_server_context(None).unwrap().build();
427
428 assert!(ssl_acceptor.context().private_key().is_some());
430 assert!(ssl_acceptor.context().certificate().is_some());
431 }
432}