1use std::{
2 fmt,
3 fs::File,
4 io::Read,
5 path::{Path, PathBuf},
6};
7
8use lookup::lookup_v2::OptionalValuePath;
9use openssl::{
10 pkcs12::{ParsedPkcs12_2, Pkcs12},
11 pkey::{PKey, Private},
12 ssl::{select_next_proto, AlpnError, ConnectConfiguration, SslContextBuilder, SslVerifyMode},
13 stack::Stack,
14 x509::{store::X509StoreBuilder, X509},
15};
16use snafu::ResultExt;
17use vector_config::configurable_component;
18
19use super::{
20 AddCertToStoreSnafu, AddExtraChainCertSnafu, CaStackPushSnafu, DerExportSnafu,
21 EncodeAlpnProtocolsSnafu, FileOpenFailedSnafu, FileReadFailedSnafu, MaybeTls, NewCaStackSnafu,
22 NewStoreBuilderSnafu, ParsePkcs12Snafu, Pkcs12Snafu, PrivateKeyParseSnafu, Result,
23 SetAlpnProtocolsSnafu, SetCertificateSnafu, SetPrivateKeySnafu, SetVerifyCertSnafu, TlsError,
24 TlsIdentitySnafu, X509ParseSnafu,
25};
26
27pub const PEM_START_MARKER: &str = "-----BEGIN ";
28
29pub const TEST_PEM_CA_PATH: &str = "tests/data/ca/certs/ca.cert.pem";
30pub const TEST_PEM_INTERMEDIATE_CA_PATH: &str =
31 "tests/data/ca/intermediate_server/certs/ca-chain.cert.pem";
32pub const TEST_PEM_CRT_PATH: &str =
33 "tests/data/ca/intermediate_server/certs/localhost-chain.cert.pem";
34pub const TEST_PEM_KEY_PATH: &str = "tests/data/ca/intermediate_server/private/localhost.key.pem";
35pub const TEST_PEM_CLIENT_CRT_PATH: &str =
36 "tests/data/ca/intermediate_client/certs/localhost-chain.cert.pem";
37pub const TEST_PEM_CLIENT_KEY_PATH: &str =
38 "tests/data/ca/intermediate_client/private/localhost.key.pem";
39
40#[configurable_component]
42#[configurable(metadata(docs::advanced))]
43#[derive(Clone, Debug, Default)]
44#[serde(deny_unknown_fields)]
45pub struct TlsEnableableConfig {
46 pub enabled: Option<bool>,
51
52 #[serde(flatten)]
53 pub options: TlsConfig,
54}
55
56impl TlsEnableableConfig {
57 pub fn enabled() -> Self {
58 Self {
59 enabled: Some(true),
60 ..Self::default()
61 }
62 }
63
64 pub fn test_config() -> Self {
65 Self {
66 enabled: Some(true),
67 options: TlsConfig::test_config(),
68 }
69 }
70}
71
72#[configurable_component]
74#[derive(Clone, Debug, Default)]
75pub struct TlsSourceConfig {
76 pub client_metadata_key: Option<OptionalValuePath>,
78
79 #[serde(flatten)]
80 pub tls_config: TlsEnableableConfig,
81}
82
83#[configurable_component]
85#[configurable(metadata(docs::advanced))]
86#[derive(Clone, Debug, Default)]
87#[serde(deny_unknown_fields)]
88pub struct TlsConfig {
89 pub verify_certificate: Option<bool>,
100
101 pub verify_hostname: Option<bool>,
110
111 #[configurable(metadata(docs::examples = "h2"))]
116 pub alpn_protocols: Option<Vec<String>>,
117
118 #[serde(alias = "ca_path")]
122 #[configurable(metadata(docs::examples = "/path/to/certificate_authority.crt"))]
123 #[configurable(metadata(docs::human_name = "CA File Path"))]
124 pub ca_file: Option<PathBuf>,
125
126 #[serde(alias = "crt_path")]
133 #[configurable(metadata(docs::examples = "/path/to/host_certificate.crt"))]
134 #[configurable(metadata(docs::human_name = "Certificate File Path"))]
135 pub crt_file: Option<PathBuf>,
136
137 #[serde(alias = "key_path")]
141 #[configurable(metadata(docs::examples = "/path/to/host_certificate.key"))]
142 #[configurable(metadata(docs::human_name = "Key File Path"))]
143 pub key_file: Option<PathBuf>,
144
145 #[configurable(metadata(docs::examples = "${KEY_PASS_ENV_VAR}"))]
149 #[configurable(metadata(docs::examples = "PassWord1"))]
150 #[configurable(metadata(docs::human_name = "Key File Password"))]
151 pub key_pass: Option<String>,
152
153 #[serde(alias = "server_name")]
157 #[configurable(metadata(docs::examples = "www.example.com"))]
158 #[configurable(metadata(docs::human_name = "Server Name"))]
159 pub server_name: Option<String>,
160}
161
162impl TlsConfig {
163 pub fn test_config() -> Self {
164 Self {
165 ca_file: Some(TEST_PEM_CA_PATH.into()),
166 crt_file: Some(TEST_PEM_CRT_PATH.into()),
167 key_file: Some(TEST_PEM_KEY_PATH.into()),
168 ..Self::default()
169 }
170 }
171}
172
173#[derive(Clone, Default)]
175pub struct TlsSettings {
176 verify_certificate: bool,
177 pub(super) verify_hostname: bool,
178 authorities: Vec<X509>,
179 pub(super) identity: Option<IdentityStore>, alpn_protocols: Option<Vec<u8>>,
181 server_name: Option<String>,
182}
183
184#[derive(Clone)]
185pub(super) struct IdentityStore(Vec<u8>, String);
186
187impl TlsSettings {
188 pub fn from_options(options: Option<&TlsConfig>) -> Result<Self> {
192 Self::from_options_base(options, false)
193 }
194
195 pub(super) fn from_options_base(options: Option<&TlsConfig>, for_server: bool) -> Result<Self> {
196 let default = TlsConfig::default();
197 let options = options.unwrap_or(&default);
198
199 if !for_server {
200 if options.verify_certificate == Some(false) {
201 warn!(
202 "The `verify_certificate` option is DISABLED, this may lead to security vulnerabilities."
203 );
204 }
205 if options.verify_hostname == Some(false) {
206 warn!("The `verify_hostname` option is DISABLED, this may lead to security vulnerabilities.");
207 }
208 }
209
210 Ok(Self {
211 verify_certificate: options.verify_certificate.unwrap_or(!for_server),
212 verify_hostname: options.verify_hostname.unwrap_or(!for_server),
213 authorities: options.load_authorities()?,
214 identity: options.load_identity()?,
215 alpn_protocols: options.parse_alpn_protocols()?,
216 server_name: options.server_name.clone(),
217 })
218 }
219
220 fn identity(&self) -> Option<ParsedPkcs12_2> {
226 self.identity.as_ref().map(|identity| {
231 Pkcs12::from_der(&identity.0)
232 .expect("Could not build PKCS#12 archive from parsed data")
233 .parse2(&identity.1)
234 .expect("Could not parse stored PKCS#12 archive")
235 })
236 }
237
238 pub fn identity_pem(&self) -> Option<(Vec<u8>, Vec<u8>)> {
244 self.identity().map(|identity| {
245 let mut cert = identity
246 .cert
247 .expect("Identity required")
248 .to_pem()
249 .expect("Invalid stored identity");
250 if let Some(chain) = identity.ca {
251 for authority in chain {
252 cert.extend(
253 authority
254 .to_pem()
255 .expect("Invalid stored identity chain certificate"),
256 );
257 }
258 }
259 let key = identity
260 .pkey
261 .expect("Private key required")
262 .private_key_to_pem_pkcs8()
263 .expect("Invalid stored private key");
264 (cert, key)
265 })
266 }
267
268 pub fn authorities_pem(&self) -> impl Iterator<Item = Vec<u8>> + '_ {
274 self.authorities.iter().map(|authority| {
275 authority
276 .to_pem()
277 .expect("Invalid stored authority certificate")
278 })
279 }
280
281 pub(super) fn apply_context(&self, context: &mut SslContextBuilder) -> Result<()> {
282 self.apply_context_base(context, false)
283 }
284
285 pub(super) fn apply_context_base(
286 &self,
287 context: &mut SslContextBuilder,
288 for_server: bool,
289 ) -> Result<()> {
290 context.set_verify(if self.verify_certificate {
291 SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT
292 } else {
293 SslVerifyMode::NONE
294 });
295 if let Some(identity) = self.identity() {
296 if let Some(cert) = &identity.cert {
297 context.set_certificate(cert).context(SetCertificateSnafu)?;
298 }
299 if let Some(pkey) = &identity.pkey {
300 context.set_private_key(pkey).context(SetPrivateKeySnafu)?;
301 }
302
303 if let Some(chain) = identity.ca {
304 for cert in chain {
305 context
306 .add_extra_chain_cert(cert)
307 .context(AddExtraChainCertSnafu)?;
308 }
309 }
310 }
311 if self.authorities.is_empty() {
312 debug!("Fetching system root certs.");
313
314 #[cfg(windows)]
315 load_windows_certs(context).unwrap();
316
317 #[cfg(target_os = "macos")]
318 load_mac_certs(context).unwrap();
319 } else {
320 let mut store = X509StoreBuilder::new().context(NewStoreBuilderSnafu)?;
321 for authority in &self.authorities {
322 store
323 .add_cert(authority.clone())
324 .context(AddCertToStoreSnafu)?;
325 }
326 context
327 .set_verify_cert_store(store.build())
328 .context(SetVerifyCertSnafu)?;
329 }
330
331 if let Some(alpn) = &self.alpn_protocols {
332 if for_server {
333 let server_proto = alpn.clone();
334 let server_proto_ref: &'static [u8] = Box::leak(server_proto.into_boxed_slice());
336 context.set_alpn_select_callback(move |_, client_proto| {
337 select_next_proto(server_proto_ref, client_proto).ok_or(AlpnError::NOACK)
338 });
339 } else {
340 context
341 .set_alpn_protos(alpn.as_slice())
342 .context(SetAlpnProtocolsSnafu)?;
343 }
344 }
345
346 Ok(())
347 }
348
349 pub fn apply_connect_configuration(
350 &self,
351 connection: &mut ConnectConfiguration,
352 ) -> std::result::Result<(), openssl::error::ErrorStack> {
353 connection.set_verify_hostname(self.verify_hostname);
354 if let Some(server_name) = &self.server_name {
355 connection.set_use_server_name_indication(false);
357 connection.set_hostname(server_name)?;
358 }
359 Ok(())
360 }
361}
362
363impl TlsConfig {
364 fn load_authorities(&self) -> Result<Vec<X509>> {
365 match &self.ca_file {
366 None => Ok(vec![]),
367 Some(filename) => {
368 let (data, filename) = open_read(filename, "certificate")?;
369 der_or_pem(
370 data,
371 |der| X509::from_der(&der).map(|x509| vec![x509]),
372 |pem| {
373 pem.match_indices(PEM_START_MARKER)
374 .map(|(start, _)| X509::from_pem(&pem.as_bytes()[start..]))
375 .collect()
376 },
377 )
378 .with_context(|_| X509ParseSnafu { filename })
379 }
380 }
381 }
382
383 fn load_identity(&self) -> Result<Option<IdentityStore>> {
384 match (&self.crt_file, &self.key_file) {
385 (None, Some(_)) => Err(TlsError::MissingCrtKeyFile),
386 (None, None) => Ok(None),
387 (Some(filename), _) => {
388 let (data, filename) = open_read(filename, "certificate")?;
389 der_or_pem(
390 data,
391 |der| self.parse_pkcs12_identity(der),
392 |pem| self.parse_pem_identity(&pem, &filename),
393 )
394 }
395 }
396 }
397
398 fn parse_alpn_protocols(&self) -> Result<Option<Vec<u8>>> {
402 match &self.alpn_protocols {
403 None => Ok(None),
404 Some(protocols) => {
405 let mut data: Vec<u8> = Vec::new();
406 for str in protocols {
407 data.push(str.len().try_into().context(EncodeAlpnProtocolsSnafu)?);
408 data.append(&mut str.clone().into_bytes());
409 }
410 Ok(Some(data))
411 }
412 }
413 }
414
415 fn parse_pem_identity(&self, pem: &str, crt_file: &Path) -> Result<Option<IdentityStore>> {
417 match &self.key_file {
418 None => Err(TlsError::MissingKey),
419 Some(key_file) => {
420 let name = crt_file.to_string_lossy().to_string();
421 let mut crt_stack = X509::stack_from_pem(pem.as_bytes())
422 .with_context(|_| X509ParseSnafu { filename: crt_file })?
423 .into_iter();
424
425 let crt = crt_stack.next().ok_or(TlsError::MissingCertificate)?;
426 let key = load_key(key_file.as_path(), self.key_pass.as_ref())?;
427
428 let mut ca_stack = Stack::new().context(NewCaStackSnafu)?;
429 for intermediate in crt_stack {
430 ca_stack.push(intermediate).context(CaStackPushSnafu)?;
431 }
432
433 let pkcs12 = Pkcs12::builder()
434 .ca(ca_stack)
435 .name(&name)
436 .pkey(&key)
437 .cert(&crt)
438 .build2("")
439 .context(Pkcs12Snafu)?;
440 let identity = pkcs12.to_der().context(DerExportSnafu)?;
441
442 pkcs12.parse2("").context(TlsIdentitySnafu)?;
446
447 Ok(Some(IdentityStore(identity, String::new())))
448 }
449 }
450 }
451
452 fn parse_pkcs12_identity(&self, der: Vec<u8>) -> Result<Option<IdentityStore>> {
454 let pkcs12 = Pkcs12::from_der(&der).context(ParsePkcs12Snafu)?;
455 let key_pass = self.key_pass.as_deref().unwrap_or("");
457 pkcs12.parse2(key_pass).context(ParsePkcs12Snafu)?;
458 Ok(Some(IdentityStore(der, key_pass.to_string())))
459 }
460}
461
462#[cfg(windows)]
469fn load_windows_certs(builder: &mut SslContextBuilder) -> Result<()> {
470 use super::SchannelSnafu;
471
472 let mut store = X509StoreBuilder::new().context(NewStoreBuilderSnafu)?;
473
474 let current_user_store =
475 schannel::cert_store::CertStore::open_current_user("ROOT").context(SchannelSnafu)?;
476
477 for cert in current_user_store.certs() {
478 let cert = cert.to_der().to_vec();
479 let cert = X509::from_der(&cert[..]).context(super::X509SystemParseSnafu)?;
480 store.add_cert(cert).context(AddCertToStoreSnafu)?;
481 }
482
483 builder
484 .set_verify_cert_store(store.build())
485 .context(SetVerifyCertSnafu)?;
486
487 Ok(())
488}
489
490#[cfg(target_os = "macos")]
491fn load_mac_certs(builder: &mut SslContextBuilder) -> Result<()> {
492 use std::collections::HashMap;
493
494 use security_framework::trust_settings::{Domain, TrustSettings, TrustSettingsForCertificate};
495
496 use super::SecurityFrameworkSnafu;
497
498 let mut store = X509StoreBuilder::new().context(NewStoreBuilderSnafu)?;
510 let mut all_certs = HashMap::new();
511
512 for domain in &[Domain::User, Domain::Admin, Domain::System] {
513 let ts = TrustSettings::new(*domain);
514
515 for cert in ts.iter().context(SecurityFrameworkSnafu)? {
516 let trusted = ts
523 .tls_trust_settings_for_certificate(&cert)
524 .context(SecurityFrameworkSnafu)?
525 .unwrap_or(TrustSettingsForCertificate::TrustRoot);
526
527 all_certs.entry(cert.to_der()).or_insert(trusted);
528 }
529 }
530
531 for (cert, trusted) in all_certs {
532 if matches!(
533 trusted,
534 TrustSettingsForCertificate::TrustRoot | TrustSettingsForCertificate::TrustAsRoot
535 ) {
536 let cert = X509::from_der(&cert[..]).context(super::X509SystemParseSnafu)?;
537 store.add_cert(cert).context(AddCertToStoreSnafu)?;
538 }
539 }
540
541 builder
542 .set_verify_cert_store(store.build())
543 .context(SetVerifyCertSnafu)?;
544
545 Ok(())
546}
547
548impl fmt::Debug for TlsSettings {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550 f.debug_struct("TlsSettings")
551 .field("verify_certificate", &self.verify_certificate)
552 .field("verify_hostname", &self.verify_hostname)
553 .finish_non_exhaustive()
554 }
555}
556
557pub type MaybeTlsSettings = MaybeTls<(), TlsSettings>;
558
559impl MaybeTlsSettings {
560 pub fn enable_client() -> Result<Self> {
561 let tls = TlsSettings::from_options_base(None, false)?;
562 Ok(Self::Tls(tls))
563 }
564
565 pub fn tls_client(config: Option<&TlsConfig>) -> Result<Self> {
566 Ok(Self::Tls(TlsSettings::from_options_base(config, false)?))
567 }
568
569 pub fn from_config(config: Option<&TlsEnableableConfig>, for_server: bool) -> Result<Self> {
576 match config {
577 None => Ok(Self::Raw(())), Some(config) => {
579 if config.enabled.unwrap_or(false) {
580 let tls = TlsSettings::from_options_base(Some(&config.options), for_server)?;
581 match (for_server, &tls.identity) {
582 (true, None) => Err(TlsError::MissingRequiredIdentity),
584 _ => Ok(Self::Tls(tls)),
585 }
586 } else {
587 Ok(Self::Raw(())) }
589 }
590 }
591 }
592
593 pub const fn http_protocol_name(&self) -> &'static str {
594 match self {
595 MaybeTls::Raw(()) => "http",
596 MaybeTls::Tls(_) => "https",
597 }
598 }
599}
600
601impl From<TlsSettings> for MaybeTlsSettings {
602 fn from(tls: TlsSettings) -> Self {
603 Self::Tls(tls)
604 }
605}
606
607fn load_key(filename: &Path, pass_phrase: Option<&String>) -> Result<PKey<Private>> {
609 let (data, filename) = open_read(filename, "key")?;
610 match pass_phrase {
611 None => der_or_pem(
612 data,
613 |der| PKey::private_key_from_der(&der),
614 |pem| PKey::private_key_from_pem(pem.as_bytes()),
615 )
616 .with_context(|_| PrivateKeyParseSnafu { filename }),
617 Some(phrase) => der_or_pem(
618 data,
619 |der| PKey::private_key_from_pkcs8_passphrase(&der, phrase.as_bytes()),
620 |pem| PKey::private_key_from_pem_passphrase(pem.as_bytes(), phrase.as_bytes()),
621 )
622 .with_context(|_| PrivateKeyParseSnafu { filename }),
623 }
624}
625
626fn der_or_pem<T>(data: Vec<u8>, der_fn: impl Fn(Vec<u8>) -> T, pem_fn: impl Fn(String) -> T) -> T {
630 match String::from_utf8(data) {
633 Ok(text) => match text.find(PEM_START_MARKER) {
634 Some(_) => pem_fn(text),
635 None => der_fn(text.into_bytes()),
636 },
637 Err(err) => der_fn(err.into_bytes()),
638 }
639}
640
641fn open_read(filename: &Path, note: &'static str) -> Result<(Vec<u8>, PathBuf)> {
645 if let Some(filename) = filename.to_str() {
646 if filename.contains(PEM_START_MARKER) {
647 return Ok((Vec::from(filename), "inline text".into()));
648 }
649 }
650
651 let mut text = Vec::<u8>::new();
652
653 File::open(filename)
654 .with_context(|_| FileOpenFailedSnafu { note, filename })?
655 .read_to_end(&mut text)
656 .with_context(|_| FileReadFailedSnafu { note, filename })?;
657
658 Ok((text, filename.into()))
659}
660
661#[cfg(test)]
662mod test {
663 use super::*;
664
665 const TEST_PKCS12_PATH: &str = "tests/data/ca/intermediate_client/private/localhost.p12";
666 const TEST_PEM_CRT_BYTES: &[u8] =
667 include_bytes!("../../../../tests/data/ca/intermediate_server/certs/localhost.cert.pem");
668 const TEST_PEM_KEY_BYTES: &[u8] =
669 include_bytes!("../../../../tests/data/ca/intermediate_server/private/localhost.key.pem");
670
671 #[test]
672 fn parse_alpn_protocols() {
673 let options = TlsConfig {
674 alpn_protocols: Some(vec![String::from("h2")]),
675 ..Default::default()
676 };
677 let settings =
678 TlsSettings::from_options(Some(&options)).expect("Failed to parse alpn_protocols");
679 assert_eq!(settings.alpn_protocols, Some(vec![2, 104, 50]));
680 }
681
682 #[test]
683 fn from_options_pkcs12() {
684 let _provider = openssl::provider::Provider::try_load(None, "legacy", true).unwrap();
685 let options = TlsConfig {
686 crt_file: Some(TEST_PKCS12_PATH.into()),
687 key_pass: Some("NOPASS".into()),
688 ..Default::default()
689 };
690 let settings =
691 TlsSettings::from_options(Some(&options)).expect("Failed to load PKCS#12 certificate");
692 assert!(settings.identity.is_some());
693 assert_eq!(settings.authorities.len(), 0);
694 }
695
696 #[test]
697 fn from_options_pem() {
698 let options = TlsConfig {
699 crt_file: Some(TEST_PEM_CRT_PATH.into()),
700 key_file: Some(TEST_PEM_KEY_PATH.into()),
701 ..Default::default()
702 };
703 let settings =
704 TlsSettings::from_options(Some(&options)).expect("Failed to load PEM certificate");
705 assert!(settings.identity.is_some());
706 assert_eq!(settings.authorities.len(), 0);
707 }
708
709 #[test]
710 fn from_options_inline_pem() {
711 let crt = String::from_utf8(TEST_PEM_CRT_BYTES.to_vec()).unwrap();
712 let key = String::from_utf8(TEST_PEM_KEY_BYTES.to_vec()).unwrap();
713 let options = TlsConfig {
714 crt_file: Some(crt.into()),
715 key_file: Some(key.into()),
716 ..Default::default()
717 };
718 let settings =
719 TlsSettings::from_options(Some(&options)).expect("Failed to load PEM certificate");
720 assert!(settings.identity.is_some());
721 assert_eq!(settings.authorities.len(), 0);
722 }
723
724 #[test]
725 fn from_options_ca() {
726 let options = TlsConfig {
727 ca_file: Some(TEST_PEM_CA_PATH.into()),
728 ..Default::default()
729 };
730 let settings = TlsSettings::from_options(Some(&options))
731 .expect("Failed to load authority certificate");
732 assert!(settings.identity.is_none());
733 assert_eq!(settings.authorities.len(), 1);
734 }
735
736 #[test]
737 fn from_options_inline_ca() {
738 let ca = String::from_utf8(
739 include_bytes!("../../../../tests/data/ca/certs/ca.cert.pem").to_vec(),
740 )
741 .unwrap();
742 let options = TlsConfig {
743 ca_file: Some(ca.into()),
744 ..Default::default()
745 };
746 let settings = TlsSettings::from_options(Some(&options))
747 .expect("Failed to load authority certificate");
748 assert!(settings.identity.is_none());
749 assert_eq!(settings.authorities.len(), 1);
750 }
751
752 #[test]
753 fn from_options_intermediate_ca() {
754 let options = TlsConfig {
755 ca_file: Some("tests/data/ca/intermediate_server/certs/ca-chain.cert.pem".into()),
756 ..Default::default()
757 };
758 let settings = TlsSettings::from_options(Some(&options))
759 .expect("Failed to load authority certificate");
760 assert!(settings.identity.is_none());
761 assert_eq!(settings.authorities.len(), 2);
762 }
763
764 #[test]
765 fn from_options_multi_ca() {
766 let options = TlsConfig {
767 ca_file: Some("tests/data/Multi_CA.crt".into()),
768 ..Default::default()
769 };
770 let settings = TlsSettings::from_options(Some(&options))
771 .expect("Failed to load authority certificate");
772 assert!(settings.identity.is_none());
773 assert_eq!(settings.authorities.len(), 2);
774 }
775
776 #[test]
777 fn from_options_none() {
778 let settings = TlsSettings::from_options(None).expect("Failed to generate null settings");
779 assert!(settings.identity.is_none());
780 assert_eq!(settings.authorities.len(), 0);
781 }
782
783 #[test]
784 fn from_options_bad_certificate() {
785 let options = TlsConfig {
786 key_file: Some(TEST_PEM_KEY_PATH.into()),
787 ..Default::default()
788 };
789 let error = TlsSettings::from_options(Some(&options))
790 .expect_err("from_options failed to check certificate");
791 assert!(matches!(error, TlsError::MissingCrtKeyFile));
792
793 let options = TlsConfig {
794 crt_file: Some(TEST_PEM_CRT_PATH.into()),
795 ..Default::default()
796 };
797 let _error = TlsSettings::from_options(Some(&options))
798 .expect_err("from_options failed to check certificate");
799 }
801
802 #[test]
803 fn from_config_none() {
804 assert!(MaybeTlsSettings::from_config(None, true).unwrap().is_raw());
805 assert!(MaybeTlsSettings::from_config(None, false).unwrap().is_raw());
806 }
807
808 #[test]
809 fn from_config_not_enabled() {
810 assert!(settings_from_config(None, false, false, true).is_raw());
811 assert!(settings_from_config(None, false, false, false).is_raw());
812 assert!(settings_from_config(Some(false), false, false, true).is_raw());
813 assert!(settings_from_config(Some(false), false, false, false).is_raw());
814 }
815
816 #[test]
817 fn from_config_fails_without_certificate() {
818 let config = make_config(Some(true), false, false);
819 let error = MaybeTlsSettings::from_config(Some(&config), true)
820 .expect_err("from_config failed to check for a certificate");
821 assert!(matches!(error, TlsError::MissingRequiredIdentity));
822 }
823
824 #[test]
825 fn from_config_with_certificate() {
826 let config = settings_from_config(Some(true), true, true, true);
827 assert!(config.is_tls());
828 }
829
830 fn settings_from_config(
831 enabled: Option<bool>,
832 set_crt: bool,
833 set_key: bool,
834 for_server: bool,
835 ) -> MaybeTlsSettings {
836 let config = make_config(enabled, set_crt, set_key);
837 MaybeTlsSettings::from_config(Some(&config), for_server)
838 .expect("Failed to generate settings from config")
839 }
840
841 fn make_config(enabled: Option<bool>, set_crt: bool, set_key: bool) -> TlsEnableableConfig {
842 TlsEnableableConfig {
843 enabled,
844 options: TlsConfig {
845 crt_file: set_crt.then(|| TEST_PEM_CRT_PATH.into()),
846 key_file: set_key.then(|| TEST_PEM_KEY_PATH.into()),
847 ..Default::default()
848 },
849 }
850 }
851}