datadog_agent_commons/ipc/
tls.rs1use std::{
4 path::Path,
5 sync::Arc,
6 time::{Duration, Instant},
7};
8
9use rustls::{
10 client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
11 crypto::CryptoProvider,
12 pki_types::{CertificateDer, ServerName, UnixTime},
13 version::TLS13,
14 CertificateError, ClientConfig, DigitallySignedStruct, ServerConfig, SignatureScheme,
15};
16use rustls_pki_types::{pem::PemObject as _, PrivateKeyDer};
17use saluki_error::{generic_error, ErrorContext as _, GenericError};
18use saluki_tls::{ensure_client_config_fips_compliant, ensure_server_config_fips_compliant};
19
20const DEFAULT_CERT_READ_TIMEOUT: Duration = Duration::from_secs(20);
21const DEFAULT_CERT_READ_INTERVAL: Duration = Duration::from_millis(100);
22
23#[derive(Debug)]
24struct DatadogAgentServerCertVerifier {
25 cert: CertificateDer<'static>,
26 provider: Arc<CryptoProvider>,
27}
28
29impl DatadogAgentServerCertVerifier {
30 fn from_certificate_and_provider(cert: CertificateDer<'static>, provider: Arc<CryptoProvider>) -> Self {
31 Self { cert, provider }
32 }
33}
34
35impl ServerCertVerifier for DatadogAgentServerCertVerifier {
36 fn verify_server_cert(
37 &self, end_entity: &CertificateDer<'_>, _intermediates: &[CertificateDer<'_>], _server_name: &ServerName<'_>,
38 _ocsp_response: &[u8], _now: UnixTime,
39 ) -> Result<ServerCertVerified, rustls::Error> {
40 if end_entity != &self.cert {
45 return Err(rustls::Error::InvalidCertificate(CertificateError::UnknownIssuer));
46 }
47
48 Ok(ServerCertVerified::assertion())
49 }
50
51 fn verify_tls12_signature(
52 &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct,
53 ) -> Result<HandshakeSignatureValid, rustls::Error> {
54 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
55 }
56
57 fn verify_tls13_signature(
58 &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct,
59 ) -> Result<HandshakeSignatureValid, rustls::Error> {
60 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.provider.signature_verification_algorithms)
61 }
62
63 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
64 self.provider.signature_verification_algorithms.supported_schemes()
65 }
66}
67
68pub async fn build_ipc_client_ipc_tls_config<P: AsRef<Path>>(cert_path: P) -> Result<ClientConfig, GenericError> {
78 let (parsed_cert, parsed_key) = read_and_parse_certificate_file(
80 cert_path.as_ref(),
81 DEFAULT_CERT_READ_TIMEOUT,
82 DEFAULT_CERT_READ_INTERVAL,
83 )
84 .await?;
85
86 let crypto_provider = rustls::crypto::CryptoProvider::get_default()
88 .map(Arc::clone)
89 .ok_or_else(|| generic_error!("Default cryptography provider not yet installed."))?;
90 let agent_cert_verifier = Arc::new(DatadogAgentServerCertVerifier::from_certificate_and_provider(
91 parsed_cert.clone(),
92 crypto_provider,
93 ));
94
95 let config = ClientConfig::builder_with_protocol_versions(&[&TLS13])
96 .dangerous()
97 .with_custom_certificate_verifier(agent_cert_verifier)
98 .with_client_auth_cert(vec![parsed_cert], parsed_key)
99 .with_error_context(|| {
100 format!(
101 "Failed to build client TLS configuration from certificate file '{}'.",
102 cert_path.as_ref().display()
103 )
104 })?;
105
106 ensure_client_config_fips_compliant(&config)?;
107
108 Ok(config)
109}
110
111pub async fn build_ipc_server_tls_config<P: AsRef<Path>>(cert_path: P) -> Result<ServerConfig, GenericError> {
121 let (parsed_cert, parsed_key) = read_and_parse_certificate_file(
123 cert_path.as_ref(),
124 DEFAULT_CERT_READ_TIMEOUT,
125 DEFAULT_CERT_READ_INTERVAL,
126 )
127 .await?;
128
129 let mut config = ServerConfig::builder()
130 .with_no_client_auth()
131 .with_single_cert(vec![parsed_cert], parsed_key)
132 .with_error_context(|| {
133 format!(
134 "Failed to build server TLS configuration from certificate file '{}'.",
135 cert_path.as_ref().display()
136 )
137 })?;
138
139 ensure_server_config_fips_compliant(&mut config)?;
140
141 Ok(config)
142}
143
144async fn read_and_parse_certificate_file(
154 cert_path: &Path, timeout: Duration, interval: Duration,
155) -> Result<(CertificateDer<'static>, PrivateKeyDer<'static>), GenericError> {
156 if timeout < interval {
157 return Err(generic_error!(
158 "Timeout is less than interval ({} < {}).",
159 timeout.as_secs(),
160 interval.as_secs()
161 ));
162 }
163
164 let start_time = Instant::now();
165 let mut last_error = String::new();
166 while start_time.elapsed() < timeout {
167 match tokio::fs::read(cert_path).await {
168 Ok(raw_cert_data) => {
169 let parsed_cert = CertificateDer::from_pem_slice(&raw_cert_data[..])
170 .with_error_context(|| format!("Failed to parse certificate file '{}'.", cert_path.display()))?
171 .into_owned();
172
173 let parsed_key = PrivateKeyDer::from_pem_slice(&raw_cert_data[..])
174 .with_error_context(|| format!("Failed to parse private key file '{}'.", cert_path.display()))?
175 .clone_key();
176
177 return Ok((parsed_cert, parsed_key));
178 }
179 Err(e) => {
180 last_error = e.to_string();
181 tokio::time::sleep(interval).await;
182 }
183 }
184 }
185
186 Err(generic_error!(
187 "Failed to read certificate file '{}' after {} seconds: {}",
188 cert_path.display(),
189 timeout.as_secs(),
190 last_error
191 ))
192}