diff --git a/Cargo.lock b/Cargo.lock index 61b73e8d7f0..fddb6d5404c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2479,6 +2479,23 @@ dependencies = [ "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399c78f9338483cb7e630c8474b07268983c6bd5acee012e4211f9f7bb21b070" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.22.4", + "rustls-native-certs 0.7.3", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", +] + [[package]] name = "hyper-rustls" version = "0.27.5" @@ -3122,6 +3139,7 @@ dependencies = [ "http 0.2.12", "humantime", "hyper 0.14.32", + "hyper-rustls 0.25.0", "influxdb-line-protocol", "influxdb3_authz", "influxdb3_cache", @@ -3159,6 +3177,7 @@ dependencies = [ "pretty_assertions", "pyo3", "regex", + "rustls-pemfile 2.2.0", "schema", "secrecy", "serde", diff --git a/Cargo.toml b/Cargo.toml index 62696c214e3..f1501670f19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ hex = "0.4.3" http = "0.2.9" humantime = "2.1.0" hyper = "0.14" +hyper-rustls = { version = "0.25", features = ["http1", "http2", "ring", "rustls-native-certs"] } insta = { version = "1.39", features = ["json", "redactions", "yaml"] } indexmap = { version = "2.2.6" } itertools = "0.13.0" @@ -109,6 +110,7 @@ pyo3 = { version = "0.24.1", features = ["experimental-async"]} rand = "0.8.5" regex = "1.11.1" reqwest = { version = "0.11.27", default-features = false, features = ["rustls-tls", "stream", "json"] } +rustls-pemfile = "2.2.0" secrecy = "0.8.0" serde = { version = "1.0", features = ["derive"] } # serde_json is set to 1.0.127 to prevent a conflict with core, if that gets updated upstream, this diff --git a/influxdb3/src/commands/create.rs b/influxdb3/src/commands/create.rs index 2cd50f45049..f34ace7fa40 100644 --- a/influxdb3/src/commands/create.rs +++ b/influxdb3/src/commands/create.rs @@ -13,6 +13,7 @@ use secrecy::ExposeSecret; use secrecy::Secret; use std::error::Error; use std::num::NonZeroUsize; +use std::path::PathBuf; use std::str; use token::AdminTokenConfig; use token::TokenCommands; @@ -27,13 +28,15 @@ pub struct Config { impl Config { fn get_client(&self) -> Result> { - let (host_url, auth_token) = match &self.cmd { + let (host_url, auth_token, ca_cert) = match &self.cmd { SubCommand::Database(DatabaseConfig { host_url, auth_token, + ca_cert, .. }) | SubCommand::LastCache(LastCacheConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -43,6 +46,7 @@ impl Config { .. }) | SubCommand::DistinctCache(DistinctCacheConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -52,6 +56,7 @@ impl Config { .. }) | SubCommand::Table(TableConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -61,6 +66,7 @@ impl Config { .. }) | SubCommand::Trigger(TriggerConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -68,20 +74,21 @@ impl Config { .. }, .. - }) => (host_url, auth_token), + }) => (host_url, auth_token, ca_cert), SubCommand::Token(token_commands) => { - let (host_url, auth_token) = match &token_commands.commands { + let (host_url, auth_token, ca_cert) = match &token_commands.commands { token::TokenSubCommand::Admin(AdminTokenConfig { host_url, auth_token, + ca_cert, .. - }) => (host_url, auth_token), + }) => (host_url, auth_token, ca_cert), }; - (host_url, auth_token) + (host_url, auth_token, ca_cert) } }; - let mut client = Client::new(host_url.clone())?; + let mut client = Client::new(host_url.clone(), ca_cert.clone())?; if let Some(token) = &auth_token { client = client.with_auth_token(token.expose_secret()); } @@ -126,6 +133,10 @@ pub struct DatabaseConfig { /// alphanumeric with - and _ allowed and starts with a letter or number #[clap(env = "INFLUXDB3_DATABASE_NAME", required = true)] pub database_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Args)] @@ -164,6 +175,10 @@ pub struct LastCacheConfig { /// Give a name for the cache. #[clap(required = false)] cache_name: Option, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Args)] @@ -197,6 +212,10 @@ pub struct DistinctCacheConfig { /// This will be automatically generated if not provided #[clap(required = false)] cache_name: Option, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Args)] @@ -216,6 +235,10 @@ pub struct TableConfig { #[clap(required = true)] /// The name of the table to be created table_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Parser)] @@ -246,6 +269,10 @@ pub struct TriggerConfig { error_behavior: ErrorBehavior, /// Name for the new trigger trigger_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } pub async fn command(config: Config) -> Result<(), Box> { @@ -264,6 +291,7 @@ pub async fn command(config: Config) -> Result<(), Box> { value_columns, count, ttl, + .. }) => { let mut b = client.api_v3_configure_last_cache_create(database_name, table); @@ -301,6 +329,7 @@ pub async fn command(config: Config) -> Result<(), Box> { columns, max_cardinality, max_age, + .. }) => { let mut b = client.api_v3_configure_distinct_cache_create(database_name, table, columns); @@ -330,6 +359,7 @@ pub async fn command(config: Config) -> Result<(), Box> { table_name, tags, fields, + .. }) => { client .api_v3_configure_table_create(&database_name, &table_name, tags, fields) @@ -371,6 +401,7 @@ pub async fn command(config: Config) -> Result<(), Box> { disabled, run_asynchronous, error_behavior, + .. }) => { let trigger_arguments: Option> = trigger_arguments.map(|a| { a.into_iter() @@ -441,6 +472,7 @@ mod tests { count, ttl, influxdb3_config: crate::commands::common::InfluxDb3Config { database_name, .. }, + .. }) = args.cmd else { panic!("Did not parse args correctly: {args:#?}") @@ -478,6 +510,7 @@ mod tests { run_asynchronous, error_behavior, influxdb3_config: crate::commands::common::InfluxDb3Config { database_name, .. }, + .. }) = args.cmd else { panic!("Did not parse args correctly: {args:#?}") diff --git a/influxdb3/src/commands/create/token.rs b/influxdb3/src/commands/create/token.rs index 1121e7f5fd0..5285264165a 100644 --- a/influxdb3/src/commands/create/token.rs +++ b/influxdb3/src/commands/create/token.rs @@ -1,4 +1,4 @@ -use std::error::Error; +use std::{error::Error, path::PathBuf}; use clap::Parser; use influxdb3_client::Client; @@ -36,6 +36,10 @@ pub struct AdminTokenConfig { /// The token for authentication with the InfluxDB 3 Enterprise server #[clap(long = "token", env = "INFLUXDB3_AUTH_TOKEN")] pub auth_token: Option>, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + pub ca_cert: Option, } pub(crate) async fn handle_token_creation( diff --git a/influxdb3/src/commands/delete.rs b/influxdb3/src/commands/delete.rs index 68c04158bd3..513aed9a8c6 100644 --- a/influxdb3/src/commands/delete.rs +++ b/influxdb3/src/commands/delete.rs @@ -4,6 +4,7 @@ use secrecy::ExposeSecret; use secrecy::Secret; use std::error::Error; use std::io; +use std::path::PathBuf; use url::Url; #[derive(Debug, clap::Parser)] @@ -18,9 +19,11 @@ impl Config { SubCommand::Database(DatabaseConfig { host_url, auth_token, + ca_cert, .. }) | SubCommand::LastCache(LastCacheConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -30,6 +33,7 @@ impl Config { .. }) | SubCommand::DistinctCache(DistinctCacheConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -39,6 +43,7 @@ impl Config { .. }) | SubCommand::Table(TableConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -48,6 +53,7 @@ impl Config { .. }) | SubCommand::Trigger(TriggerConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -56,7 +62,7 @@ impl Config { }, .. }) => { - let mut client = Client::new(host_url.clone())?; + let mut client = Client::new(host_url.clone(), ca_cert.clone())?; if let Some(token) = &auth_token { client = client.with_auth_token(token.expose_secret()); } @@ -100,6 +106,10 @@ pub struct DatabaseConfig { /// The name of the database to be deleted #[clap(env = "INFLUXDB3_DATABASE_NAME", required = true)] pub database_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Args)] @@ -114,6 +124,10 @@ pub struct LastCacheConfig { /// The name of the cache being deleted #[clap(required = true)] cache_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Args)] @@ -128,6 +142,10 @@ pub struct DistinctCacheConfig { /// The name of the cache being deleted #[clap(required = true)] cache_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Args)] @@ -137,6 +155,10 @@ pub struct TableConfig { #[clap(required = true)] /// The name of the table to be deleted table_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, clap::Parser)] @@ -151,6 +173,10 @@ pub struct TriggerConfig { /// Name of trigger to delete #[clap(required = true)] trigger_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } pub async fn command(config: Config) -> Result<(), Box> { @@ -175,6 +201,7 @@ pub async fn command(config: Config) -> Result<(), Box> { influxdb3_config: InfluxDb3Config { database_name, .. }, table, cache_name, + .. }) => { client .api_v3_configure_last_cache_delete(database_name, table, cache_name) @@ -186,6 +213,7 @@ pub async fn command(config: Config) -> Result<(), Box> { influxdb3_config: InfluxDb3Config { database_name, .. }, table, cache_name, + .. }) => { client .api_v3_configure_distinct_cache_delete(database_name, table, cache_name) @@ -196,6 +224,7 @@ pub async fn command(config: Config) -> Result<(), Box> { SubCommand::Table(TableConfig { influxdb3_config: InfluxDb3Config { database_name, .. }, table_name, + .. }) => { println!( "Are you sure you want to delete {:?}.{:?}? Enter 'yes' to confirm", @@ -220,6 +249,7 @@ pub async fn command(config: Config) -> Result<(), Box> { influxdb3_config: InfluxDb3Config { database_name, .. }, trigger_name, force, + .. }) => { client .api_v3_configure_processing_engine_trigger_delete( diff --git a/influxdb3/src/commands/disable.rs b/influxdb3/src/commands/disable.rs index 1759a0948a9..610324c6f29 100644 --- a/influxdb3/src/commands/disable.rs +++ b/influxdb3/src/commands/disable.rs @@ -1,7 +1,7 @@ use crate::commands::common::InfluxDb3Config; use influxdb3_client::Client; use secrecy::ExposeSecret; -use std::error::Error; +use std::{error::Error, path::PathBuf}; #[derive(Debug, clap::Parser)] pub struct Config { @@ -11,8 +11,9 @@ pub struct Config { impl Config { fn get_client(&self) -> Result> { - let (host_url, auth_token) = match &self.cmd { + let (host_url, auth_token, ca_cert) = match &self.cmd { SubCommand::Trigger(TriggerConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -20,9 +21,9 @@ impl Config { .. }, .. - }) => (host_url, auth_token), + }) => (host_url, auth_token, ca_cert), }; - let mut client = Client::new(host_url.clone())?; + let mut client = Client::new(host_url.clone(), ca_cert.clone())?; if let Some(token) = &auth_token { client = client.with_auth_token(token.expose_secret()); } @@ -44,6 +45,10 @@ struct TriggerConfig { /// Name of trigger to disable #[clap(required = true)] trigger_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + pub ca_cert: Option, } pub async fn command(config: Config) -> Result<(), Box> { @@ -52,6 +57,7 @@ pub async fn command(config: Config) -> Result<(), Box> { SubCommand::Trigger(TriggerConfig { influxdb3_config: InfluxDb3Config { database_name, .. }, trigger_name, + .. }) => { client .api_v3_configure_processing_engine_trigger_disable(database_name, &trigger_name) diff --git a/influxdb3/src/commands/enable.rs b/influxdb3/src/commands/enable.rs index aab0d7ab31c..140241ac0c6 100644 --- a/influxdb3/src/commands/enable.rs +++ b/influxdb3/src/commands/enable.rs @@ -1,7 +1,7 @@ use crate::commands::common::InfluxDb3Config; use influxdb3_client::Client; use secrecy::ExposeSecret; -use std::error::Error; +use std::{error::Error, path::PathBuf}; #[derive(Debug, clap::Parser)] pub struct Config { @@ -11,8 +11,9 @@ pub struct Config { impl Config { fn get_client(&self) -> Result> { - let (host_url, auth_token) = match &self.cmd { + let (host_url, auth_token, ca_cert) = match &self.cmd { SubCommand::Trigger(TriggerConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -20,9 +21,9 @@ impl Config { .. }, .. - }) => (host_url, auth_token), + }) => (host_url, auth_token, ca_cert), }; - let mut client = Client::new(host_url.clone())?; + let mut client = Client::new(host_url.clone(), ca_cert.clone())?; if let Some(token) = &auth_token { client = client.with_auth_token(token.expose_secret()); } @@ -44,6 +45,10 @@ struct TriggerConfig { /// Name of trigger to enable #[clap(required = true)] trigger_name: String, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + pub ca_cert: Option, } pub async fn command(config: Config) -> Result<(), Box> { @@ -52,6 +57,7 @@ pub async fn command(config: Config) -> Result<(), Box> { SubCommand::Trigger(TriggerConfig { influxdb3_config: InfluxDb3Config { database_name, .. }, trigger_name, + .. }) => { client .api_v3_configure_processing_engine_trigger_enable(database_name, &trigger_name) diff --git a/influxdb3/src/commands/install.rs b/influxdb3/src/commands/install.rs index 3704307206c..d09335415a8 100644 --- a/influxdb3/src/commands/install.rs +++ b/influxdb3/src/commands/install.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use influxdb3_clap_blocks::plugins::ProcessingEngineConfig; use influxdb3_client::Client; use secrecy::{ExposeSecret, Secret}; @@ -49,11 +51,15 @@ pub struct PackageConfig { /// Package names to install #[arg(required_unless_present = "requirements")] packages: Vec, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } impl PackageConfig { async fn run_command(&self) -> Result<(), anyhow::Error> { - let mut client = Client::new(self.host_url.clone())?; + let mut client = Client::new(self.host_url.clone(), self.ca_cert.clone())?; if let Some(token) = &self.auth_token { client = client.with_auth_token(token.expose_secret()); } diff --git a/influxdb3/src/commands/query.rs b/influxdb3/src/commands/query.rs index 9fd635cdf36..ad6261e7969 100644 --- a/influxdb3/src/commands/query.rs +++ b/influxdb3/src/commands/query.rs @@ -1,3 +1,4 @@ +use std::path::PathBuf; use std::str::Utf8Error; use clap::{Parser, ValueEnum}; @@ -71,6 +72,10 @@ pub struct Config { /// The query string to execute query: Option, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, ValueEnum, Clone)] @@ -85,7 +90,7 @@ pub(crate) async fn command(config: Config) -> Result<()> { database_name, auth_token, } = config.influxdb3_config; - let mut client = influxdb3_client::Client::new(host_url)?; + let mut client = influxdb3_client::Client::new(host_url, config.ca_cert)?; if let Some(t) = auth_token { client = client.with_auth_token(t.expose_secret()); } diff --git a/influxdb3/src/commands/serve.rs b/influxdb3/src/commands/serve.rs index 3c4e67ffe0c..47b97c6e04c 100644 --- a/influxdb3/src/commands/serve.rs +++ b/influxdb3/src/commands/serve.rs @@ -51,9 +51,9 @@ use object_store::ObjectStore; use observability_deps::tracing::*; use panic_logging::SendPanicsToTracing; use parquet_file::storage::{ParquetStorage, StorageId}; -use std::process::Command; use std::{env, num::NonZeroUsize, sync::Arc, time::Duration}; use std::{path::Path, str::FromStr}; +use std::{path::PathBuf, process::Command}; use thiserror::Error; use tokio::net::TcpListener; use tokio::time::Instant; @@ -113,6 +113,9 @@ pub enum Error { #[error("lost HTTP/gRPC service")] LostHttpGrpc, + + #[error("tls requires both a cert and a key file to be passed in to work")] + NoCertOrKeyFile, } pub type Result = std::result::Result; @@ -369,6 +372,12 @@ pub struct Config { /// smaller time ranges if possible in a query. #[clap(long = "query-file-limit", env = "INFLUXDB3_QUERY_FILE_LIMIT", action)] pub query_file_limit: Option, + + #[clap(long = "tls-key")] + pub key_file: Option, + + #[clap(long = "tls-cert")] + pub cert_file: Option, } /// Specified size of the Parquet cache in megabytes (MB) @@ -436,6 +445,14 @@ fn ensure_directory_exists(p: &Path) { } pub async fn command(config: Config) -> Result<()> { + // Check that both a cert file and key file are present if TLS is being set up + match (&config.cert_file, &config.key_file) { + (Some(_), None) | (None, Some(_)) => { + return Err(Error::NoCertOrKeyFile); + } + (Some(_), Some(_)) | (None, None) => {} + } + let startup_timer = Instant::now(); let num_cpus = num_cpus::get(); let build_malloc_conf = build_malloc_conf(); @@ -661,6 +678,8 @@ pub async fn command(config: Config) -> Result<()> { .processing_engine(processing_engine); // We can ignore the token passed in for now, as the token is supposed to be in catalog + let cert_file = config.cert_file; + let key_file = config.key_file; let server = if let Some(_token) = config.bearer_token { let authentication_provider = Arc::new(TokenAuthenticator::new( Arc::clone(&catalog) as _, @@ -668,10 +687,10 @@ pub async fn command(config: Config) -> Result<()> { )); builder .authorizer(authentication_provider as _) - .build() + .build(cert_file, key_file) .await } else { - builder.build().await + builder.build(cert_file, key_file).await }; // There are two different select! macros - tokio::select and futures::select diff --git a/influxdb3/src/commands/show.rs b/influxdb3/src/commands/show.rs index 93a95a0ed57..86b670a1c63 100644 --- a/influxdb3/src/commands/show.rs +++ b/influxdb3/src/commands/show.rs @@ -1,6 +1,6 @@ use clap::Parser; use secrecy::{ExposeSecret, Secret}; -use std::error::Error; +use std::{error::Error, path::PathBuf}; use url::Url; use crate::commands::common::Format; @@ -44,6 +44,10 @@ pub struct ShowTokensConfig { /// The format in which to output the list of databases #[clap(value_enum, long = "format", default_value = "pretty")] output_format: Format, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } #[derive(Debug, Parser)] @@ -68,6 +72,10 @@ pub struct DatabaseConfig { /// The format in which to output the list of databases #[clap(value_enum, long = "format", default_value = "pretty")] output_format: Format, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } pub(crate) async fn command(config: Config) -> Result<(), Box> { @@ -77,8 +85,9 @@ pub(crate) async fn command(config: Config) -> Result<(), Box> { auth_token, show_deleted, output_format, + ca_cert, }) => { - let mut client = influxdb3_client::Client::new(host_url)?; + let mut client = influxdb3_client::Client::new(host_url, ca_cert)?; if let Some(t) = auth_token { client = client.with_auth_token(t.expose_secret()); @@ -95,7 +104,10 @@ pub(crate) async fn command(config: Config) -> Result<(), Box> { } SubCommand::System(cfg) => system::command(cfg).await?, SubCommand::Tokens(show_tokens_config) => { - let mut client = influxdb3_client::Client::new(show_tokens_config.host_url.clone())?; + let mut client = influxdb3_client::Client::new( + show_tokens_config.host_url.clone(), + show_tokens_config.ca_cert, + )?; if let Some(t) = show_tokens_config.auth_token { client = client.with_auth_token(t.expose_secret()); diff --git a/influxdb3/src/commands/show/system.rs b/influxdb3/src/commands/show/system.rs index 7d0d6cce701..eb72a97aa8a 100644 --- a/influxdb3/src/commands/show/system.rs +++ b/influxdb3/src/commands/show/system.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Parser; use influxdb3_client::Client; use secrecy::ExposeSecret; @@ -41,7 +43,15 @@ pub enum SubCommand { } pub async fn command(config: SystemConfig) -> Result<()> { - let mut client = Client::new(config.core_config.host_url.clone())?; + let mut client = Client::new( + config.core_config.host_url.clone(), + match config.subcommand { + SubCommand::TableList(TableListConfig { ref ca_cert, .. }) => ca_cert, + SubCommand::Table(TableConfig { ref ca_cert, .. }) => ca_cert, + SubCommand::Summary(SummaryConfig { ref ca_cert, .. }) => ca_cert, + } + .clone(), + )?; if let Some(token) = config .core_config .auth_token @@ -77,6 +87,10 @@ pub struct TableListConfig { /// The format in which to output the query #[clap(value_enum, long = "format", default_value = "pretty")] output_format: Format, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } const SYS_TABLES_QUERY: &str = "WITH cols (table_name, column_name) AS (SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = 'system' ORDER BY (table_name, column_name)) SELECT table_name, array_agg(column_name) AS column_names FROM cols GROUP BY table_name ORDER BY table_name"; @@ -134,6 +148,10 @@ pub struct TableConfig { /// The format in which to output the query #[clap(value_enum, long = "format", default_value = "pretty")] output_format: Format, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } impl SystemCommandRunner { @@ -157,6 +175,7 @@ impl SystemCommandRunner { select, order_by, output_format, + .. } = &config; let select_expr = if !select.is_empty() { @@ -221,6 +240,10 @@ pub struct SummaryConfig { /// The format in which to output the query #[clap(value_enum, long = "format", default_value = "pretty")] output_format: Format, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } impl SystemCommandRunner { diff --git a/influxdb3/src/commands/test.rs b/influxdb3/src/commands/test.rs index 5923915440f..ea10b611bc1 100644 --- a/influxdb3/src/commands/test.rs +++ b/influxdb3/src/commands/test.rs @@ -4,7 +4,7 @@ use hashbrown::HashMap; use influxdb3_client::Client; use influxdb3_types::http::{SchedulePluginTestRequest, WalPluginTestRequest}; use secrecy::ExposeSecret; -use std::error::Error; +use std::{error::Error, path::PathBuf}; #[derive(Debug, clap::Parser)] pub struct Config { @@ -16,6 +16,7 @@ impl Config { fn get_client(&self) -> Result> { match &self.cmd { SubCommand::WalPlugin(WalPluginConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -25,6 +26,7 @@ impl Config { .. }) | SubCommand::SchedulePlugin(SchedulePluginConfig { + ca_cert, influxdb3_config: InfluxDb3Config { host_url, @@ -33,7 +35,7 @@ impl Config { }, .. }) => { - let mut client = Client::new(host_url.clone())?; + let mut client = Client::new(host_url.clone(), ca_cert.clone())?; if let Some(token) = &auth_token { client = client.with_auth_token(token.expose_secret()); } @@ -72,6 +74,9 @@ pub struct WalPluginConfig { pub filename: String, #[clap(long = "cache-name")] pub cache_name: Option, + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + pub ca_cert: Option, } #[derive(Debug, clap::Parser)] @@ -90,6 +95,9 @@ pub struct SchedulePluginConfig { pub schedule: Option, #[clap(long = "cache-name")] pub cache_name: Option, + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + pub ca_cert: Option, } pub async fn command(config: Config) -> Result<(), Box> { diff --git a/influxdb3/src/commands/write.rs b/influxdb3/src/commands/write.rs index 87ad5b1fc2b..bbead120612 100644 --- a/influxdb3/src/commands/write.rs +++ b/influxdb3/src/commands/write.rs @@ -1,6 +1,7 @@ use std::{ fs, io::{BufReader, IsTerminal, Read, stdin}, + path::PathBuf, }; use clap::Parser; @@ -58,6 +59,10 @@ pub struct Config { /// Specify a supported precision (eg: ns, us, ms, s). #[clap(short = 'p', long = "precision")] precision: Option, + + /// An optional arg to use a custom ca for useful for testing with self signed certs + #[clap(long = "tls-ca")] + ca_cert: Option, } pub(crate) async fn command(config: Config) -> Result<()> { @@ -66,7 +71,7 @@ pub(crate) async fn command(config: Config) -> Result<()> { database_name, auth_token, } = config.influxdb3_config; - let mut client = influxdb3_client::Client::new(host_url)?; + let mut client = influxdb3_client::Client::new(host_url, config.ca_cert)?; if let Some(t) = auth_token { client = client.with_auth_token(t.expose_secret()); } diff --git a/influxdb3/tests/cli/api.rs b/influxdb3/tests/cli/api.rs index 76912049799..e531bb0bf85 100644 --- a/influxdb3/tests/cli/api.rs +++ b/influxdb3/tests/cli/api.rs @@ -73,8 +73,15 @@ impl TestServer { } impl CreateDatabaseQuery<'_> { pub fn run(self) -> Result { - self.server - .run(vec!["create", "database"], &[self.name.as_str()]) + self.server.run( + vec![ + "create", + "database", + "--tls-ca", + "../testing-certs/rootCA.pem", + ], + &[self.name.as_str()], + ) } } // Builder for the 'create table' command @@ -147,6 +154,8 @@ impl CreateTableQuery<'_> { &self.table_name, "--tags", &tags_arg, + "--tls-ca", + "../testing-certs/rootCA.pem", ]; if !self.fields.is_empty() { @@ -198,6 +207,9 @@ impl ShowDatabasesQuery<'_> { args.push("--show-deleted"); } + args.push("--tls-ca"); + args.push("../testing-certs/rootCA.pem"); + self.server.run(vec!["show", "databases"], &args) } } @@ -219,8 +231,15 @@ impl TestServer { impl DeleteDatabaseQuery<'_> { pub fn run(self) -> Result { - self.server - .run_with_confirmation(vec!["delete", "database"], &[self.name.as_str()]) + self.server.run_with_confirmation( + vec![ + "delete", + "database", + "--tls-ca", + "../testing-certs/rootCA.pem", + ], + &[self.name.as_str()], + ) } } @@ -264,7 +283,14 @@ impl QuerySqlQuery<'_> { } pub fn run(self) -> Result { - let mut args = vec!["--database", &self.database, "--format", "json"]; + let mut args = vec![ + "--database", + &self.database, + "--format", + "json", + "--tls-ca", + "../testing-certs/rootCA.pem", + ]; if let Some(file_path) = self.output_file.as_ref() { args.push("--output"); args.push(file_path); @@ -321,6 +347,8 @@ impl DeleteTableQuery<'_> { self.table_name.as_str(), "--database", self.db_name.as_str(), + "--tls-ca", + "../testing-certs/rootCA.pem", ]; self.server @@ -393,6 +421,8 @@ impl CreateDistinctCacheQuery<'_> { self.table_name.as_str(), "--columns", &columns_arg, + "--tls-ca", + "../testing-certs/rootCA.pem", ]; let max_cardinality = self.max_cardinality.unwrap_or_default(); let max_cardinality_str = max_cardinality.to_string(); @@ -445,6 +475,8 @@ impl DeleteDistinctCacheQuery<'_> { "--table", self.table_name.as_str(), self.cache_name.as_str(), + "--tls-ca", + "../testing-certs/rootCA.pem", ]; self.server @@ -524,6 +556,8 @@ impl CreateTriggerQuery<'_> { self.plugin_filename.as_str(), "--trigger-spec", self.trigger_spec.as_str(), + "--tls-ca", + "../testing-certs/rootCA.pem", ]; let trigger_args = self.trigger_arguments.join(","); @@ -576,6 +610,8 @@ impl EnableTriggerQuery<'_> { self.trigger_name.as_str(), "--database", self.db_name.as_str(), + "--tls-ca", + "../testing-certs/rootCA.pem", ]; self.server.run(vec!["enable", "trigger"], args.as_slice()) @@ -609,6 +645,8 @@ impl DisableTriggerQuery<'_> { self.trigger_name.as_str(), "--database", self.db_name.as_str(), + "--tls-ca", + "../testing-certs/rootCA.pem", ]; self.server.run(vec!["disable", "trigger"], args.as_slice()) @@ -648,6 +686,8 @@ impl DeleteTriggerQuery<'_> { self.trigger_name.as_str(), "--database", self.db_name.as_str(), + "--tls-ca", + "../testing-certs/rootCA.pem", ]; if self.force { @@ -730,7 +770,8 @@ impl TestWalPluginQuery<'_> { } args.push(&self.plugin_filename); - + args.push("--tls-ca"); + args.push("../testing-certs/rootCA.pem"); let output = self.server.run( vec!["test", "wal_plugin"], &args.iter().map(AsRef::as_ref).collect::>(), @@ -796,6 +837,8 @@ impl TestSchedulePluginQuery<'_> { self.db_name.as_str(), "--schedule", self.schedule.as_str(), + "--tls-ca", + "../testing-certs/rootCA.pem", ]; if let Some(cache_name) = &self.cache_name { @@ -884,6 +927,8 @@ impl InstallPackageQuery<'_> { args.push("--package-manager"); args.push(pkg_mgr); } + args.push("--tls-ca"); + args.push("../testing-certs/rootCA.pem"); // Run the command self.server.run(vec!["package", "install"], &args) @@ -927,7 +972,12 @@ impl WriteQuery<'_> { } pub fn run(self) -> Result { - let mut args = vec!["--database", &self.db_name]; + let mut args = vec![ + "--database", + &self.db_name, + "--tls-ca", + "../testing-certs/rootCA.pem", + ]; if let Some(precision) = &self.precision { args.push("--precision"); @@ -951,7 +1001,12 @@ impl WriteQuery<'_> { } pub fn run_with_stdin(self, stdin_input: impl Into) -> Result { - let mut args = vec!["--database", &self.db_name]; + let mut args = vec![ + "--database", + &self.db_name, + "--tls-ca", + "../testing-certs/rootCA.pem", + ]; if let Some(precision) = &self.precision { args.push("--precision"); @@ -1037,7 +1092,13 @@ impl<'a> ShowSystemQuery<'a> { impl ShowSystemTableListQuery<'_> { // Run the table-list command pub fn run(self) -> Result { - let mut args = vec!["--database", &self.base.db_name, "table-list"]; + let mut args = vec![ + "--database", + &self.base.db_name, + "table-list", + "--tls-ca", + "../testing-certs/rootCA.pem", + ]; if let Some(format) = &self.base.format { args.push("--format"); @@ -1069,7 +1130,13 @@ impl ShowSystemTableQuery<'_> { // Run the table command pub fn run(self) -> Result { - let mut args = vec!["--database", &self.base.db_name, "table"]; + let mut args = vec![ + "--database", + &self.base.db_name, + "table", + "--tls-ca", + "../testing-certs/rootCA.pem", + ]; if let Some(format) = &self.base.format { args.push("--format"); @@ -1109,7 +1176,13 @@ impl ShowSystemSummaryQuery<'_> { // Run the summary command pub fn run(self) -> Result { - let mut args = vec!["--database", &self.base.db_name, "summary"]; + let mut args = vec![ + "--database", + &self.base.db_name, + "summary", + "--tls-ca", + "../testing-certs/rootCA.pem", + ]; if let Some(format) = &self.base.format { args.push("--format"); diff --git a/influxdb3/tests/cli/mod.rs b/influxdb3/tests/cli/mod.rs index 60563e09593..ea3a4e3e6e3 100644 --- a/influxdb3/tests/cli/mod.rs +++ b/influxdb3/tests/cli/mod.rs @@ -890,7 +890,10 @@ async fn test_show_system() { // Test failure cases // 1. Missing database (this can't be tested with the fluent API since we always require a db name) let output = server - .run(vec!["show", "system"], &["table-list"]) + .run( + vec!["show", "system"], + &["table-list", "--tls-ca", "../testing-certs/rootCA.pem"], + ) .unwrap_err() .to_string(); insta::assert_snapshot!("fail_without_database_name", output); @@ -1582,7 +1585,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ assert_contains!(&result, "Trigger foo created successfully"); // send an HTTP request to the server - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .post(format!("{}/api/v3/engine/bar", server.client_addr())) .header("Content-Type", "application/json") @@ -1671,7 +1674,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .expect("Failed to create trigger"); // Send request to test string response - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -1715,7 +1718,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .expect("Failed to create trigger"); // Send request to test dict/JSON response - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -1765,7 +1768,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .expect("Failed to create trigger"); // Send request to test tuple with status response - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -1809,7 +1812,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .expect("Failed to create trigger"); // Send request to test tuple with headers response - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -1860,7 +1863,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .expect("Failed to create trigger"); // Send request to test tuple with status and headers response - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -1908,7 +1911,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .expect("Failed to create trigger"); // Send request to test list/JSON response - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -1960,7 +1963,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .expect("Failed to create trigger"); // Send request to test iterator/generator response - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -2018,7 +2021,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .unwrap(); // Send request to test Flask Response object - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -2066,7 +2069,7 @@ def process_request(influxdb3_local, query_parameters, request_headers, request_ .unwrap(); // Send request to test JSON dict with status - let client = reqwest::Client::new(); + let client = server.http_client(); let response = client .get(format!("{}/api/v3/engine/test_route", server.client_addr())) .send() @@ -2868,7 +2871,7 @@ async fn test_wal_overwritten() { #[test_log::test(tokio::test)] async fn test_create_admin_token() { let server = TestServer::spawn().await; - let args = &[]; + let args = &["--tls-ca", "../testing-certs/rootCA.pem"]; let result = server .run(vec!["create", "token", "--admin"], args) .unwrap(); @@ -2882,7 +2885,7 @@ async fn test_create_admin_token() { #[test_log::test(tokio::test)] async fn test_create_admin_token_allowed_once() { let server = TestServer::spawn().await; - let args = &[]; + let args = &["--tls-ca", "../testing-certs/rootCA.pem"]; let result = server .run(vec!["create", "token", "--admin"], args) .unwrap(); @@ -2904,7 +2907,7 @@ async fn test_create_admin_token_allowed_once() { async fn test_regenerate_admin_token() { // when created with_auth, TestServer spins up server and generates admin token. let mut server = TestServer::configure().with_auth().spawn().await; - let args = &[]; + let args = &["--tls-ca", "../testing-certs/rootCA.pem"]; let result = server .run(vec!["create", "token", "--admin"], args) .unwrap(); @@ -2916,7 +2919,10 @@ async fn test_regenerate_admin_token() { // regenerating token is allowed let result = server - .run(vec!["create", "token", "--admin"], &["--regenerate"]) + .run( + vec!["create", "token", "--admin"], + &["--regenerate", "--tls-ca", "../testing-certs/rootCA.pem"], + ) .unwrap(); assert_contains!( &result, diff --git a/influxdb3/tests/server/auth.rs b/influxdb3/tests/server/auth.rs index 7048d5489c4..9c35830b957 100644 --- a/influxdb3/tests/server/auth.rs +++ b/influxdb3/tests/server/auth.rs @@ -13,7 +13,7 @@ async fn auth_http() { .clone() .expect("admin token to have been present"); - let client = reqwest::Client::new(); + let client = server.http_client(); let base = server.client_addr(); let write_lp_url = format!("{base}/api/v3/write_lp"); let write_lp_params = [("db", "foo")]; @@ -224,7 +224,7 @@ async fn v1_password_parameter() { .clone() .expect("admin token to have been present"); - let client = reqwest::Client::new(); + let client = server.http_client(); let query_url = format!("{base}/query", base = server.client_addr()); let write_url = format!("{base}/write", base = server.client_addr()); // Send requests without any authentication: diff --git a/influxdb3/tests/server/client.rs b/influxdb3/tests/server/client.rs index 891fb5c103f..b4801e2ac48 100644 --- a/influxdb3/tests/server/client.rs +++ b/influxdb3/tests/server/client.rs @@ -12,7 +12,11 @@ async fn write_and_query() { let server = TestServer::spawn().await; let db_name = "foo"; let tbl_name = "bar"; - let client = influxdb3_client::Client::new(server.client_addr()).unwrap(); + let client = influxdb3_client::Client::new( + server.client_addr(), + Some("../testing-certs/rootCA.pem".into()), + ) + .unwrap(); client .api_v3_write_lp(db_name) .precision(Precision::Nanosecond) @@ -40,7 +44,11 @@ async fn configure_last_caches() { let server = TestServer::spawn().await; let db_name = "foo"; let tbl_name = "bar"; - let client = influxdb3_client::Client::new(server.client_addr()).unwrap(); + let client = influxdb3_client::Client::new( + server.client_addr(), + Some("../testing-certs/rootCA.pem".into()), + ) + .unwrap(); client .api_v3_write_lp(db_name) .precision(Precision::Nanosecond) diff --git a/influxdb3/tests/server/configure.rs b/influxdb3/tests/server/configure.rs index a52c3e3086c..1ef97566a7e 100644 --- a/influxdb3/tests/server/configure.rs +++ b/influxdb3/tests/server/configure.rs @@ -9,7 +9,7 @@ use crate::server::TestServer; #[tokio::test] async fn api_v3_configure_distinct_cache_create() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/distinct_cache", base = server.client_addr() @@ -174,7 +174,7 @@ async fn api_v3_configure_distinct_cache_create() { #[tokio::test] async fn api_v3_configure_distinct_cache_delete() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/distinct_cache", base = server.client_addr() @@ -388,7 +388,7 @@ async fn api_v3_configure_distinct_cache_delete() { #[tokio::test] async fn api_v3_configure_last_cache_create() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/last_cache", base = server.client_addr() @@ -567,7 +567,7 @@ async fn api_v3_configure_last_cache_create() { #[tokio::test] async fn api_v3_configure_last_cache_delete() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/last_cache", base = server.client_addr() @@ -760,7 +760,7 @@ async fn api_v3_configure_db_delete() { let db_name = "foo"; let tbl_name = "tbl"; let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/database?db={db_name}", base = server.client_addr() @@ -883,7 +883,7 @@ async fn api_v3_configure_db_delete() { async fn api_v3_configure_db_delete_no_db() { let db_name = "db"; let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/database?db={db_name}", base = server.client_addr() @@ -900,7 +900,7 @@ async fn api_v3_configure_db_delete_no_db() { #[tokio::test] async fn api_v3_configure_db_delete_missing_query_param() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/database", base = server.client_addr() @@ -917,7 +917,7 @@ async fn api_v3_configure_db_delete_missing_query_param() { #[test_log::test(tokio::test)] async fn api_v3_configure_db_create() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/database", base = server.client_addr() @@ -935,7 +935,7 @@ async fn api_v3_configure_db_create() { #[test_log::test(tokio::test)] async fn api_v3_configure_db_create_db_with_same_name() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/database", base = server.client_addr() @@ -961,7 +961,7 @@ async fn api_v3_configure_db_create_db_with_same_name() { #[test_log::test(tokio::test)] async fn api_v3_configure_db_create_db_hit_limit() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/database", base = server.client_addr() @@ -988,7 +988,7 @@ async fn api_v3_configure_db_create_db_hit_limit() { #[test_log::test(tokio::test)] async fn api_v3_configure_db_create_db_reuse_old_name() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/database", base = server.client_addr() @@ -1018,7 +1018,7 @@ async fn api_v3_configure_db_create_db_reuse_old_name() { #[test_log::test(tokio::test)] async fn api_v3_configure_table_create_then_write() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let db_url = format!( "{base}/api/v3/configure/database", base = server.client_addr() @@ -1104,7 +1104,7 @@ async fn api_v3_configure_table_create_then_write() { #[test_log::test(tokio::test)] async fn api_v3_configure_table_create_no_fields() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let db_url = format!( "{base}/api/v3/configure/database", base = server.client_addr() @@ -1164,7 +1164,7 @@ async fn api_v3_configure_table_create_no_fields() { #[test_log::test(tokio::test)] async fn api_v3_configure_table_create_invalid_field_types() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let table_url = format!("{base}/api/v3/configure/table", base = server.client_addr()); let resp = client @@ -1192,7 +1192,7 @@ async fn api_v3_configure_table_delete() { let db_name = "foo"; let tbl_name = "tbl"; let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/table?db={db_name}&table={tbl_name}", base = server.client_addr() @@ -1283,7 +1283,7 @@ async fn api_v3_configure_table_delete() { async fn api_v3_configure_table_delete_no_db() { let db_name = "db"; let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!( "{base}/api/v3/configure/table?db={db_name}&table=foo", base = server.client_addr() @@ -1300,7 +1300,7 @@ async fn api_v3_configure_table_delete_no_db() { #[tokio::test] async fn api_v3_configure_table_delete_missing_query_param() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!("{base}/api/v3/configure/table", base = server.client_addr()); let resp = client @@ -1316,7 +1316,7 @@ async fn try_deleting_table_after_db_is_deleted() { let db_name = "db"; let tbl_name = "tbl"; let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let delete_db_url = format!( "{base}/api/v3/configure/database?db={db_name}", base = server.client_addr() diff --git a/influxdb3/tests/server/mod.rs b/influxdb3/tests/server/mod.rs index 1058853eb6d..d989a5acac1 100644 --- a/influxdb3/tests/server/mod.rs +++ b/influxdb3/tests/server/mod.rs @@ -11,8 +11,9 @@ use assert_cmd::cargo::CommandCargoExt; use futures::TryStreamExt; use influxdb_iox_client::flightsql::FlightSqlClient; use influxdb3_client::Precision; -use reqwest::Response; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; +use reqwest::{Certificate, Response, tls::Version}; +use tonic::transport::ClientTlsConfig; mod auth; mod client; @@ -206,6 +207,8 @@ impl TestServer { .args(["--http-bind", "0.0.0.0:0"]) .args(["--wal-flush-interval", "10ms"]) .args(["--wal-snapshot-size", "1"]) + .args(["--tls-cert", "../testing-certs/localhost.pem"]) + .args(["--tls-key", "../testing-certs/localhost.key"]) .args(config.as_args()) .stdout(Stdio::piped()); @@ -250,17 +253,32 @@ impl TestServer { } }); + let http_client = reqwest::ClientBuilder::new() + .min_tls_version(Version::TLS_1_3) + .use_rustls_tls() + .add_root_certificate( + Certificate::from_pem(&std::fs::read("../testing-certs/rootCA.pem").unwrap()) + .unwrap(), + ) + .build() + .unwrap(); + let server = Self { auth_token: config.auth_token().map(|s| s.to_owned()), bind_addr, server_process, - http_client: reqwest::Client::new(), + http_client, }; server.wait_until_ready().await; let (mut server, token) = if config.auth_enabled() { - let result = server.run(vec!["create", "token", "--admin"], &[]).unwrap(); + let result = server + .run( + vec!["create", "token", "--admin"], + &["--tls-ca", "../testing-certs/rootCA.pem"], + ) + .unwrap(); let token = parse_token(result); (server, Some(token)) } else { @@ -274,7 +292,10 @@ impl TestServer { /// Get the URL of the running service for use with an HTTP client pub fn client_addr(&self) -> String { - format!("http://{addr}", addr = self.bind_addr) + format!( + "https://localhost:{}", + self.bind_addr.split(':').nth(1).unwrap() + ) } /// Get the token for the server @@ -289,8 +310,13 @@ impl TestServer { /// Get a [`FlightSqlClient`] for making requests to the running service over gRPC pub async fn flight_sql_client(&self, database: &str) -> FlightSqlClient { + let cert = tonic::transport::Certificate::from_pem( + std::fs::read("../testing-certs/rootCA.pem").unwrap(), + ); let channel = tonic::transport::Channel::from_shared(self.client_addr()) .expect("create tonic channel") + .tls_config(ClientTlsConfig::new().ca_certificate(cert)) + .unwrap() .connect() .await .expect("connect to gRPC client"); @@ -301,14 +327,23 @@ impl TestServer { /// Get a raw [`FlightClient`] for performing Flight actions directly pub async fn flight_client(&self) -> FlightClient { + let cert = tonic::transport::Certificate::from_pem( + std::fs::read("../testing-certs/rootCA.pem").unwrap(), + ); let channel = tonic::transport::Channel::from_shared(self.client_addr()) .expect("create tonic channel") + .tls_config(ClientTlsConfig::new().ca_certificate(cert)) + .unwrap() .connect() .await .expect("connect to gRPC client"); FlightClient::new(channel) } + pub fn http_client(&self) -> &reqwest::Client { + &self.http_client + } + pub fn kill(&mut self) { self.server_process.kill().expect("kill the server process"); } @@ -354,7 +389,11 @@ impl TestServer { lp: impl ToString, precision: Precision, ) -> Result<(), influxdb3_client::Error> { - let mut client = influxdb3_client::Client::new(self.client_addr()).unwrap(); + let mut client = influxdb3_client::Client::new( + self.client_addr(), + Some("../testing-certs/rootCA.pem".into()), + ) + .unwrap(); if let Some(token) = &self.auth_token { client = client.with_auth_token(token); } @@ -424,7 +463,7 @@ impl TestServer { } self.http_client - .get(format!("{base}/query", base = self.client_addr(),)) + .get(format!("{base}/query", base = self.client_addr())) .headers(header_map) .query(params) .send() @@ -512,7 +551,11 @@ pub async fn write_lp_to_db( lp: &str, precision: Precision, ) -> Result<(), influxdb3_client::Error> { - let client = influxdb3_client::Client::new(server.client_addr()).unwrap(); + let client = influxdb3_client::Client::new( + server.client_addr(), + Some("../testing-certs/rootCA.pem".into()), + ) + .unwrap(); client .api_v3_write_lp(database) .body(lp.to_string()) diff --git a/influxdb3/tests/server/ping.rs b/influxdb3/tests/server/ping.rs index 04bec1d9aa6..1a717f55c63 100644 --- a/influxdb3/tests/server/ping.rs +++ b/influxdb3/tests/server/ping.rs @@ -6,7 +6,7 @@ use crate::server::TestServer; #[tokio::test] async fn test_ping() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); struct TestCase<'a> { url: &'a str, diff --git a/influxdb3/tests/server/query.rs b/influxdb3/tests/server/query.rs index 33955697a4e..cf194cba575 100644 --- a/influxdb3/tests/server/query.rs +++ b/influxdb3/tests/server/query.rs @@ -94,7 +94,7 @@ async fn api_v3_query_sql_params() { .await .unwrap(); - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!("{base}/api/v3/query_sql", base = server.client_addr()); // Use a POST request @@ -436,7 +436,7 @@ async fn api_v3_query_influxql_params() { .await .unwrap(); - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!("{base}/api/v3/query_influxql", base = server.client_addr()); // Use a POST request @@ -1301,9 +1301,14 @@ async fn api_v1_query_chunked() { } let stream = server.api_v1_query(¶ms, None).await.bytes_stream(); let values = stream - .map(|chunk| { - println!("{chunk:?}"); - serde_json::from_slice(chunk.unwrap().as_ref()).unwrap() + .filter_map(|chunk| async move { + let chunk = chunk.unwrap(); + if chunk.is_empty() { + None + } else { + println!("{chunk:?}"); + Some(serde_json::from_slice(&chunk).unwrap()) + } }) .collect::>() .await; @@ -1642,7 +1647,15 @@ async fn api_v1_query_group_by() { ]; let stream = server.api_v1_query(¶ms, None).await.bytes_stream(); let values = stream - .map(|chunk| serde_json::from_slice(&chunk.unwrap()).unwrap()) + .filter_map(|chunk| async move { + let chunk = chunk.unwrap(); + if chunk.is_empty() { + None + } else { + println!("{chunk:?}"); + Some(serde_json::from_slice(&chunk).unwrap()) + } + }) .collect::>() .await; // Use a snapshot to assert on the output structure. This deserializes each emitted line as @@ -1684,7 +1697,15 @@ async fn api_v1_query_group_by_with_nulls() { ]; let stream = server.api_v1_query(¶ms, None).await.bytes_stream(); let values = stream - .map(|chunk| serde_json::from_slice(&chunk.unwrap()).unwrap()) + .filter_map(|chunk| async move { + let chunk = chunk.unwrap(); + if chunk.is_empty() { + None + } else { + println!("{chunk:?}"); + Some(serde_json::from_slice(&chunk).unwrap()) + } + }) .collect::>() .await; // Use a snapshot to assert on the output structure. This deserializes each emitted line as @@ -1790,7 +1811,7 @@ async fn api_v3_query_null_tag_values_null_fields() { .await .unwrap(); - let client = reqwest::Client::new(); + let client = server.http_client(); let url = format!("{base}/api/v3/query_sql", base = server.client_addr()); let resp = client diff --git a/influxdb3/tests/server/write.rs b/influxdb3/tests/server/write.rs index 3b1efa50bdb..3a2b5336643 100644 --- a/influxdb3/tests/server/write.rs +++ b/influxdb3/tests/server/write.rs @@ -7,7 +7,7 @@ use crate::server::TestServer; #[tokio::test] async fn api_v1_write_request_parsing() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let write_url = format!("{base}/write", base = server.client_addr()); let write_body = "cpu,host=a usage=0.5"; @@ -123,7 +123,7 @@ async fn api_v1_write_request_parsing() { #[tokio::test] async fn api_v1_write_round_trip() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let write_url = format!("{base}/write", base = server.client_addr()); client @@ -163,7 +163,7 @@ async fn api_v1_write_round_trip() { #[tokio::test] async fn api_v2_write_request_parsing() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let write_url = format!("{base}/api/v2/write", base = server.client_addr()); let write_body = "cpu,host=a usage=0.5"; @@ -261,7 +261,7 @@ async fn api_v2_write_request_parsing() { #[tokio::test] async fn api_v2_write_round_trip() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let write_url = format!("{base}/api/v2/write", base = server.client_addr()); client @@ -348,7 +348,7 @@ async fn writes_with_different_schema_should_fail() { /// default to requiring the WAL to synce before returning. async fn api_no_sync_param() { let server = TestServer::spawn().await; - let client = reqwest::Client::new(); + let client = server.http_client(); let v1_write_url = format!("{base}/write", base = server.client_addr()); let v2_write_url = format!("{base}/api/v2/write", base = server.client_addr()); let v3_write_url = format!("{base}/api/v3/write_lp", base = server.client_addr()); diff --git a/influxdb3_client/src/lib.rs b/influxdb3_client/src/lib.rs index 1e6dc11d1ee..995b7e32a05 100644 --- a/influxdb3_client/src/lib.rs +++ b/influxdb3_client/src/lib.rs @@ -3,12 +3,13 @@ use hashbrown::HashMap; use influxdb3_catalog::log::{OrderedCatalogBatch, TriggerSettings}; use iox_query_params::StatementParam; use reqwest::{ - Body, IntoUrl, Method, StatusCode, + Body, Certificate, IntoUrl, Method, StatusCode, header::{CONTENT_TYPE, HeaderMap, HeaderValue}, + tls::Version, }; use secrecy::{ExposeSecret, Secret}; use serde::{Serialize, de::DeserializeOwned}; -use std::{fmt::Display, num::NonZeroUsize, string::FromUtf8Error, time::Duration}; +use std::{fmt::Display, num::NonZeroUsize, path::PathBuf, string::FromUtf8Error, time::Duration}; use url::Url; use influxdb3_types::http::*; @@ -58,6 +59,12 @@ pub enum Error { #[source] source: reqwest::Error, }, + + #[error("failed to build an http client: {0}")] + Builder(#[source] reqwest::Error), + + #[error("io error: {0}")] + IO(#[from] std::io::Error), } impl Error { @@ -87,11 +94,30 @@ pub struct Client { impl Client { /// Create a new [`Client`] - pub fn new(base_url: U) -> Result { + pub fn new(base_url: U, ca_cert: Option) -> Result { + let client = reqwest::Client::builder() + .min_tls_version(Version::TLS_1_3) + .use_rustls_tls(); + + let http_client = if let Some(ca_cert) = ca_cert { + let cert = std::fs::read(&ca_cert)?; + let cert = match ca_cert.extension().and_then(|s| s.to_str()) { + Some("der") => Certificate::from_der(&cert), + Some("pem") | Some(_) | None => Certificate::from_pem(&cert), + } + .map_err(Error::Builder)?; + client + .add_root_certificate(cert) + .build() + .map_err(Error::Builder)? + } else { + client.build().map_err(Error::Builder)? + }; + Ok(Self { base_url: base_url.into_url().map_err(Error::BaseUrl)?, auth_token: None, - http_client: reqwest::Client::new(), + http_client, }) } @@ -1226,7 +1252,7 @@ mod tests { .create_async() .await; - let client = Client::new(mock_server.url()) + let client = Client::new(mock_server.url(), None) .expect("create client") .with_auth_token(token); @@ -1267,7 +1293,7 @@ mod tests { .create_async() .await; - let client = Client::new(mock_server.url()) + let client = Client::new(mock_server.url(), None) .expect("create client") .with_auth_token(token); @@ -1306,7 +1332,7 @@ mod tests { .create_async() .await; - let client = Client::new(mock_server.url()).expect("create client"); + let client = Client::new(mock_server.url(), None).expect("create client"); let r = client .api_v3_query_sql(db, query) @@ -1340,7 +1366,7 @@ mod tests { .create_async() .await; - let client = Client::new(mock_server.url()).expect("create client"); + let client = Client::new(mock_server.url(), None).expect("create client"); let r = client .api_v3_query_influxql(db, query) @@ -1378,7 +1404,7 @@ mod tests { .create_async() .await; - let client = Client::new(mock_server.url()).expect("create client"); + let client = Client::new(mock_server.url(), None).expect("create client"); let mut builder = client.api_v3_query_influxql(db, query); @@ -1421,7 +1447,7 @@ mod tests { .create_async() .await; - let client = Client::new(mock_server.url()).expect("create client"); + let client = Client::new(mock_server.url(), None).expect("create client"); let r = client .api_v3_query_influxql(db, query) @@ -1481,7 +1507,7 @@ mod tests { ) .create_async() .await; - let client = Client::new(mock_server.url()).unwrap(); + let client = Client::new(mock_server.url(), None).unwrap(); client .api_v3_configure_last_cache_create(db, table) .name(name) @@ -1511,7 +1537,7 @@ mod tests { .with_status(204) .create_async() .await; - let client = Client::new(mock_server.url()).unwrap(); + let client = Client::new(mock_server.url(), None).unwrap(); let resp = client .api_v3_configure_last_cache_create(db, table) .send() @@ -1537,7 +1563,7 @@ mod tests { .with_status(200) .create_async() .await; - let client = Client::new(mock_server.url()).unwrap(); + let client = Client::new(mock_server.url(), None).unwrap(); client .api_v3_configure_last_cache_delete(db, table, name) .await diff --git a/influxdb3_load_generator/src/commands/common.rs b/influxdb3_load_generator/src/commands/common.rs index d25a829aa32..ef34eb68541 100644 --- a/influxdb3_load_generator/src/commands/common.rs +++ b/influxdb3_load_generator/src/commands/common.rs @@ -453,7 +453,7 @@ pub(crate) fn create_client( host_url: Url, auth_token: Option>, ) -> Result { - let mut client = Client::new(host_url)?; + let mut client = Client::new(host_url, None)?; if let Some(t) = auth_token { client = client.with_auth_token(t.expose_secret()); } diff --git a/influxdb3_server/Cargo.toml b/influxdb3_server/Cargo.toml index 33b6268f4d3..0e5d88df41b 100644 --- a/influxdb3_server/Cargo.toml +++ b/influxdb3_server/Cargo.toml @@ -70,6 +70,7 @@ futures.workspace = true hashbrown.workspace = true hex.workspace = true hyper.workspace = true +hyper-rustls.workspace = true humantime.workspace = true mime.workspace = true object_store.workspace = true @@ -77,6 +78,7 @@ parking_lot.workspace = true pin-project-lite.workspace = true pyo3.workspace = true regex.workspace = true +rustls-pemfile.workspace = true secrecy.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/influxdb3_server/src/builder.rs b/influxdb3_server/src/builder.rs index 81342cfb2b1..a5139898d6e 100644 --- a/influxdb3_server/src/builder.rs +++ b/influxdb3_server/src/builder.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use crate::{CommonServerState, Server, http::HttpApi}; use influxdb3_authz::{AuthProvider, NoAuthAuthenticator}; @@ -202,7 +202,7 @@ impl WithProcessingEngine, > { - pub async fn build(self) -> Server { + pub async fn build(self, cert_file: Option, key_file: Option) -> Server { let persister = Arc::clone(&self.persister.0); let authorizer = Arc::clone(&self.authorizer); let processing_engine = Arc::clone(&self.processing_engine.0); @@ -228,6 +228,8 @@ impl Server { common_state: self.common_state, http, + cert_file, + key_file, persister, authorizer, listener: self.listener.0, diff --git a/influxdb3_server/src/lib.rs b/influxdb3_server/src/lib.rs index d57175b2bba..68e3378bbf9 100644 --- a/influxdb3_server/src/lib.rs +++ b/influxdb3_server/src/lib.rs @@ -35,6 +35,9 @@ use observability_deps::tracing::info; use service::hybrid; use std::convert::Infallible; use std::fmt::Debug; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; use std::sync::Arc; use thiserror::Error; use tokio::net::TcpListener; @@ -122,6 +125,8 @@ pub struct Server { persister: Arc, authorizer: Arc, listener: TcpListener, + key_file: Option, + cert_file: Option, } impl Server { @@ -146,35 +151,88 @@ pub async fn serve( TRACE_SERVER_NAME, ); - let grpc_service = trace_layer.clone().layer(make_flight_server( - Arc::clone(&server.http.query_executor), - Some(server.authorizer()), - )); + if let (Some(key_file), Some(cert_file)) = (&server.key_file, &server.cert_file) { + let grpc_service = trace_layer.clone().layer(make_flight_server( + Arc::clone(&server.http.query_executor), + Some(server.authorizer()), + )); - let rest_service = hyper::service::make_service_fn(|_| { - let http_server = Arc::clone(&server.http); - let service = service_fn(move |req: hyper::Request| { - route_request(Arc::clone(&http_server), req) + let rest_service = hyper::service::make_service_fn(|_| { + let http_server = Arc::clone(&server.http); + let service = service_fn(move |req: hyper::Request| { + route_request(Arc::clone(&http_server), req) + }); + let service = trace_layer.layer(service); + futures::future::ready(Ok::<_, Infallible>(service)) + }); + + let hybrid_make_service = hybrid(rest_service, grpc_service); + let mut addr = AddrIncoming::from_listener(server.listener)?; + addr.set_nodelay(true); + let certs = { + let cert_file = File::open(cert_file).unwrap(); + let mut buf_reader = BufReader::new(cert_file); + rustls_pemfile::certs(&mut buf_reader) + .collect::, _>>() + .unwrap() + }; + let key = { + let key_file = File::open(key_file).unwrap(); + let mut buf_reader = BufReader::new(key_file); + rustls_pemfile::private_key(&mut buf_reader) + .unwrap() + .unwrap() + }; + + let timer_end = Instant::now(); + let startup_time = timer_end.duration_since(startup_timer); + info!( + address = %addr.local_addr(), + "startup time: {}ms", + startup_time.as_millis() + ); + + let acceptor = hyper_rustls::TlsAcceptor::builder() + .with_single_cert(certs, key) + .unwrap() + .with_all_versions_alpn() + .with_incoming(addr); + hyper::server::Server::builder(acceptor) + .serve(hybrid_make_service) + .with_graceful_shutdown(shutdown.cancelled()) + .await?; + } else { + let grpc_service = trace_layer.clone().layer(make_flight_server( + Arc::clone(&server.http.query_executor), + Some(server.authorizer()), + )); + + let rest_service = hyper::service::make_service_fn(|_| { + let http_server = Arc::clone(&server.http); + let service = service_fn(move |req: hyper::Request| { + route_request(Arc::clone(&http_server), req) + }); + let service = trace_layer.layer(service); + futures::future::ready(Ok::<_, Infallible>(service)) }); - let service = trace_layer.layer(service); - futures::future::ready(Ok::<_, Infallible>(service)) - }); - - let hybrid_make_service = hybrid(rest_service, grpc_service); - - let addr = AddrIncoming::from_listener(server.listener)?; - let timer_end = Instant::now(); - let startup_time = timer_end.duration_since(startup_timer); - info!( - address = %addr.local_addr(), - "startup time: {}ms", - startup_time.as_millis() - ); - hyper::server::Builder::new(addr, Http::new()) - .tcp_nodelay(true) - .serve(hybrid_make_service) - .with_graceful_shutdown(shutdown.cancelled()) - .await?; + + let hybrid_make_service = hybrid(rest_service, grpc_service); + let addr = AddrIncoming::from_listener(server.listener)?; + + let timer_end = Instant::now(); + let startup_time = timer_end.duration_since(startup_timer); + info!( + address = %addr.local_addr(), + "startup time: {}ms", + startup_time.as_millis() + ); + + hyper::server::Builder::new(addr, Http::new()) + .tcp_nodelay(true) + .serve(hybrid_make_service) + .with_graceful_shutdown(shutdown.cancelled()) + .await?; + } Ok(()) } @@ -840,7 +898,7 @@ mod tests { .time_provider(Arc::clone(&time_provider) as _) .tcp_listener(listener) .processing_engine(processing_engine) - .build() + .build(None, None) .await; let shutdown = frontend_shutdown.clone(); diff --git a/testing-certs/localhost.key b/testing-certs/localhost.key new file mode 100644 index 00000000000..09f11717599 --- /dev/null +++ b/testing-certs/localhost.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDXIz2Bej2Fm/Tn +EPkMZZZGYqGqeAvSBcY4VXKkG0H8DqZJMlC/l4AoH8pInbFUIvP/gT/keV7Yk7M6 +WGeOdpaz39ad/nW2P4a18jWiSnw+K5gakgO4nhVTNv/lUC7UXOZHhRYBbi2hjSTW +WcXgzdinb4yNz9n0d2RJxWu5YUuIrHfhj531dDDvXWZ9OxAOezlmo6JjxZ1Vnf2u +no9cGbTphe3XHQUqIUsi9WiFzEXR78+lR8Xw9gw1lxZhG8CDusiN9N0JLw22g015 +vqJqF8Cc7OngUmOYhCZejLnL8cqhpgg+bJNfPJBFXgxsuyAn32SjFiv3G3gAiKvY +qeak4n5XAgMBAAECggEAJM42m4waMl14DH/u25wVlX0SQ2O+QfQS8VhNAJ5ZL5Ii +CKu+/MgieLrXlIzF5asFK1UlZSUOgObj5rtxHAJVzfghc24xHTjJBx+k5Vrw3oH+ +x6zrTg5df5sNNGH/YP1zuBCgryX2UIkhIDrDi64rvplq5mgE1boHQkewIkru7Tak +DMMmyDvo00IuvwKgDsxN1Bg7s9h3TYByBEm0D+XSFT60idqb0xUu6cV9RN4u14Ng +KXsbeHfZBMTGMOOpPOc9AnR6uBznscV/01Zw9VUWgzQbHlUS6WRnHf9fj/wFcUmq +PjcyCG7VGMNVaJ5tjq8xL2Ft04KUFnNyf4oNBNZAgQKBgQDuTetU2ec1qE62eQf9 +X67mRHvqJ1r0XcE2AkemfF/AvSGSw4j9UmhSWdHbsoobI0hC0bdWaxP1ZiC7guD6 +8Rze545d+6pbJjBIbnR+lTnUOfRx6wFxbtN/CKoYUscLHtQGggYXW0Vtz3jtWz9O +2yV8tZE0N+/JVVx2TJNNeRNZZwKBgQDnHO4XWK5LBz1nMtaThJhX356LqjE7glNW +lsq1gm9Kl/Yrkl1gZRRZKdlR9FUrjov5w7B4kwzjprAZh1TvY6l30DIjEbpnhSeZ +5OEC9Sa3rp+7wuWx/gy0ESXQhfZ5bjMxgS9oBcWwlJKCQSl6mfmRA3htirzQ5OeB +vXxT98RtkQKBgGzTxrncPTuC3ld0lFKApNXhxpkbWe0o97hbeC8us2fIt5FXnLLb +NGsPO8hSE8Hq7fAd59UeV2IFPsODNI1FGUMuihSffp/bo8cgMjEwotUpllmD0gfk +bc+gvFUZUG7iqD9El9WIx/8q9XWn6xRu/gKixGoNdYMDaZNYyxhhR5XXAoGAJHpE +ehrdJ6p03pz/kTkUcooxFApAywu+66PRSuTTfAqQKEBpzJUnqQsZ6f2Ok9lj7oAc +kmtjVWsx0FWz6jTowqhxwWdC7KPvpDKj8TuQaN1/CLuyVW2rw6ARP+BlDrV+Dwo8 +2UZP9Zb9qOFwEpHK+kB5+ZA0TSRUBf5KaVZqAoECgYEApo0lo6IpXMeF7lhFxk8T +aeFbT0mZ9m76Ijy3/FBukUrVXDlr7LYikxqGbWS0UHEUWTp6oZkUpiQ00MScplDK +oHLY+vQCZqiwlE5EtwHwcdSXL9qi3pkLPrFS8eNVkP35a7ji9pfsVm3wB8kw/VaT +aTFaCqUAur7nj39EdXzV4Bk= +-----END PRIVATE KEY----- diff --git a/testing-certs/localhost.pem b/testing-certs/localhost.pem new file mode 100644 index 00000000000..cd3f322d1d7 --- /dev/null +++ b/testing-certs/localhost.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIUerzyPbmy4o/dUaDeSImifFDUTOMwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNTA0MDkyMjI3MjlaFw0yNjA0 +MDkyMjI3MjlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDXIz2Bej2Fm/TnEPkMZZZGYqGqeAvSBcY4VXKkG0H8 +DqZJMlC/l4AoH8pInbFUIvP/gT/keV7Yk7M6WGeOdpaz39ad/nW2P4a18jWiSnw+ +K5gakgO4nhVTNv/lUC7UXOZHhRYBbi2hjSTWWcXgzdinb4yNz9n0d2RJxWu5YUuI +rHfhj531dDDvXWZ9OxAOezlmo6JjxZ1Vnf2uno9cGbTphe3XHQUqIUsi9WiFzEXR +78+lR8Xw9gw1lxZhG8CDusiN9N0JLw22g015vqJqF8Cc7OngUmOYhCZejLnL8cqh +pgg+bJNfPJBFXgxsuyAn32SjFiv3G3gAiKvYqeak4n5XAgMBAAGjYzBhMB8GA1Ud +IwQYMBaAFDr3sQj/xXd0o29pUHuaZCoIVRYdMAkGA1UdEwQCMAAwFAYDVR0RBA0w +C4IJbG9jYWxob3N0MB0GA1UdDgQWBBSO/+wi3E08RgYGh5DvbEqfjtFEgzANBgkq +hkiG9w0BAQsFAAOCAQEAADYimLkdMMA2ZSg7dHfgaWmwcSSV7qocxrjPBCAPtK+m +jFba3pUgCfrjQPOpWE0ZwEqSB9G2EnfrSSrb+i8cbDoCBINvK1mnuchiSGFcqCaf +IhwdgyP+ZH1hQvOVFhLwywIarC+K6Y7NkrBDgBUddF3M52b/YLXT4nna4/Y6+Mj1 +/k4jy0w0lqZMTuyk/3xBDAjAMn3qnS/IRpVcyxGHk9OHNoGJDb534IHhle0uDebD +YqmPN0IT+XJdMEcLwN4os4lxKDJSeVgiSfJHJAo/70qk6Pgl1VSBxpMS4SJXY4rP +r9XrJr+Upxscyx8BoHXCAmVNMcvrsKsi6vfm6jyLVg== +-----END CERTIFICATE----- diff --git a/testing-certs/rootCA.pem b/testing-certs/rootCA.pem new file mode 100644 index 00000000000..a58ef9d5d93 --- /dev/null +++ b/testing-certs/rootCA.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUZgAxjo53WJySDwP7lArHapi+m0owDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNTA0MDkyMjI2MjJaFw0zMDA0 +MDgyMjI2MjJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDRhlqJhGL8uXsX55irVSU92a/GWXxIIVfiJlWU/Wcs +4fpTEYB++10Br2kQb344uUdq1l79VWsH7xQWDqTD0N1G9RS9z4Gi5sffNI/s+zIE +NE6skJxfl39P+2PXFir307mma/zXVVFEFHv1FFvSs0CBz8Fhgi1riAcPZKhbojDB +SbatrSJh/OMLixA8z3nvLk4knFWlV/NM6BWGYqPV4DdYXEpRzgl7lRuGJBufFASZ +MfDfcRHIDxhuqi0wbdx8oXJuzT+9eglPsBKcmTQS3StTmxvouephSoRJ6yZiT4r4 +W4cZDsexZvjsWtmXdfNYEJaE7o9FrDFn+fWl5QSg55OlAgMBAAGjUzBRMB0GA1Ud +DgQWBBQ697EI/8V3dKNvaVB7mmQqCFUWHTAfBgNVHSMEGDAWgBQ697EI/8V3dKNv +aVB7mmQqCFUWHTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBW +eoQlmPlZtQiHWvxtI0Qv8l3NmRCU6lwNSgCbG16Nruli5ZO5272Izt8oI2Ag490P +SW9pt22HqxjvMQtDWJUehHC2NcWV+x0I/m4SQ4uelMd/F9LM5mmrbRF00Rnl/iHZ +HZ5KBRn4Mt1lsHOA2WKSwIOoaOx4VA4vXpF43mhcoCobTjuWXrTF9f9vuoxf5KTL +Lke789keCJ3p62DSEzb5hWHjxUdV50GS2sU8FBM4k319PZPcLLSOSBl8C54rqyM6 +T5aWTfvsUhYTuT9hn/UVFpmJ3s4v4nL90lXNHDcDmExjQgYAJdTjv1BuZC4k5O3T +MP5MMUPIHlIF1urGxHhF +-----END CERTIFICATE-----