diff --git a/object_store/src/client/mod.rs b/object_store/src/client/mod.rs index 5f3a042be46a..117940142bca 100644 --- a/object_store/src/client/mod.rs +++ b/object_store/src/client/mod.rs @@ -110,6 +110,8 @@ pub enum ClientConfigKey { Timeout, /// User-Agent header to be used by this client UserAgent, + /// PEM-formatted CA certificate + RootCa } impl AsRef for ClientConfigKey { @@ -129,6 +131,7 @@ impl AsRef for ClientConfigKey { Self::ProxyUrl => "proxy_url", Self::Timeout => "timeout", Self::UserAgent => "user_agent", + Self::RootCa => "root_ca", } } } @@ -156,6 +159,7 @@ impl FromStr for ClientConfigKey { store: "HTTP", key: s.into(), }), + "root_ca" => Ok(Self::RootCa), } } } @@ -179,6 +183,7 @@ pub struct ClientOptions { http2_keep_alive_while_idle: ConfigValue, http1_only: ConfigValue, http2_only: ConfigValue, + root_ca: Option, } impl ClientOptions { @@ -222,6 +227,9 @@ impl ClientOptions { ClientConfigKey::UserAgent => { self.user_agent = Some(ConfigValue::Deferred(value.into())) } + ClientConfigKey::RootCa => { + self.root_ca = Some(value.into()) + } } self } @@ -261,6 +269,9 @@ impl ClientOptions { .as_ref() .and_then(|v| v.get().ok()) .and_then(|v| v.to_str().ok().map(|s| s.to_string())), + ClientConfigKey::RootCa => self + .root_ca + .clone(), } } @@ -398,6 +409,13 @@ impl ClientOptions { self } + + /// Set a trusted CA certificate + pub fn with_root_ca(mut self, root_ca: impl Into) -> Self { + self.root_ca = Some(root_ca.into()); + self + } + /// Get the mime type for the file in `path` to be uploaded /// /// Gets the file extension from `path`, and returns the @@ -473,6 +491,13 @@ impl ClientOptions { builder = builder.danger_accept_invalid_certs(true) } + if let Some(cert) = &self.root_ca { + let certificate = reqwest::tls::Certificate::from_pem(cert.as_bytes()) + .map_err(map_client_error)?; + + builder = builder.add_root_certificate(certificate); + } + builder .https_only(!self.allow_http.get()?) .build() @@ -623,6 +648,7 @@ mod tests { let proxy_url = "https://fake_proxy_url".to_string(); let timeout = "95 seconds".to_string(); let user_agent = "object_store:fake_user_agent".to_string(); + let root_ca = "fake_certificate".to_string(); let options = HashMap::from([ ("allow_http", allow_http.clone()), @@ -648,6 +674,7 @@ mod tests { ("proxy_url", proxy_url.clone()), ("timeout", timeout.clone()), ("user_agent", user_agent.clone()), + ("root_ca", root_ca.clone()), ]); let builder = options @@ -739,5 +766,11 @@ mod tests { .unwrap(), user_agent ); + assert_eq!( + builder + .get_config_value(&ClientConfigKey::RootCa) + .unwrap(), + root_ca + ); } }