vector_core/tls/
mod.rs

1#![allow(clippy::missing_errors_doc)]
2
3use std::{fmt::Debug, net::SocketAddr, num::TryFromIntError, path::PathBuf, time::Duration};
4
5use openssl::{
6    error::ErrorStack,
7    ssl::{ConnectConfiguration, SslConnector, SslConnectorBuilder, SslMethod},
8};
9use snafu::{ResultExt, Snafu};
10use tokio::net::TcpStream;
11use tokio_openssl::SslStream;
12
13use crate::tcp::{self, TcpKeepaliveConfig};
14
15mod incoming;
16mod maybe_tls;
17mod outgoing;
18mod settings;
19
20pub use incoming::{CertificateMetadata, MaybeTlsIncomingStream, MaybeTlsListener};
21pub use maybe_tls::MaybeTls;
22pub use settings::{
23    MaybeTlsSettings, PEM_START_MARKER, TEST_PEM_CA_PATH, TEST_PEM_CLIENT_CRT_PATH,
24    TEST_PEM_CLIENT_KEY_PATH, TEST_PEM_CRT_PATH, TEST_PEM_INTERMEDIATE_CA_PATH, TEST_PEM_KEY_PATH,
25    TlsConfig, TlsEnableableConfig, TlsSettings, TlsSourceConfig,
26};
27
28pub type Result<T> = std::result::Result<T, TlsError>;
29
30pub type MaybeTlsStream<S> = MaybeTls<S, SslStream<S>>;
31
32#[derive(Debug, Snafu)]
33pub enum TlsError {
34    #[snafu(display("Could not open {} file {:?}: {}", note, filename, source))]
35    FileOpenFailed {
36        note: &'static str,
37        filename: PathBuf,
38        source: std::io::Error,
39    },
40    #[snafu(display("Could not read {} file {:?}: {}", note, filename, source))]
41    FileReadFailed {
42        note: &'static str,
43        filename: PathBuf,
44        source: std::io::Error,
45    },
46    #[snafu(display("Could not build TLS connector: {}", source))]
47    TlsBuildConnector { source: ErrorStack },
48    #[snafu(display("Could not set TCP TLS identity: {}", source))]
49    TlsIdentityError { source: ErrorStack },
50    #[snafu(display("Could not export identity to DER: {}", source))]
51    DerExportError { source: ErrorStack },
52    #[snafu(display("Identity certificate is missing a key"))]
53    MissingKey,
54    #[snafu(display("Certificate file contains no certificates"))]
55    MissingCertificate,
56    #[snafu(display("Could not parse certificate in {:?}: {}", filename, source))]
57    CertificateParseError {
58        filename: PathBuf,
59        source: ErrorStack,
60    },
61    #[snafu(display("Must specify both TLS key_file and crt_file"))]
62    MissingCrtKeyFile,
63    #[snafu(display("Could not parse X509 certificate in {:?}: {}", filename, source))]
64    X509ParseError {
65        filename: PathBuf,
66        source: ErrorStack,
67    },
68    #[snafu(display("Could not parse private key in {:?}: {}", filename, source))]
69    PrivateKeyParseError {
70        filename: PathBuf,
71        source: ErrorStack,
72    },
73    #[snafu(display("Could not build PKCS#12 archive for identity: {}", source))]
74    Pkcs12Error { source: ErrorStack },
75    #[snafu(display("Could not parse identity in {:?}: {}", filename, source))]
76    IdentityParseError {
77        filename: PathBuf,
78        source: ErrorStack,
79    },
80    #[snafu(display("TLS configuration requires a certificate when enabled"))]
81    MissingRequiredIdentity,
82    #[snafu(display("TLS handshake failed: {}", source))]
83    Handshake { source: openssl::ssl::Error },
84    #[snafu(display("Incoming listener failed: {}", source))]
85    IncomingListener { source: tokio::io::Error },
86    #[snafu(display("Creating the TLS acceptor failed: {}", source))]
87    CreateAcceptor { source: ErrorStack },
88    #[snafu(display("Error building SSL context: {}", source))]
89    SslBuildError { source: openssl::error::ErrorStack },
90    #[snafu(display("Error setting up the TLS certificate: {}", source))]
91    SetCertificate { source: ErrorStack },
92    #[snafu(display("Error setting up the TLS private key: {}", source))]
93    SetPrivateKey { source: ErrorStack },
94    #[snafu(display("Error setting up the TLS chain certificates: {}", source))]
95    AddExtraChainCert { source: ErrorStack },
96    #[snafu(display("Error creating a certificate store: {}", source))]
97    NewStoreBuilder { source: ErrorStack },
98    #[snafu(display("Error adding a certificate to a store: {}", source))]
99    AddCertToStore { source: ErrorStack },
100    #[snafu(display("Error setting up the verification certificate: {}", source))]
101    SetVerifyCert { source: ErrorStack },
102    #[snafu(display("Error setting SNI: {}", source))]
103    SetSni { source: ErrorStack },
104    #[snafu(display("Error setting ALPN protocols: {}", source))]
105    SetAlpnProtocols { source: ErrorStack },
106    #[snafu(display(
107        "Error encoding ALPN protocols, could not encode length as u8: {}",
108        source
109    ))]
110    EncodeAlpnProtocols { source: TryFromIntError },
111    #[snafu(display("PKCS#12 parse failed: {}", source))]
112    ParsePkcs12 { source: ErrorStack },
113    #[snafu(display("TCP bind failed: {}", source))]
114    TcpBind { source: tokio::io::Error },
115    #[snafu(display("{}", source))]
116    Connect { source: tokio::io::Error },
117    #[snafu(display("Could not get peer address: {}", source))]
118    PeerAddress { source: std::io::Error },
119    #[snafu(display("Security Framework Error: {}", source))]
120    #[cfg(target_os = "macos")]
121    SecurityFramework {
122        source: security_framework::base::Error,
123    },
124    #[snafu(display("Schannel Error: {}", source))]
125    #[cfg(windows)]
126    Schannel { source: std::io::Error },
127    #[cfg(any(windows, target_os = "macos"))]
128    #[snafu(display("Unable to parse X509 from system cert: {}", source))]
129    X509SystemParseError { source: ErrorStack },
130    #[snafu(display("Creating an empty CA stack failed"))]
131    NewCaStack { source: ErrorStack },
132    #[snafu(display("Could not push intermediate certificate onto stack"))]
133    CaStackPush { source: ErrorStack },
134}
135
136impl MaybeTlsStream<TcpStream> {
137    pub fn peer_addr(&self) -> std::result::Result<SocketAddr, std::io::Error> {
138        match self {
139            Self::Raw(raw) => raw.peer_addr(),
140            Self::Tls(tls) => tls.get_ref().peer_addr(),
141        }
142    }
143
144    pub fn set_keepalive(&mut self, keepalive: TcpKeepaliveConfig) -> std::io::Result<()> {
145        let stream = match self {
146            Self::Raw(raw) => raw,
147            Self::Tls(tls) => tls.get_ref(),
148        };
149
150        if let Some(time_secs) = keepalive.time_secs {
151            let config = socket2::TcpKeepalive::new().with_time(Duration::from_secs(time_secs));
152
153            tcp::set_keepalive(stream, &config)?;
154        }
155
156        Ok(())
157    }
158
159    pub fn set_send_buffer_bytes(&mut self, bytes: usize) -> std::io::Result<()> {
160        let stream = match self {
161            Self::Raw(raw) => raw,
162            Self::Tls(tls) => tls.get_ref(),
163        };
164
165        tcp::set_send_buffer_size(stream, bytes)
166    }
167
168    pub fn set_receive_buffer_bytes(&mut self, bytes: usize) -> std::io::Result<()> {
169        let stream = match self {
170            Self::Raw(raw) => raw,
171            Self::Tls(tls) => tls.get_ref(),
172        };
173
174        tcp::set_receive_buffer_size(stream, bytes)
175    }
176}
177
178pub fn tls_connector_builder(settings: &MaybeTlsSettings) -> Result<SslConnectorBuilder> {
179    let mut builder = SslConnector::builder(SslMethod::tls()).context(TlsBuildConnectorSnafu)?;
180    if let Some(settings) = settings.tls() {
181        settings.apply_context(&mut builder)?;
182    }
183    Ok(builder)
184}
185
186fn tls_connector(settings: &MaybeTlsSettings) -> Result<ConnectConfiguration> {
187    let mut configure = tls_connector_builder(settings)?
188        .build()
189        .configure()
190        .context(TlsBuildConnectorSnafu)?;
191    let tls_setting = settings.tls().cloned();
192    if let Some(tls_setting) = &tls_setting {
193        tls_setting
194            .apply_connect_configuration(&mut configure)
195            .context(SetSniSnafu)?;
196    }
197    Ok(configure)
198}