diff --git a/Cargo.lock b/Cargo.lock index b3c267fb1b7..f67099b64c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2807,6 +2807,7 @@ dependencies = [ "rand", "rcgen", "reqwest 0.11.27", + "rustls 0.22.4", "secrecy", "serde", "serde_json", @@ -3179,6 +3180,7 @@ dependencies = [ "pretty_assertions", "pyo3", "regex", + "rustls 0.22.4", "rustls-pemfile 2.2.0", "schema", "secrecy", diff --git a/Cargo.toml b/Cargo.toml index 18db9e5abfe..f5364b48530 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,6 +111,7 @@ rand = "0.8.5" rcgen = "0.13.2" regex = "1.11.1" reqwest = { version = "0.11.27", default-features = false, features = ["rustls-tls", "stream", "json"] } +rustls = "0.22.1" rustls-pemfile = "2.2.0" secrecy = "0.8.0" serde = { version = "1.0", features = ["derive"] } diff --git a/influxdb3/Cargo.toml b/influxdb3/Cargo.toml index ab0ab29b55a..ccd6139bdf5 100644 --- a/influxdb3/Cargo.toml +++ b/influxdb3/Cargo.toml @@ -53,6 +53,7 @@ libc.workspace = true num_cpus.workspace = true parking_lot.workspace = true rand.workspace = true +rustls.workspace = true secrecy.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/influxdb3/src/commands/serve.rs b/influxdb3/src/commands/serve.rs index cab6069197f..e074c347901 100644 --- a/influxdb3/src/commands/serve.rs +++ b/influxdb3/src/commands/serve.rs @@ -51,6 +51,10 @@ use object_store::ObjectStore; use observability_deps::tracing::*; use panic_logging::SendPanicsToTracing; use parquet_file::storage::{ParquetStorage, StorageId}; +use rustls::{ + SupportedProtocolVersion, + version::{TLS12, TLS13}, +}; use std::{env, num::NonZeroUsize, sync::Arc, time::Duration}; use std::{path::Path, str::FromStr}; use std::{path::PathBuf, process::Command}; @@ -378,6 +382,44 @@ pub struct Config { #[clap(long = "tls-cert", env = "INFLUXDB3_TLS_CERT")] pub cert_file: Option, + + #[clap( + long = "tls-minimum-version", + env = "INFLUXDB3_TLS_MINIMUM_VERSION", + default_value = "tls-1.2" + )] + pub tls_minimum_version: TlsMinimumVersion, +} + +/// The minimum version of TLS to use for InfluxDB +#[derive(Debug, Clone, Copy, Default)] +pub enum TlsMinimumVersion { + #[default] + Tls1_2, + Tls1_3, +} + +impl FromStr for TlsMinimumVersion { + type Err = String; + + fn from_str(s: &str) -> std::prelude::v1::Result { + match s { + "tls-1.2" => Ok(Self::Tls1_2), + "tls-1.3" => Ok(Self::Tls1_3), + _ => Err("Valid minimum version strings are tls-1.2 and tls-1.3".into()), + } + } +} + +impl From for &'static [&'static SupportedProtocolVersion] { + fn from(val: TlsMinimumVersion) -> Self { + static TLS1_2: &[&SupportedProtocolVersion] = &[&TLS12, &TLS13]; + static TLS1_3: &[&SupportedProtocolVersion] = &[&TLS13]; + match val { + TlsMinimumVersion::Tls1_2 => TLS1_2, + TlsMinimumVersion::Tls1_3 => TLS1_3, + } + } } /// Specified size of the Parquet cache in megabytes (MB) @@ -683,7 +725,9 @@ pub async fn command(config: Config) -> Result<()> { let cert_file = config.cert_file; let key_file = config.key_file; let server = if config.without_auth { - builder.build(cert_file, key_file).await + builder + .build(cert_file, key_file, config.tls_minimum_version.into()) + .await } else { let authentication_provider = Arc::new(TokenAuthenticator::new( Arc::clone(&catalog) as _, @@ -691,7 +735,7 @@ pub async fn command(config: Config) -> Result<()> { )); builder .authorizer(authentication_provider as _) - .build(cert_file, key_file) + .build(cert_file, key_file, config.tls_minimum_version.into()) .await }; diff --git a/influxdb3/src/help/serve.txt b/influxdb3/src/help/serve.txt index 883e61eaacb..a062b5c7d01 100644 --- a/influxdb3/src/help/serve.txt +++ b/influxdb3/src/help/serve.txt @@ -31,6 +31,9 @@ Examples [env: INFLUXDB3_TLS_KEY=] --tls-cert The path to the cert file for TLS to be enabled [env: INFLUXDB3_TLS_CERT=] + --tls-minimum-version The minimum version for TLS. Valid values are + tls-1.2 and tls-1.3, default is tls-1.2 + [env: INFLUXDB3_TLS_MINIMUM_VERSION=] {} --object-store Object storage to use [default: memory] diff --git a/influxdb3/src/help/serve_all.txt b/influxdb3/src/help/serve_all.txt index b7fe23982f1..feb9aaa6140 100644 --- a/influxdb3/src/help/serve_all.txt +++ b/influxdb3/src/help/serve_all.txt @@ -27,6 +27,9 @@ Examples: [env: INFLUXDB3_TLS_KEY=] --tls-cert The path to the cert file for TLS to be enabled [env: INFLUXDB3_TLS_CERT=] + --tls-minimum-version The minimum version for TLS. Valid values are + tls-1.2 and tls-1.3, default is tls-1.2 + [env: INFLUXDB3_TLS_MINIMUM_VERSION=] {} --object-store Object storage to use [default: memory] diff --git a/influxdb3_server/Cargo.toml b/influxdb3_server/Cargo.toml index 0e5d88df41b..ae4656f0b5f 100644 --- a/influxdb3_server/Cargo.toml +++ b/influxdb3_server/Cargo.toml @@ -78,6 +78,7 @@ parking_lot.workspace = true pin-project-lite.workspace = true pyo3.workspace = true regex.workspace = true +rustls.workspace = true rustls-pemfile.workspace = true secrecy.workspace = true serde.workspace = true diff --git a/influxdb3_server/src/builder.rs b/influxdb3_server/src/builder.rs index a5139898d6e..a5e6b70c1fb 100644 --- a/influxdb3_server/src/builder.rs +++ b/influxdb3_server/src/builder.rs @@ -6,6 +6,7 @@ use influxdb3_internal_api::query_executor::QueryExecutor; use influxdb3_processing_engine::ProcessingEngineManagerImpl; use influxdb3_write::{WriteBuffer, persister::Persister}; use iox_time::TimeProvider; +use rustls::SupportedProtocolVersion; use tokio::net::TcpListener; #[derive(Debug)] @@ -202,7 +203,12 @@ impl WithProcessingEngine, > { - pub async fn build(self, cert_file: Option, key_file: Option) -> Server { + pub async fn build<'a>( + self, + cert_file: Option, + key_file: Option, + tls_minimum_version: &'a [&'static SupportedProtocolVersion], + ) -> Server<'a> { let persister = Arc::clone(&self.persister.0); let authorizer = Arc::clone(&self.authorizer); let processing_engine = Arc::clone(&self.processing_engine.0); @@ -230,6 +236,7 @@ impl http, cert_file, key_file, + tls_minimum_version, persister, authorizer, listener: self.listener.0, diff --git a/influxdb3_server/src/lib.rs b/influxdb3_server/src/lib.rs index 4d2de3ad036..f3cb7d52e27 100644 --- a/influxdb3_server/src/lib.rs +++ b/influxdb3_server/src/lib.rs @@ -32,6 +32,8 @@ use influxdb3_telemetry::store::TelemetryStore; use influxdb3_write::persister::Persister; use observability_deps::tracing::error; use observability_deps::tracing::info; +use rustls::ServerConfig; +use rustls::SupportedProtocolVersion; use service::hybrid; use std::convert::Infallible; use std::fmt::Debug; @@ -119,7 +121,7 @@ impl CommonServerState { #[allow(dead_code)] #[derive(Debug)] -pub struct Server { +pub struct Server<'a> { common_state: CommonServerState, http: Arc, persister: Arc, @@ -127,16 +129,17 @@ pub struct Server { listener: TcpListener, key_file: Option, cert_file: Option, + tls_minimum_version: &'a [&'static SupportedProtocolVersion], } -impl Server { +impl Server<'_> { pub fn authorizer(&self) -> Arc { Arc::clone(&self.authorizer.upcast()) } } pub async fn serve( - server: Server, + server: Server<'_>, shutdown: CancellationToken, startup_timer: Instant, without_auth: bool, @@ -194,8 +197,12 @@ pub async fn serve( ); let acceptor = hyper_rustls::TlsAcceptor::builder() - .with_single_cert(certs, key) - .unwrap() + .with_tls_config( + ServerConfig::builder_with_protocol_versions(server.tls_minimum_version) + .with_no_client_auth() + .with_single_cert(certs, key) + .unwrap(), + ) .with_all_versions_alpn() .with_incoming(addr); hyper::server::Server::builder(acceptor) @@ -893,6 +900,11 @@ mod tests { ) .await; + // We declare this as a static so that the lifetimes workout here and that + // it lives long enough. + static TLS_MIN_VERSION: &[&rustls::SupportedProtocolVersion] = + &[&rustls::version::TLS12, &rustls::version::TLS13]; + let server = ServerBuilder::new(common_state) .write_buffer(Arc::clone(&write_buffer)) .query_executor(query_executor) @@ -901,7 +913,7 @@ mod tests { .time_provider(Arc::clone(&time_provider) as _) .tcp_listener(listener) .processing_engine(processing_engine) - .build(None, None) + .build(None, None, TLS_MIN_VERSION) .await; let shutdown = frontend_shutdown.clone();