Skip to content

Commit 869dcdc

Browse files
chore: provide our own tls config so that we can check that it is FIPSish
1 parent a37ca6e commit 869dcdc

File tree

11 files changed

+151
-84
lines changed

11 files changed

+151
-84
lines changed

bottlecap/Cargo.lock

Lines changed: 34 additions & 65 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bottlecap/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ datadog-trace-protobuf = { git = "https://github.com/DataDog/libdatadog", rev =
1515
datadog-trace-utils = { git = "https://github.com/DataDog/libdatadog", rev = "d6a2da32c6b92d6865a7e7987c8a1df2203fb1ae" , features = ["compression"] }
1616
datadog-trace-normalization = { git = "https://github.com/DataDog/libdatadog", rev = "d6a2da32c6b92d6865a7e7987c8a1df2203fb1ae" }
1717
datadog-trace-obfuscation = { git = "https://github.com/DataDog/libdatadog", rev = "d6a2da32c6b92d6865a7e7987c8a1df2203fb1ae" }
18-
dogstatsd = { git = "https://github.com/DataDog/serverless-components", rev = "0aac11e23a31cdae22ebe42406028964a2b36cea", default-features = false }
19-
trace-agent = { git = "https://github.com/DataDog/serverless-components", rev = "0aac11e23a31cdae22ebe42406028964a2b36cea" }
18+
dogstatsd = { git = "https://github.com/DataDog/serverless-components", branch = "aleksandr.pasechnik/svls-6242-reqwest-fips-tls-injection", default-features = false }
19+
datadog-trace-agent = { git = "https://github.com/DataDog/serverless-components", branch = "aleksandr.pasechnik/svls-6242-reqwest-fips-tls-injection" }
2020
figment = { version = "0.10", default-features = false, features = ["yaml", "env"] }
2121
hyper = { version = "1.6", default-features = false, features = ["server"] }
2222
hyper-util = { version = "0.1.10", features = [
@@ -53,6 +53,7 @@ futures = { version = "0.3.31", default-features = false }
5353
serde-aux = { version = "4.7", default-features = false }
5454
opentelemetry-proto = { version = "0.29", features = ["trace", "with-serde", "gen-tonic"] }
5555
opentelemetry-semantic-conventions = { version = "0.29", features = ["semconv_experimental"] }
56+
rustls-native-certs = { version = "0.8.1", optional = true }
5657

5758
[dev-dependencies]
5859
figment = { version = "0.10", default-features = false, features = ["yaml", "env", "test"] }
@@ -86,5 +87,6 @@ fips = [
8687
"dogstatsd/fips",
8788
"reqwest/rustls-tls-native-roots-no-provider",
8889
"rustls/fips",
90+
"rustls-native-certs",
8991
]
9092
force_fallback = []

bottlecap/src/bin/bottlecap/main.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use bottlecap::{
1818
},
1919
event_bus::bus::EventBus,
2020
events::Event,
21+
fips::create_reqwest_client_builder,
2122
lifecycle::{
2223
flush_control::FlushControl, invocation::processor::Processor as InvocationProcessor,
2324
listener::Listener as LifecycleListener,
@@ -229,8 +230,13 @@ async fn main() -> Result<()> {
229230
let version_without_next = EXTENSION_VERSION.split('-').next().unwrap_or("NA");
230231
debug!("Starting Datadog Extension {version_without_next}");
231232
prepare_client_provider()?;
232-
let client = Client::builder()
233-
.use_rustls_tls()
233+
let client = create_reqwest_client_builder()
234+
.map_err(|e| {
235+
Error::new(
236+
std::io::ErrorKind::InvalidData,
237+
format!("Failed to create client builder: {e:?}"),
238+
)
239+
})?
234240
.no_proxy()
235241
.build()
236242
.map_err(|e| {

bottlecap/src/fips.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use reqwest::ClientBuilder;
2+
use std::error::Error;
3+
#[cfg(feature = "fips")]
4+
use tracing::debug;
5+
6+
// TODO: once we confirm that this does what we think it does we'll move it to a separate crate.
7+
// for now going to copy the this code to bottlecap and make sure that all the clients we build do
8+
// in fact do the fips thing right.
9+
10+
/// Creates a reqwest client builder with TLS configuration.
11+
/// When the "fips" feature is enabled, it uses a FIPS-compliant TLS configuration.
12+
/// Otherwise, it uses reqwest's default rustls TLS implementation.
13+
#[cfg(not(feature = "fips"))]
14+
pub fn create_reqwest_client_builder() -> Result<ClientBuilder, Box<dyn Error>> {
15+
// Just return the default builder with rustls TLS
16+
Ok(reqwest::Client::builder().use_rustls_tls())
17+
}
18+
19+
/// Creates a reqwest client builder with FIPS-compliant TLS configuration.
20+
/// This version loads native root certificates and verifies FIPS compliance.
21+
#[cfg(feature = "fips")]
22+
pub fn create_reqwest_client_builder() -> Result<ClientBuilder, Box<dyn Error>> {
23+
// Get the runtime crypto provider that should have been configured elsewhere in the application
24+
let provider =
25+
rustls::crypto::CryptoProvider::get_default().ok_or("No crypto provider configured")?;
26+
27+
// Verify the provider is FIPS-compliant
28+
if !provider.fips() {
29+
return Err("Crypto provider is not FIPS-compliant".into());
30+
}
31+
32+
// Create an empty root cert store
33+
let mut root_cert_store = rustls::RootCertStore::empty();
34+
35+
// Load native certificates
36+
let native_certs = rustls_native_certs::load_native_certs();
37+
38+
// Add the certificates to the store
39+
let mut valid_count = 0;
40+
41+
for cert in native_certs.certs {
42+
match root_cert_store.add(cert) {
43+
Ok(()) => valid_count += 1,
44+
Err(err) => {
45+
// Optionally log errors
46+
debug!("Failed to parse certificate: {:?}", err);
47+
}
48+
}
49+
}
50+
51+
// Verify we have at least some valid certificates
52+
if valid_count == 0 {
53+
return Err("No valid certificates found in native root store".into());
54+
}
55+
56+
// Configure TLS versions (FIPS typically requires TLS 1.2 or higher)
57+
let versions = rustls::ALL_VERSIONS.to_vec();
58+
59+
// Build the client config
60+
let config_builder = rustls::ClientConfig::builder_with_provider(provider.clone())
61+
.with_protocol_versions(&versions)
62+
.map_err(|_| "Failed to set protocol versions")?;
63+
64+
// Complete the configuration without client authentication
65+
let config = config_builder
66+
.with_root_certificates(root_cert_store)
67+
.with_no_client_auth();
68+
69+
// Verify the final config is FIPS-compliant
70+
if !config.fips() {
71+
return Err("The final TLS configuration is not FIPS-compliant".into());
72+
}
73+
debug!("Client Builder is in FIPS mode");
74+
75+
// Create the reqwest client builder with our FIPS-compliant TLS configuration
76+
let client_builder = reqwest::Client::builder().use_preconfigured_tls(config);
77+
78+
Ok(client_builder)
79+
}

bottlecap/src/http_client.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::config;
2+
use crate::fips::create_reqwest_client_builder;
23
use core::time::Duration;
4+
use std::error::Error;
35
use std::sync::Arc;
46
use tracing::error;
57

@@ -15,9 +17,8 @@ pub fn get_client(config: Arc<config::Config>) -> reqwest::Client {
1517
})
1618
}
1719

18-
fn build_client(config: Arc<config::Config>) -> Result<reqwest::Client, reqwest::Error> {
19-
let client = reqwest::Client::builder()
20-
.use_rustls_tls()
20+
fn build_client(config: Arc<config::Config>) -> Result<reqwest::Client, Box<dyn Error>> {
21+
let client = create_reqwest_client_builder()?
2122
.timeout(Duration::from_secs(config.flush_timeout))
2223
// Temporarily not force http2
2324
// Enable HTTP/2 for better multiplexing
@@ -36,8 +37,8 @@ fn build_client(config: Arc<config::Config>) -> Result<reqwest::Client, reqwest:
3637
// This covers DD_PROXY_HTTPS and HTTPS_PROXY
3738
if let Some(https_uri) = &config.https_proxy {
3839
let proxy = reqwest::Proxy::https(https_uri.clone())?;
39-
client.proxy(proxy).build()
40+
Ok(client.proxy(proxy).build()?)
4041
} else {
41-
client.build()
42+
Ok(client.build()?)
4243
}
4344
}

bottlecap/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
pub mod config;
2121
pub mod event_bus;
2222
pub mod events;
23+
pub mod fips;
2324
pub mod http_client;
2425
pub mod lifecycle;
2526
pub mod logger;

bottlecap/src/otlp/agent.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use datadog_trace_agent::http_utils::{
2+
log_and_create_http_response, log_and_create_traces_success_http_response,
3+
};
14
use datadog_trace_utils::send_data::SendData;
25
use datadog_trace_utils::trace_utils::TracerHeaderTags as DatadogTracerHeaderTags;
36
use ddcommon::hyper_migration;
@@ -7,9 +10,6 @@ use std::io;
710
use std::net::SocketAddr;
811
use std::sync::Arc;
912
use tokio::sync::mpsc::Sender;
10-
use trace_agent::http_utils::{
11-
log_and_create_http_response, log_and_create_traces_success_http_response,
12-
};
1313
use tracing::{debug, error};
1414

1515
use crate::{

0 commit comments

Comments
 (0)