kube_client/client/
config_ext.rs1use std::sync::Arc;
2
3use chrono::{DateTime, Utc};
4use http::{header::HeaderName, HeaderValue};
5#[cfg(feature = "openssl-tls")] use hyper::rt::{Read, Write};
6use hyper_util::client::legacy::connect::HttpConnector;
7use secrecy::ExposeSecret;
8use tower::{filter::AsyncFilterLayer, util::Either};
9
10#[cfg(any(feature = "rustls-tls", feature = "openssl-tls"))] use super::tls;
11use super::{
12 auth::Auth,
13 middleware::{AddAuthorizationLayer, AuthLayer, BaseUriLayer, ExtraHeadersLayer},
14};
15use crate::{Config, Error, Result};
16
17pub trait ConfigExt: private::Sealed {
23 fn base_uri_layer(&self) -> BaseUriLayer;
25
26 fn auth_layer(&self) -> Result<Option<AuthLayer>>;
28
29 fn extra_headers_layer(&self) -> Result<ExtraHeadersLayer>;
31
32 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
47 #[cfg(feature = "rustls-tls")]
48 fn rustls_https_connector(&self) -> Result<hyper_rustls::HttpsConnector<HttpConnector>>;
49
50 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
67 #[cfg(feature = "rustls-tls")]
68 fn rustls_https_connector_with_connector<H>(
69 &self,
70 connector: H,
71 ) -> Result<hyper_rustls::HttpsConnector<H>>;
72
73 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
91 #[cfg(feature = "rustls-tls")]
92 fn rustls_client_config(&self) -> Result<rustls::ClientConfig>;
93
94 #[cfg_attr(docsrs, doc(cfg(feature = "openssl-tls")))]
106 #[cfg(feature = "openssl-tls")]
107 fn openssl_https_connector(&self)
108 -> Result<hyper_openssl::client::legacy::HttpsConnector<HttpConnector>>;
109
110 #[cfg_attr(docsrs, doc(cfg(feature = "openssl-tls")))]
125 #[cfg(feature = "openssl-tls")]
126 fn openssl_https_connector_with_connector<H>(
127 &self,
128 connector: H,
129 ) -> Result<hyper_openssl::client::legacy::HttpsConnector<H>>
130 where
131 H: tower::Service<http::Uri> + Send,
132 H::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
133 H::Future: Send + 'static,
134 H::Response: Read + Write + hyper_util::client::legacy::connect::Connection + Unpin;
135
136 #[cfg_attr(docsrs, doc(cfg(feature = "openssl-tls")))]
154 #[cfg(feature = "openssl-tls")]
155 fn openssl_ssl_connector_builder(&self) -> Result<openssl::ssl::SslConnectorBuilder>;
156}
157
158mod private {
159 pub trait Sealed {}
160 impl Sealed for super::Config {}
161}
162
163impl ConfigExt for Config {
164 fn base_uri_layer(&self) -> BaseUriLayer {
165 BaseUriLayer::new(self.cluster_url.clone())
166 }
167
168 fn auth_layer(&self) -> Result<Option<AuthLayer>> {
169 Ok(match Auth::try_from(&self.auth_info).map_err(Error::Auth)? {
170 Auth::None => None,
171 Auth::Basic(user, pass) => Some(AuthLayer(Either::Left(
172 AddAuthorizationLayer::basic(&user, pass.expose_secret()).as_sensitive(true),
173 ))),
174 Auth::Bearer(token) => Some(AuthLayer(Either::Left(
175 AddAuthorizationLayer::bearer(token.expose_secret()).as_sensitive(true),
176 ))),
177 Auth::RefreshableToken(refreshable) => {
178 Some(AuthLayer(Either::Right(AsyncFilterLayer::new(refreshable))))
179 }
180 Auth::Certificate(_client_certificate_data, _client_key_data, _) => None,
181 })
182 }
183
184 fn extra_headers_layer(&self) -> Result<ExtraHeadersLayer> {
185 let mut headers = self.headers.clone();
186 if let Some(impersonate_user) = &self.auth_info.impersonate {
187 headers.push((
188 HeaderName::from_static("impersonate-user"),
189 HeaderValue::from_str(impersonate_user)
190 .map_err(http::Error::from)
191 .map_err(Error::HttpError)?,
192 ));
193 }
194 if let Some(impersonate_groups) = &self.auth_info.impersonate_groups {
195 for group in impersonate_groups {
196 headers.push((
197 HeaderName::from_static("impersonate-group"),
198 HeaderValue::from_str(group)
199 .map_err(http::Error::from)
200 .map_err(Error::HttpError)?,
201 ));
202 }
203 }
204 Ok(ExtraHeadersLayer {
205 headers: Arc::new(headers),
206 })
207 }
208
209 #[cfg(feature = "rustls-tls")]
210 fn rustls_client_config(&self) -> Result<rustls::ClientConfig> {
211 let identity = self.exec_identity_pem().0.or_else(|| self.identity_pem());
212 tls::rustls_tls::rustls_client_config(
213 identity.as_deref(),
214 self.root_cert.as_deref(),
215 self.accept_invalid_certs,
216 )
217 .map_err(Error::RustlsTls)
218 }
219
220 #[cfg(feature = "rustls-tls")]
221 fn rustls_https_connector(&self) -> Result<hyper_rustls::HttpsConnector<HttpConnector>> {
222 let mut connector = HttpConnector::new();
223 connector.enforce_http(false);
224 self.rustls_https_connector_with_connector(connector)
225 }
226
227 #[cfg(feature = "rustls-tls")]
228 fn rustls_https_connector_with_connector<H>(
229 &self,
230 connector: H,
231 ) -> Result<hyper_rustls::HttpsConnector<H>> {
232 use hyper_rustls::FixedServerNameResolver;
233
234 use crate::client::tls::rustls_tls;
235
236 let rustls_config = self.rustls_client_config()?;
237 let mut builder = hyper_rustls::HttpsConnectorBuilder::new()
238 .with_tls_config(rustls_config)
239 .https_or_http();
240 if let Some(tsn) = self.tls_server_name.as_ref() {
241 builder = builder.with_server_name_resolver(FixedServerNameResolver::new(
242 tsn.clone()
243 .try_into()
244 .map_err(rustls_tls::Error::InvalidServerName)
245 .map_err(Error::RustlsTls)?,
246 ));
247 }
248 Ok(builder.enable_http1().wrap_connector(connector))
249 }
250
251 #[cfg(feature = "openssl-tls")]
252 fn openssl_ssl_connector_builder(&self) -> Result<openssl::ssl::SslConnectorBuilder> {
253 let identity = self.exec_identity_pem().0.or_else(|| self.identity_pem());
254 tls::openssl_tls::ssl_connector_builder(identity.as_ref(), self.root_cert.as_ref())
256 .map_err(|e| Error::OpensslTls(tls::openssl_tls::Error::CreateSslConnector(e)))
257 }
258
259 #[cfg(feature = "openssl-tls")]
260 fn openssl_https_connector(
261 &self,
262 ) -> Result<hyper_openssl::client::legacy::HttpsConnector<HttpConnector>> {
263 let mut connector = HttpConnector::new();
264 connector.enforce_http(false);
265 self.openssl_https_connector_with_connector(connector)
266 }
267
268 #[cfg(feature = "openssl-tls")]
269 fn openssl_https_connector_with_connector<H>(
270 &self,
271 connector: H,
272 ) -> Result<hyper_openssl::client::legacy::HttpsConnector<H>>
273 where
274 H: tower::Service<http::Uri> + Send,
275 H::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
276 H::Future: Send + 'static,
277 H::Response: Read + Write + hyper_util::client::legacy::connect::Connection + Unpin,
278 {
279 let mut https = hyper_openssl::client::legacy::HttpsConnector::with_connector(
280 connector,
281 self.openssl_ssl_connector_builder()?,
282 )
283 .map_err(|e| Error::OpensslTls(tls::openssl_tls::Error::CreateHttpsConnector(e)))?;
284 if self.accept_invalid_certs {
285 https.set_callback(|ssl, _uri| {
286 ssl.set_verify(openssl::ssl::SslVerifyMode::NONE);
287 Ok(())
288 });
289 }
290 Ok(https)
291 }
292}
293
294impl Config {
295 pub(crate) fn exec_identity_pem(&self) -> (Option<Vec<u8>>, Option<DateTime<Utc>>) {
300 match Auth::try_from(&self.auth_info) {
301 Ok(Auth::Certificate(client_certificate_data, client_key_data, expiratiom)) => {
302 const NEW_LINE: u8 = b'\n';
303
304 let mut buffer = client_key_data.expose_secret().as_bytes().to_vec();
305 buffer.push(NEW_LINE);
306 buffer.extend_from_slice(client_certificate_data.as_bytes());
307 buffer.push(NEW_LINE);
308 (Some(buffer), expiratiom)
309 }
310 _ => (None, None),
311 }
312 }
313}