Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for client cert with exec #1089

Merged
merged 6 commits into from Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion kube-client/src/client/auth/mod.rs
Expand Up @@ -90,6 +90,7 @@ pub(crate) enum Auth {
Basic(String, SecretString),
Bearer(SecretString),
RefreshableToken(RefreshableToken),
Certificate(String, String)
rcanderson23 marked this conversation as resolved.
Show resolved Hide resolved
}

// Token file reference. Reloads at least once per minute.
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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())
Expand Down
27 changes: 24 additions & 3 deletions kube-client/src/client/config_ext.rs
Expand Up @@ -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},
Expand Down Expand Up @@ -144,6 +145,7 @@ impl ConfigExt for Config {
Auth::RefreshableToken(refreshable) => {
Some(AuthLayer(Either::B(AsyncFilterLayer::new(refreshable))))
}
Auth::Certificate(_client_certificate_data, _client_key_data) => None,
})
}

Expand Down Expand Up @@ -174,8 +176,9 @@ impl ConfigExt for Config {

#[cfg(feature = "rustls-tls")]
fn rustls_client_config(&self) -> Result<rustls::ClientConfig> {
let identity = self.exec_identity_pem().or_else(|| 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,
)
Expand All @@ -192,7 +195,8 @@ impl ConfigExt for Config {

#[cfg(feature = "openssl-tls")]
fn openssl_ssl_connector_builder(&self) -> Result<openssl::ssl::SslConnectorBuilder> {
tls::openssl_tls::ssl_connector_builder(self.identity_pem().as_ref(), self.root_cert.as_ref())
let identity = self.exec_identity_pem().or_else(|| self.identity_pem());
clux marked this conversation as resolved.
Show resolved Hide resolved
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)))
}

Expand Down Expand Up @@ -220,3 +224,20 @@ impl ConfigExt for Config {
Ok(https)
}
}

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<Vec<u8>> {
clux marked this conversation as resolved.
Show resolved Hide resolved
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,
}
}
}