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