From 087f5bf2c2d84f2256d2ee934b21acf589465117 Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Sun, 20 Nov 2022 15:58:23 -0700 Subject: [PATCH 1/5] check for client cert in with exec Signed-off-by: Carson Anderson --- kube-client/src/client/auth/mod.rs | 6 +++++- kube-client/src/client/config_ext.rs | 31 ++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/kube-client/src/client/auth/mod.rs b/kube-client/src/client/auth/mod.rs index 20849752a..5c2bc2d66 100644 --- a/kube-client/src/client/auth/mod.rs +++ b/kube-client/src/client/auth/mod.rs @@ -90,6 +90,7 @@ pub(crate) enum Auth { Basic(String, SecretString), Bearer(SecretString), RefreshableToken(RefreshableToken), + Certificate(String, String) } // Token file reference. Reloads at least once per minute. @@ -184,7 +185,7 @@ impl RefreshableToken { if Utc::now() + Duration::seconds(60) >= locked_data.1 { // TODO Improve refreshing exec to avoid `Auth::try_from` match Auth::try_from(&locked_data.2)? { - Auth::None | Auth::Basic(_, _) | Auth::Bearer(_) => { + Auth::None | Auth::Basic(_, _) | Auth::Bearer(_) | Auth::Certificate(_, _) => { return Err(Error::UnrefreshableTokenResponse); } @@ -291,6 +292,9 @@ impl TryFrom<&AuthInfo> for Auth { if let Some(exec) = &auth_info.exec { let creds = auth_exec(exec)?; let status = creds.status.ok_or(Error::ExecPluginFailed)?; + if let (Some(client_certificate_data), Some(client_key_data)) = (status.client_certificate_data, status.client_key_data) { + return Ok(Self::Certificate(client_certificate_data, client_key_data)) + } let expiration = status .expiration_timestamp .map(|ts| ts.parse()) diff --git a/kube-client/src/client/config_ext.rs b/kube-client/src/client/config_ext.rs index 35cac8c3d..e39cc6b67 100644 --- a/kube-client/src/client/config_ext.rs +++ b/kube-client/src/client/config_ext.rs @@ -23,6 +23,9 @@ pub trait ConfigExt: private::Sealed { /// Optional layer to set up `Authorization` header depending on the config. fn auth_layer(&self) -> Result>; + /// Use certificate data from exec + fn exec_identity_pem(&self) -> Option>; + /// Layer to add non-authn HTTP headers depending on the config. fn extra_headers_layer(&self) -> Result; @@ -144,9 +147,25 @@ impl ConfigExt for Config { Auth::RefreshableToken(refreshable) => { Some(AuthLayer(Either::B(AsyncFilterLayer::new(refreshable)))) } + Auth::Certificate(_client_certificate_data, _client_key_data) => None, }) } + fn exec_identity_pem(&self) -> Option> { + let exec_auth = Auth::try_from(&self.auth_info); + if exec_auth.is_err() { + return None + } + match exec_auth.unwrap() { + Auth::Certificate(client_certificate_data, client_key_data) => { + let mut buffer = client_key_data.as_bytes().to_vec(); + buffer.extend_from_slice(client_certificate_data.as_bytes()); + Some(buffer) + } + _ => None + } + } + fn extra_headers_layer(&self) -> Result { let mut headers = Vec::new(); if let Some(impersonate_user) = &self.auth_info.impersonate { @@ -174,8 +193,12 @@ impl ConfigExt for Config { #[cfg(feature = "rustls-tls")] fn rustls_client_config(&self) -> Result { + let mut identity = self.exec_identity_pem(); + if identity.is_none() { + identity = self.identity_pem(); + } tls::rustls_tls::rustls_client_config( - self.identity_pem().as_deref(), + identity.as_deref(), self.root_cert.as_deref(), self.accept_invalid_certs, ) @@ -192,7 +215,11 @@ impl ConfigExt for Config { #[cfg(feature = "openssl-tls")] fn openssl_ssl_connector_builder(&self) -> Result { - tls::openssl_tls::ssl_connector_builder(self.identity_pem().as_ref(), self.root_cert.as_ref()) + let mut identity = self.exec_identity_pem(); + if identity.is_none() { + identity = self.identity_pem(); + } + tls::openssl_tls::ssl_connector_builder(identity.as_ref(), self.root_cert.as_ref()) .map_err(|e| Error::OpensslTls(tls::openssl_tls::Error::CreateSslConnector(e))) } From e80e0314a4d4f768e66fd2517b62a946980263a7 Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Tue, 22 Nov 2022 20:16:54 -0700 Subject: [PATCH 2/5] move exec identity function, refactor funcs based on comments Signed-off-by: Carson Anderson --- kube-client/src/client/config_ext.rs | 41 ++++++++++------------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/kube-client/src/client/config_ext.rs b/kube-client/src/client/config_ext.rs index e39cc6b67..7d6f30815 100644 --- a/kube-client/src/client/config_ext.rs +++ b/kube-client/src/client/config_ext.rs @@ -23,9 +23,6 @@ pub trait ConfigExt: private::Sealed { /// Optional layer to set up `Authorization` header depending on the config. fn auth_layer(&self) -> Result>; - /// Use certificate data from exec - fn exec_identity_pem(&self) -> Option>; - /// Layer to add non-authn HTTP headers depending on the config. fn extra_headers_layer(&self) -> Result; @@ -151,21 +148,6 @@ impl ConfigExt for Config { }) } - fn exec_identity_pem(&self) -> Option> { - let exec_auth = Auth::try_from(&self.auth_info); - if exec_auth.is_err() { - return None - } - match exec_auth.unwrap() { - Auth::Certificate(client_certificate_data, client_key_data) => { - let mut buffer = client_key_data.as_bytes().to_vec(); - buffer.extend_from_slice(client_certificate_data.as_bytes()); - Some(buffer) - } - _ => None - } - } - fn extra_headers_layer(&self) -> Result { let mut headers = Vec::new(); if let Some(impersonate_user) = &self.auth_info.impersonate { @@ -193,10 +175,7 @@ impl ConfigExt for Config { #[cfg(feature = "rustls-tls")] fn rustls_client_config(&self) -> Result { - let mut identity = self.exec_identity_pem(); - if identity.is_none() { - identity = self.identity_pem(); - } + let identity = self.exec_identity_pem().or_else(|| self.identity_pem()); tls::rustls_tls::rustls_client_config( identity.as_deref(), self.root_cert.as_deref(), @@ -215,10 +194,7 @@ impl ConfigExt for Config { #[cfg(feature = "openssl-tls")] fn openssl_ssl_connector_builder(&self) -> Result { - let mut identity = self.exec_identity_pem(); - if identity.is_none() { - identity = self.identity_pem(); - } + let identity = self.exec_identity_pem().or_else(|| self.identity_pem()); tls::openssl_tls::ssl_connector_builder(identity.as_ref(), self.root_cert.as_ref()) .map_err(|e| Error::OpensslTls(tls::openssl_tls::Error::CreateSslConnector(e))) } @@ -247,3 +223,16 @@ impl ConfigExt for Config { Ok(https) } } + +impl Config { + fn exec_identity_pem(&self) -> Option> { + match Auth::try_from(&self.auth_info) { + Ok(Auth::Certificate(client_certificate_data, client_key_data)) => { + let mut buffer = client_key_data.as_bytes().to_vec(); + buffer.extend_from_slice(client_certificate_data.as_bytes()); + Some(buffer) + } + _ => None, + } + } +} From 73422b4cba72899537a22ddec73f2b2a4732670b Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Thu, 24 Nov 2022 22:03:00 -0700 Subject: [PATCH 3/5] add comment to exec_identity_pem Signed-off-by: Carson Anderson --- kube-client/src/client/config_ext.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/kube-client/src/client/config_ext.rs b/kube-client/src/client/config_ext.rs index 7d6f30815..be6c58020 100644 --- a/kube-client/src/client/config_ext.rs +++ b/kube-client/src/client/config_ext.rs @@ -4,7 +4,8 @@ use http::{header::HeaderName, HeaderValue}; use secrecy::ExposeSecret; use tower::{filter::AsyncFilterLayer, util::Either}; -#[cfg(any(feature = "rustls-tls", feature = "openssl-tls"))] use super::tls; +#[cfg(any(feature = "rustls-tls", feature = "openssl-tls"))] +use super::tls; use super::{ auth::Auth, middleware::{AddAuthorizationLayer, AuthLayer, BaseUriLayer, ExtraHeadersLayer}, @@ -225,14 +226,18 @@ impl ConfigExt for Config { } impl Config { + // This is necessary to retrieve an identity when an exec plugin + // returns a client certificate and key instead of a token. + // This has be to be checked on TLS configuration vs tokens + // which can be added in as an AuthLayer. fn exec_identity_pem(&self) -> Option> { - match Auth::try_from(&self.auth_info) { - Ok(Auth::Certificate(client_certificate_data, client_key_data)) => { + match Auth::try_from(&self.auth_info) { + Ok(Auth::Certificate(client_certificate_data, client_key_data)) => { let mut buffer = client_key_data.as_bytes().to_vec(); buffer.extend_from_slice(client_certificate_data.as_bytes()); Some(buffer) - } - _ => None, - } + } + _ => None, + } } } From 079f4602f23518fa7d0734f3846f322b8361349a Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Fri, 25 Nov 2022 07:09:58 -0700 Subject: [PATCH 4/5] move clientKeyData to SecretString Signed-off-by: Carson Anderson --- kube-client/src/client/auth/mod.rs | 14 +++++++++----- kube-client/src/client/config_ext.rs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/kube-client/src/client/auth/mod.rs b/kube-client/src/client/auth/mod.rs index 5c2bc2d66..e247583a5 100644 --- a/kube-client/src/client/auth/mod.rs +++ b/kube-client/src/client/auth/mod.rs @@ -19,8 +19,10 @@ use tower::{filter::AsyncPredicate, BoxError}; use crate::config::{AuthInfo, AuthProviderConfig, ExecConfig}; -#[cfg(feature = "oauth")] mod oauth; -#[cfg(feature = "oauth")] pub use oauth::Error as OAuthError; +#[cfg(feature = "oauth")] +mod oauth; +#[cfg(feature = "oauth")] +pub use oauth::Error as OAuthError; #[derive(Error, Debug)] /// Client auth errors @@ -90,7 +92,7 @@ pub(crate) enum Auth { Basic(String, SecretString), Bearer(SecretString), RefreshableToken(RefreshableToken), - Certificate(String, String) + Certificate(String, SecretString), } // Token file reference. Reloads at least once per minute. @@ -292,8 +294,10 @@ impl TryFrom<&AuthInfo> for Auth { if let Some(exec) = &auth_info.exec { let creds = auth_exec(exec)?; let status = creds.status.ok_or(Error::ExecPluginFailed)?; - if let (Some(client_certificate_data), Some(client_key_data)) = (status.client_certificate_data, status.client_key_data) { - return Ok(Self::Certificate(client_certificate_data, client_key_data)) + if let (Some(client_certificate_data), Some(client_key_data)) = + (status.client_certificate_data, status.client_key_data) + { + return Ok(Self::Certificate(client_certificate_data, client_key_data.into())); } let expiration = status .expiration_timestamp diff --git a/kube-client/src/client/config_ext.rs b/kube-client/src/client/config_ext.rs index be6c58020..8731dba0f 100644 --- a/kube-client/src/client/config_ext.rs +++ b/kube-client/src/client/config_ext.rs @@ -233,7 +233,7 @@ impl Config { fn exec_identity_pem(&self) -> Option> { match Auth::try_from(&self.auth_info) { Ok(Auth::Certificate(client_certificate_data, client_key_data)) => { - let mut buffer = client_key_data.as_bytes().to_vec(); + let mut buffer = client_key_data.expose_secret().as_bytes().to_vec(); buffer.extend_from_slice(client_certificate_data.as_bytes()); Some(buffer) } From 6c38bdaa0a327f8c1daad6d436649776f5d77928 Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Mon, 28 Nov 2022 09:10:10 -0700 Subject: [PATCH 5/5] just fmt Signed-off-by: Carson Anderson --- kube-client/src/client/auth/mod.rs | 6 ++---- kube-client/src/client/config_ext.rs | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/kube-client/src/client/auth/mod.rs b/kube-client/src/client/auth/mod.rs index 0ac9a5cdd..cc8cd179b 100644 --- a/kube-client/src/client/auth/mod.rs +++ b/kube-client/src/client/auth/mod.rs @@ -19,10 +19,8 @@ use tower::{filter::AsyncPredicate, BoxError}; use crate::config::{AuthInfo, AuthProviderConfig, ExecConfig}; -#[cfg(feature = "oauth")] -mod oauth; -#[cfg(feature = "oauth")] -pub use oauth::Error as OAuthError; +#[cfg(feature = "oauth")] mod oauth; +#[cfg(feature = "oauth")] pub use oauth::Error as OAuthError; #[derive(Error, Debug)] /// Client auth errors diff --git a/kube-client/src/client/config_ext.rs b/kube-client/src/client/config_ext.rs index 8731dba0f..7871b01dc 100644 --- a/kube-client/src/client/config_ext.rs +++ b/kube-client/src/client/config_ext.rs @@ -4,8 +4,7 @@ use http::{header::HeaderName, HeaderValue}; use secrecy::ExposeSecret; use tower::{filter::AsyncFilterLayer, util::Either}; -#[cfg(any(feature = "rustls-tls", feature = "openssl-tls"))] -use super::tls; +#[cfg(any(feature = "rustls-tls", feature = "openssl-tls"))] use super::tls; use super::{ auth::Auth, middleware::{AddAuthorizationLayer, AuthLayer, BaseUriLayer, ExtraHeadersLayer},