diff --git a/lib/vector-core/src/tls/incoming.rs b/lib/vector-core/src/tls/incoming.rs index 0d959bb170e1d..657d095163351 100644 --- a/lib/vector-core/src/tls/incoming.rs +++ b/lib/vector-core/src/tls/incoming.rs @@ -33,8 +33,23 @@ impl TlsSettings { if self.identity.is_none() { Err(TlsError::MissingRequiredIdentity) } else { - let mut acceptor = - SslAcceptor::mozilla_intermediate(SslMethod::tls()).context(CreateAcceptorSnafu)?; + // Use custom acceptor if TLS version, ciphersuites, or curves are configured + let mut acceptor = if self.min_tls_version.is_some() || self.ciphersuites.is_some() || self.curves.is_some() { + SslAcceptor::custom( + SslMethod::tls(), + &self.min_tls_version, + &self.ciphersuites, + &self.curves, + ) + .map_err(|e| match e { + openssl::ssl::ErrorEx::OpenSslError { error_stack } => TlsError::CreateAcceptor { source: error_stack }, + openssl::ssl::ErrorEx::InvalidTlsVersion => TlsError::SslBuildError { source: openssl::error::ErrorStack::get() }, + openssl::ssl::ErrorEx::InvalidCiphersuite => TlsError::SslBuildError { source: openssl::error::ErrorStack::get() }, + openssl::ssl::ErrorEx::InvalidCurve { error_stack } => TlsError::SetCurves { source: error_stack }, + })? + } else { + SslAcceptor::mozilla_intermediate(SslMethod::tls()).context(CreateAcceptorSnafu)? + }; self.apply_context_base(&mut acceptor, true)?; Ok(acceptor.build()) } diff --git a/lib/vector-core/src/tls/mod.rs b/lib/vector-core/src/tls/mod.rs index 5e2e06aa4462b..1f25cfbe8b3b9 100644 --- a/lib/vector-core/src/tls/mod.rs +++ b/lib/vector-core/src/tls/mod.rs @@ -108,6 +108,8 @@ pub enum TlsError { source ))] EncodeAlpnProtocols { source: TryFromIntError }, + #[snafu(display("Error setting TLS curves: {}", source))] + SetCurves { source: ErrorStack }, #[snafu(display("PKCS#12 parse failed: {}", source))] ParsePkcs12 { source: ErrorStack }, #[snafu(display("TCP bind failed: {}", source))] diff --git a/lib/vector-core/src/tls/settings.rs b/lib/vector-core/src/tls/settings.rs index 3a766c6b44f69..bbe16f0bf6b83 100644 --- a/lib/vector-core/src/tls/settings.rs +++ b/lib/vector-core/src/tls/settings.rs @@ -9,7 +9,7 @@ use super::{ AddCertToStoreSnafu, AddExtraChainCertSnafu, CaStackPushSnafu, EncodeAlpnProtocolsSnafu, FileOpenFailedSnafu, FileReadFailedSnafu, MaybeTls, NewCaStackSnafu, NewStoreBuilderSnafu, ParsePkcs12Snafu, PrivateKeyParseSnafu, Result, SetAlpnProtocolsSnafu, SetCertificateSnafu, - SetPrivateKeySnafu, SetVerifyCertSnafu, TlsError, X509ParseSnafu, + SetCurvesSnafu, SetPrivateKeySnafu, SetVerifyCertSnafu, TlsError, X509ParseSnafu, }; use cfg_if::cfg_if; use lookup::lookup_v2::OptionalValuePath; @@ -162,6 +162,15 @@ pub struct TlsConfig { /// TLS ciphersuites to enable. pub ciphersuites: Option, + + /// TLS elliptic curve groups (curves) to enable. + /// + /// Specifies the list of elliptic curve groups (curves) to use for TLS connections. + /// The curves are specified as a colon-separated list of curve names. + /// + /// Common curve names include: prime256v1, secp384r1, secp521r1, X25519, X448. + #[configurable(metadata(docs::examples = "prime256v1:secp384r1"))] + pub curves: Option, } impl TlsConfig { @@ -186,6 +195,7 @@ pub struct TlsSettings { server_name: Option, pub min_tls_version: Option, pub ciphersuites: Option, + pub curves: Option, } /// Identity store in PEM format @@ -230,6 +240,7 @@ impl TlsSettings { server_name: options.server_name.clone(), min_tls_version: options.min_tls_version.clone(), ciphersuites: options.ciphersuites.clone(), + curves: options.curves.clone(), }) } @@ -347,6 +358,13 @@ impl TlsSettings { } } + // Configure curves if specified + if let Some(ref curves) = self.curves { + context + .set_groups_list(curves) + .context(SetCurvesSnafu)?; + } + Ok(()) } @@ -880,7 +898,7 @@ mod test { }, ]; for t in tests { - match builder.set_min_tls_version_and_ciphersuites(&t.text, &None) { + match builder.set_min_tls_version_and_ciphersuites(&t.text, &None, &None) { Ok(()) => { assert!(t.want.is_ok()); assert_eq!(builder.min_proto_version(), t.num); @@ -921,7 +939,7 @@ mod test { }, ]; for t in tests { - match builder.set_min_tls_version_and_ciphersuites(&t.min_tls_version, &t.ciphersuite) { + match builder.set_min_tls_version_and_ciphersuites(&t.min_tls_version, &t.ciphersuite, &None) { Ok(()) => assert!(t.want.is_ok()), Err(e) => assert_eq!(t.want.err().unwrap(), e), } @@ -949,4 +967,33 @@ mod test { }, } } + + #[test] + fn from_options_curves() { + let options = TlsConfig { + curves: Some("prime256v1:secp384r1".to_string()), + ..Default::default() + }; + let settings = TlsSettings::from_options(Some(&options)) + .expect("Failed to parse curves"); + assert_eq!(settings.curves, Some("prime256v1:secp384r1".to_string())); + } + + #[test] + fn from_options_with_curves_applied() { + use openssl::ssl::SslConnector; + + let options = TlsConfig { + curves: Some("prime256v1:secp384r1:secp521r1".to_string()), + ..Default::default() + }; + let settings = TlsSettings::from_options(Some(&options)) + .expect("Failed to parse curves"); + + // Test that the context can be created with curves + let mut builder = SslConnector::builder(SslMethod::tls()) + .expect("Failed to create SSL builder"); + settings.apply_context(&mut builder) + .expect("Failed to apply context with curves"); + } } diff --git a/patch/openssl/src/ssl/connector.rs b/patch/openssl/src/ssl/connector.rs index 14aadfe3e2569..f65a509645b0a 100644 --- a/patch/openssl/src/ssl/connector.rs +++ b/patch/openssl/src/ssl/connector.rs @@ -317,14 +317,14 @@ impl SslAcceptor { Ok(SslAcceptorBuilder(ctx)) } - /// Creates a new builder configured with a minimum supported TLS version and a set of ciphersuites + /// Creates a new builder configured with a minimum supported TLS version, a set of ciphersuites, and elliptic curves /// - pub fn custom(method: SslMethod, min_tls_version: &Option, ciphersuites: &Option) -> Result { + pub fn custom(method: SslMethod, min_tls_version: &Option, ciphersuites: &Option, curves: &Option) -> Result { let mut ctx = ctx(method).map_err(|e| ErrorEx::OpenSslError { error_stack: e })?; let dh = Dh::params_from_pem(FFDHE_2048.as_bytes()).map_err(|e| ErrorEx::OpenSslError { error_stack: e })?; ctx.set_tmp_dh(&dh).map_err(|e| ErrorEx::OpenSslError { error_stack: e })?; setup_curves(&mut ctx).map_err(|e| ErrorEx::OpenSslError { error_stack: e })?; - ctx.set_min_tls_version_and_ciphersuites(min_tls_version, ciphersuites)?; + ctx.set_min_tls_version_and_ciphersuites(min_tls_version, ciphersuites, curves)?; Ok(SslAcceptorBuilder(ctx)) } diff --git a/patch/openssl/src/ssl/error.rs b/patch/openssl/src/ssl/error.rs index ede4e371df166..0113056f858bc 100644 --- a/patch/openssl/src/ssl/error.rs +++ b/patch/openssl/src/ssl/error.rs @@ -191,6 +191,9 @@ pub enum ErrorEx { }, InvalidTlsVersion, InvalidCiphersuite, + InvalidCurve { + error_stack: ErrorStack + }, } impl PartialEq for ErrorEx { @@ -198,7 +201,8 @@ impl PartialEq for ErrorEx { match (self, other) { (ErrorEx::OpenSslError{..}, ErrorEx::OpenSslError{..}) | (ErrorEx::InvalidTlsVersion, ErrorEx::InvalidTlsVersion) - | (ErrorEx::InvalidCiphersuite, ErrorEx::InvalidCiphersuite) => true, + | (ErrorEx::InvalidCiphersuite, ErrorEx::InvalidCiphersuite) + | (ErrorEx::InvalidCurve{..}, ErrorEx::InvalidCurve{..}) => true, _ => false, } } diff --git a/patch/openssl/src/ssl/mod.rs b/patch/openssl/src/ssl/mod.rs index 419847da342ab..b85b06ca6ae39 100644 --- a/patch/openssl/src/ssl/mod.rs +++ b/patch/openssl/src/ssl/mod.rs @@ -1774,9 +1774,9 @@ impl SslContextBuilder { self.0 } - /// Sets the context's minimal TLS version, specified as "VersionTLS1[0..3]", and a comma-separated list of ciphersuites. + /// Sets the context's minimal TLS version, specified as "VersionTLS1[0..3]", a comma-separated list of ciphersuites, and a colon-separated list of curves. /// - pub fn set_min_tls_version_and_ciphersuites(&mut self, min_tls_version: &Option, ciphersuites: &Option) -> Result<(), ErrorEx>{ + pub fn set_min_tls_version_and_ciphersuites(&mut self, min_tls_version: &Option, ciphersuites: &Option, curves: &Option) -> Result<(), ErrorEx>{ let mut min_proto_version = SslVersion::TLS1; if let Some(min_tls_version) = min_tls_version { min_proto_version = match min_tls_version.as_str() { @@ -1800,6 +1800,14 @@ impl SslContextBuilder { return Err(ErrorEx::InvalidCiphersuite); } } + if let Some(ref curves) = curves { + if !curves.is_empty() { + self.set_groups_list(curves) + .map_err(|e| ErrorEx::InvalidCurve { error_stack: e })?; + } else { + return Err(ErrorEx::InvalidCurve { error_stack: crate::error::ErrorStack::get() }); + } + } Ok(()) } }