From ced642bc623bac563e627846f612f64284e2b670 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Fri, 21 Oct 2022 01:13:19 +0300 Subject: [PATCH 1/6] Support tls-server-name and change rustls workarounds --- kube-client/Cargo.toml | 4 ++-- kube-client/src/client/config_ext.rs | 11 ++++++++-- kube-client/src/config/file_config.rs | 5 +++++ kube-client/src/config/file_loader.rs | 12 +++++++++-- kube-client/src/config/mod.rs | 30 +++++++++++++++++++++------ 5 files changed, 50 insertions(+), 12 deletions(-) diff --git a/kube-client/Cargo.toml b/kube-client/Cargo.toml index d03ce1c04..17675e703 100644 --- a/kube-client/Cargo.toml +++ b/kube-client/Cargo.toml @@ -16,7 +16,7 @@ rust-version = "1.60.0" edition = "2021" [features] -default = ["client", "openssl-tls"] +default = ["client", "rustls-tls"] rustls-tls = ["rustls", "rustls-pemfile", "hyper-rustls"] openssl-tls = ["openssl", "hyper-openssl"] ws = ["client", "tokio-tungstenite", "rand", "kube-core/ws"] @@ -57,7 +57,7 @@ kube-core = { path = "../kube-core", version = "=0.75.0" } jsonpath_lib = { version = "0.3.0", optional = true } tokio-util = { version = "0.7.0", optional = true, features = ["io", "codec"] } hyper = { version = "0.14.13", optional = true, features = ["client", "http1", "stream", "tcp"] } -hyper-rustls = { version = "0.23.0", optional = true } +hyper-rustls = { git = "https://github.com/rustls/hyper-rustls", version = "0.23.0", optional = true } tokio-tungstenite = { version = "0.17.1", optional = true } tower = { version = "0.4.6", optional = true, features = ["buffer", "filter", "util"] } tower-http = { version = "0.3.2", optional = true, features = ["auth", "map-response-body", "trace"] } diff --git a/kube-client/src/client/config_ext.rs b/kube-client/src/client/config_ext.rs index 35cac8c3d..4f24ac5d4 100644 --- a/kube-client/src/client/config_ext.rs +++ b/kube-client/src/client/config_ext.rs @@ -184,10 +184,17 @@ impl ConfigExt for Config { #[cfg(feature = "rustls-tls")] fn rustls_https_connector(&self) -> Result> { - let rustls_config = std::sync::Arc::new(self.rustls_client_config()?); + let rustls_config = self.rustls_client_config()?; let mut http = hyper::client::HttpConnector::new(); http.enforce_http(false); - Ok(hyper_rustls::HttpsConnector::from((http, rustls_config))) + let mut b = hyper_rustls::HttpsConnectorBuilder::new() + .with_tls_config(rustls_config) + .https_or_http(); + if let Some(tsn) = self.tls_server_name.as_ref() { + b = b.with_server_name(tsn.clone()); + } + + Ok(b.enable_http1().wrap_connector(http)) } #[cfg(feature = "openssl-tls")] diff --git a/kube-client/src/config/file_config.rs b/kube-client/src/config/file_config.rs index bdfec434b..cbcce7314 100644 --- a/kube-client/src/config/file_config.rs +++ b/kube-client/src/config/file_config.rs @@ -103,6 +103,11 @@ pub struct Cluster { #[serde(rename = "proxy-url")] #[serde(skip_serializing_if = "Option::is_none")] pub proxy_url: Option, + /// If set, apiserver certificate will be validated to contain this string + /// (instead of hostname or IP address from the url). + #[serde(rename = "tls-server-name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub tls_server_name: Option, /// Additional information for extenders so that reads and writes don't clobber unknown fields #[serde(skip_serializing_if = "Option::is_none")] pub extensions: Option>, diff --git a/kube-client/src/config/file_loader.rs b/kube-client/src/config/file_loader.rs index 7cf8552a8..a7d0feb42 100644 --- a/kube-client/src/config/file_loader.rs +++ b/kube-client/src/config/file_loader.rs @@ -12,6 +12,8 @@ pub struct KubeConfigOptions { pub cluster: Option, /// The user to load pub user: Option, + /// Disable rustls-specific override for `tls-server-name` + pub disable_rustls_override: bool } /// ConfigLoader loads current context, cluster, and authentication information @@ -74,12 +76,13 @@ impl ConfigLoader { .ok_or_else(|| KubeconfigError::LoadContext(context_name.clone()))?; let cluster_name = cluster.unwrap_or(¤t_context.cluster); - let cluster = config + let mut cluster = config .clusters .iter() .find(|named_cluster| &named_cluster.name == cluster_name) .map(|named_cluster| &named_cluster.cluster) - .ok_or_else(|| KubeconfigError::LoadClusterOfContext(cluster_name.clone()))?; + .ok_or_else(|| KubeconfigError::LoadClusterOfContext(cluster_name.clone()))? + .clone(); let user_name = user.unwrap_or(¤t_context.user); let user = config @@ -89,6 +92,11 @@ impl ConfigLoader { .map(|named_user| &named_user.auth_info) .ok_or_else(|| KubeconfigError::FindUser(user_name.clone()))?; + // TODO: docs + if cfg!(feature = "rustls-tls") && true && cluster.tls_server_name.is_none() { + cluster.tls_server_name = Some("kubernetes.default.svc".to_string()); + } + Ok(ConfigLoader { current_context: current_context.clone(), cluster: cluster.clone(), diff --git a/kube-client/src/config/mod.rs b/kube-client/src/config/mod.rs index 77ccf4def..3843f4bad 100644 --- a/kube-client/src/config/mod.rs +++ b/kube-client/src/config/mod.rs @@ -155,6 +155,9 @@ pub struct Config { // TODO Actually support proxy or create an example with custom client /// Optional proxy URL. pub proxy_url: Option, + /// If set, apiserver certificate will be validated to contain this string + /// (instead of hostname or IP address from the url). + pub tls_server_name: Option, } impl Config { @@ -176,6 +179,7 @@ impl Config { accept_invalid_certs: false, auth_info: AuthInfo::default(), proxy_url: None, + tls_server_name: None, } } @@ -188,6 +192,13 @@ impl Config { /// /// [`Config::apply_debug_overrides`] is used to augment the loaded /// configuration based on the environment. + /// # Rustls-specific behavior + /// Rustls does not support validating IP addresses (see + /// ). + /// To work around this, when rustls is configured, this function automatically appends + /// `tls-server-name = "kubernetes.default.svc"` to the resulting configuration. + /// To opt out of this behavior, use lower-level methods [`Config::from_kubeconfig`] with customized options, + /// [`Config::incluster_dns`] and [`Config::incluster_env`]. pub async fn infer() -> Result { let mut config = match Self::from_kubeconfig(&KubeConfigOptions::default()).await { Err(kubeconfig_err) => { @@ -215,14 +226,19 @@ impl Config { } /// Load an in-cluster Kubernetes client configuration using - /// [`Config::incluster_dns`]. - /// - /// The `rustls-tls` feature is currently incompatible with - /// [`Config::incluster_env`]. See - /// . + /// [`Config::incluster_env`]. + /// # Rustls-specific behavior + /// Rustls does not support validating IP addresses (see + /// ). + /// To work around this, when rustls is configured, this function automatically appends + /// `tls-server-name = "kubernetes.default.svc"` to the resulting configuration. + /// To opt out of this behavior, use [`Config::incluster_env`] or + /// [`Config::incluster_dns`] instead. #[cfg(feature = "rustls-tls")] pub fn incluster() -> Result { - Self::incluster_dns() + let mut cfg = Self::incluster_env()?; + cfg.tls_server_name = Some("kubernetes.default.svc".to_string()); + Ok(cfg) } /// Load an in-cluster config using the `KUBERNETES_SERVICE_HOST` and @@ -271,6 +287,7 @@ impl Config { ..Default::default() }, proxy_url: None, + tls_server_name: None, }) } @@ -327,6 +344,7 @@ impl Config { accept_invalid_certs, proxy_url: loader.proxy_url()?, auth_info: loader.user, + tls_server_name: loader.cluster.tls_server_name }) } From 98510df408b19cbdda1dcb6a394b7e2a1544dea5 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Wed, 16 Nov 2022 20:56:36 +0300 Subject: [PATCH 2/6] Remove git override --- kube-client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kube-client/Cargo.toml b/kube-client/Cargo.toml index 17675e703..adc72cd7e 100644 --- a/kube-client/Cargo.toml +++ b/kube-client/Cargo.toml @@ -57,7 +57,7 @@ kube-core = { path = "../kube-core", version = "=0.75.0" } jsonpath_lib = { version = "0.3.0", optional = true } tokio-util = { version = "0.7.0", optional = true, features = ["io", "codec"] } hyper = { version = "0.14.13", optional = true, features = ["client", "http1", "stream", "tcp"] } -hyper-rustls = { git = "https://github.com/rustls/hyper-rustls", version = "0.23.0", optional = true } +hyper-rustls = { version = "0.23.1", optional = true } tokio-tungstenite = { version = "0.17.1", optional = true } tower = { version = "0.4.6", optional = true, features = ["buffer", "filter", "util"] } tower-http = { version = "0.3.2", optional = true, features = ["auth", "map-response-body", "trace"] } From f2b00050954cf4899f4a8c042ae1bd455729549a Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Wed, 16 Nov 2022 20:57:20 +0300 Subject: [PATCH 3/6] Reword description --- kube-client/src/config/file_config.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kube-client/src/config/file_config.rs b/kube-client/src/config/file_config.rs index cbcce7314..7dd0829c4 100644 --- a/kube-client/src/config/file_config.rs +++ b/kube-client/src/config/file_config.rs @@ -103,8 +103,9 @@ pub struct Cluster { #[serde(rename = "proxy-url")] #[serde(skip_serializing_if = "Option::is_none")] pub proxy_url: Option, - /// If set, apiserver certificate will be validated to contain this string - /// (instead of hostname or IP address from the url). + /// Name used to check server certificate. + /// + /// If `tls_server_name` is `None`, the hostname used to contact the server is used. #[serde(rename = "tls-server-name")] #[serde(skip_serializing_if = "Option::is_none")] pub tls_server_name: Option, From ad0a29493af8d68f7cc4a81240505e77ced4cebb Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Wed, 16 Nov 2022 21:04:02 +0300 Subject: [PATCH 4/6] Use new function in client builder --- kube-client/src/client/builder.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kube-client/src/client/builder.rs b/kube-client/src/client/builder.rs index bd5575bbf..83357f3e5 100644 --- a/kube-client/src/client/builder.rs +++ b/kube-client/src/client/builder.rs @@ -85,10 +85,7 @@ impl TryFrom for ClientBuilder, Response #[cfg(feature = "openssl-tls")] let connector = config.openssl_https_connector_with_connector(connector)?; #[cfg(all(not(feature = "openssl-tls"), feature = "rustls-tls"))] - let connector = hyper_rustls::HttpsConnector::from(( - connector, - std::sync::Arc::new(config.rustls_client_config()?), - )); + let connector = config.rustls_https_connector()?; let mut connector = TimeoutConnector::new(connector); From cac661a1c2a933f2ddfa49877e8b50c8009cf2ba Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Wed, 16 Nov 2022 21:11:53 +0300 Subject: [PATCH 5/6] Move hack to other place --- kube-client/src/client/config_ext.rs | 3 +++ kube-client/src/config/file_loader.rs | 12 ++---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/kube-client/src/client/config_ext.rs b/kube-client/src/client/config_ext.rs index 4f24ac5d4..edd18f109 100644 --- a/kube-client/src/client/config_ext.rs +++ b/kube-client/src/client/config_ext.rs @@ -192,6 +192,8 @@ impl ConfigExt for Config { .https_or_http(); if let Some(tsn) = self.tls_server_name.as_ref() { b = b.with_server_name(tsn.clone()); + } else { + b = b.with_server_name("kubernetes.default.svc".to_string()); } Ok(b.enable_http1().wrap_connector(http)) @@ -215,6 +217,7 @@ impl ConfigExt for Config { &self, connector: hyper::client::HttpConnector, ) -> Result> { + // TODO: pass tls_server_name to hyper-openssl let mut https = hyper_openssl::HttpsConnector::with_connector(connector, self.openssl_ssl_connector_builder()?) .map_err(|e| Error::OpensslTls(tls::openssl_tls::Error::CreateHttpsConnector(e)))?; diff --git a/kube-client/src/config/file_loader.rs b/kube-client/src/config/file_loader.rs index a7d0feb42..7cf8552a8 100644 --- a/kube-client/src/config/file_loader.rs +++ b/kube-client/src/config/file_loader.rs @@ -12,8 +12,6 @@ pub struct KubeConfigOptions { pub cluster: Option, /// The user to load pub user: Option, - /// Disable rustls-specific override for `tls-server-name` - pub disable_rustls_override: bool } /// ConfigLoader loads current context, cluster, and authentication information @@ -76,13 +74,12 @@ impl ConfigLoader { .ok_or_else(|| KubeconfigError::LoadContext(context_name.clone()))?; let cluster_name = cluster.unwrap_or(¤t_context.cluster); - let mut cluster = config + let cluster = config .clusters .iter() .find(|named_cluster| &named_cluster.name == cluster_name) .map(|named_cluster| &named_cluster.cluster) - .ok_or_else(|| KubeconfigError::LoadClusterOfContext(cluster_name.clone()))? - .clone(); + .ok_or_else(|| KubeconfigError::LoadClusterOfContext(cluster_name.clone()))?; let user_name = user.unwrap_or(¤t_context.user); let user = config @@ -92,11 +89,6 @@ impl ConfigLoader { .map(|named_user| &named_user.auth_info) .ok_or_else(|| KubeconfigError::FindUser(user_name.clone()))?; - // TODO: docs - if cfg!(feature = "rustls-tls") && true && cluster.tls_server_name.is_none() { - cluster.tls_server_name = Some("kubernetes.default.svc".to_string()); - } - Ok(ConfigLoader { current_context: current_context.clone(), cluster: cluster.clone(), From 31f85947d8850c1ca775a3dbfeddb21bcd0d7ff6 Mon Sep 17 00:00:00 2001 From: Eirik A Date: Mon, 12 Dec 2022 07:25:08 +0000 Subject: [PATCH 6/6] Update kube-client/Cargo.toml Signed-off-by: Eirik A --- kube-client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kube-client/Cargo.toml b/kube-client/Cargo.toml index adc72cd7e..363b9b22b 100644 --- a/kube-client/Cargo.toml +++ b/kube-client/Cargo.toml @@ -16,7 +16,7 @@ rust-version = "1.60.0" edition = "2021" [features] -default = ["client", "rustls-tls"] +default = ["client", "openssl-tls"] rustls-tls = ["rustls", "rustls-pemfile", "hyper-rustls"] openssl-tls = ["openssl", "hyper-openssl"] ws = ["client", "tokio-tungstenite", "rand", "kube-core/ws"]