From 3d68ec1e6a98c61a3e7c412a8fe3bf9d84531c48 Mon Sep 17 00:00:00 2001 From: Eirik A Date: Fri, 8 Sep 2023 10:16:15 +0100 Subject: [PATCH] Change default TLS stack to `rustls-tls` (#1261) * Make rustls-tls the default tls stack feature Signed-off-by: clux * try unhacking windows ci for rustls Signed-off-by: clux * invert the readme on tls Signed-off-by: clux * disallow Client construction that requires auth when no tls stacks present fixes #1275 Signed-off-by: clux * fix defaults in examples also Signed-off-by: clux * properly fix example defaults Signed-off-by: clux * also force TlsRequired when cluster_url is https scheme Signed-off-by: clux --------- Signed-off-by: clux Signed-off-by: Eirik A --- .github/workflows/ci.yml | 12 +----------- README.md | 8 ++++---- examples/Cargo.toml | 2 +- examples/README.md | 6 +++--- examples/custom_client.rs | 3 ++- examples/custom_client_tls.rs | 8 ++++---- examples/custom_client_trace.rs | 2 +- kube-client/src/client/auth/oauth.rs | 12 ++++++------ kube-client/src/client/auth/oidc.rs | 17 ++++++++--------- kube-client/src/client/builder.rs | 18 ++++++++++++------ kube-client/src/error.rs | 4 ++++ kube/Cargo.toml | 12 +++++++++--- 12 files changed, 55 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1e780089..07ff3fb59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,19 +37,9 @@ jobs: # echo "OPENSSL_LIB_DIR=C:/Program Files/OpenSSL-Win64/lib" >> $env:GITHUB_ENV # echo "OPENSSL_DIR=C:/Program Files/OpenSSL-Win64/" >> $env:GITHUB_ENV # echo "OPENSSL_INCLUDE_DIR=C:/Program Files/OpenSSL-Win64/include" >> $env:GITHUB_ENV - # Only test Rustls on Windows instead due to #1191 - - name: "Interim Hacky Windows Test for #1191" - if: matrix.os == 'windows-latest' - run: | - sed -i '0,/openssl/s//rustls/' kube/Cargo.toml - cat kube/Cargo.toml - cargo build - cargo test --workspace --lib --exclude kube-examples --exclude e2e -j6 - # Real CI work starts here - name: Build workspace - if: matrix.os != 'windows-latest' run: cargo build # Workspace unit tests with various feature sets @@ -58,7 +48,7 @@ jobs: if: matrix.os == 'ubuntu-latest' # only linux tests all feature combinations - name: Run workspace unit tests (default features) run: cargo test --workspace --lib --exclude kube-examples --exclude e2e -j6 - if: matrix.os == 'ubuntu-latest' + if: matrix.os != 'macos-latest' - name: Run workspace unit tests (all features) if: matrix.os != 'windows-latest' run: cargo test --workspace --lib --all-features --exclude kube-examples --exclude e2e -j6 diff --git a/README.md b/README.md index 9363102a3..a0eed3374 100644 --- a/README.md +++ b/README.md @@ -146,17 +146,17 @@ Controller::new(root_kind_api, Config::default()) Here `reconcile` and `error_policy` refer to functions you define. The first will be called when the root or child elements change, and the second when the `reconciler` returns an `Err`. -## Rustls +## TLS -By default `openssl` is used for TLS, but [rustls](https://github.com/ctz/rustls) is supported. To switch, turn off `default-features`, and enable the `rustls-tls` feature: +By default [rustls](https://github.com/ctz/rustls) is used for TLS, but `openssl` is supported. To switch, turn off `default-features`, and enable the `openssl-tls` feature: ```toml [dependencies] -kube = { version = "0.85.0", default-features = false, features = ["client", "rustls-tls"] } +kube = { version = "0.85.0", default-features = false, features = ["client", "openssl-tls"] } k8s-openapi = { version = "0.19.0", features = ["v1_27"] } ``` -This will pull in `rustls` and `hyper-rustls`. If `default-features` is left enabled, you will pull in two TLS stacks, and the default will remain as `openssl`. +This will pull in `openssl` and `hyper-openssl`. If `default-features` is left enabled, you will pull in two TLS stacks, and the default will remain as `rustls`. ## musl-libc diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 985566972..ccbf69f71 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -14,7 +14,7 @@ license = "Apache-2.0" release = false [features] -default = ["openssl-tls", "kubederive", "ws", "latest", "runtime", "refresh"] +default = ["rustls-tls", "kubederive", "ws", "latest", "runtime", "refresh"] kubederive = ["kube/derive"] openssl-tls = ["kube/client", "kube/openssl-tls"] rustls-tls = ["kube/client", "kube/rustls-tls"] diff --git a/examples/README.md b/examples/README.md index 3387c1670..19eb7df7b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -136,9 +136,9 @@ cargo run --example crd_reflector The `crd_reflector` will just await changes. You can run `kubectl apply -f crd-baz.yaml`, or `kubectl delete -f crd-baz.yaml -n default`, or `kubectl edit foos baz -n default` to verify that the events are being picked up. -## rustls -Disable default features and enable `rustls-tls`: +## openssl +Disable default features and enable `openssl-tls`: ```sh -cargo run --example pod_watcher --no-default-features --features=rustls-tls,latest,runtime +cargo run --example pod_watcher --no-default-features --features=openssl-tls,latest,runtime ``` diff --git a/examples/custom_client.rs b/examples/custom_client.rs index d8323b88d..301adbe6a 100644 --- a/examples/custom_client.rs +++ b/examples/custom_client.rs @@ -9,7 +9,8 @@ async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); let config = Config::infer().await?; - let https = config.openssl_https_connector()?; + + let https = config.rustls_https_connector()?; let service = tower::ServiceBuilder::new() .layer(config.base_uri_layer()) .option_layer(config.auth_layer()?) diff --git a/examples/custom_client_tls.rs b/examples/custom_client_tls.rs index 36c7baed4..0ed8f5a55 100644 --- a/examples/custom_client_tls.rs +++ b/examples/custom_client_tls.rs @@ -14,15 +14,15 @@ async fn main() -> anyhow::Result<()> { let config = Config::infer().await?; // Pick TLS at runtime - let use_rustls = std::env::var("USE_RUSTLS").map(|s| s == "1").unwrap_or(false); - let client = if use_rustls { - let https = config.rustls_https_connector()?; + let use_openssl = std::env::var("USE_OPENSSL").map(|s| s == "1").unwrap_or(false); + let client = if use_openssl { + let https = config.openssl_https_connector()?; let service = ServiceBuilder::new() .layer(config.base_uri_layer()) .service(hyper::Client::builder().build(https)); Client::new(service, config.default_namespace) } else { - let https = config.openssl_https_connector()?; + let https = config.rustls_https_connector()?; let service = ServiceBuilder::new() .layer(config.base_uri_layer()) .service(hyper::Client::builder().build(https)); diff --git a/examples/custom_client_trace.rs b/examples/custom_client_trace.rs index 615b04b8a..5a43dd4f2 100644 --- a/examples/custom_client_trace.rs +++ b/examples/custom_client_trace.rs @@ -14,7 +14,7 @@ async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); let config = Config::infer().await?; - let https = config.openssl_https_connector()?; + let https = config.rustls_https_connector()?; let service = ServiceBuilder::new() .layer(config.base_uri_layer()) // showcase rate limiting; max 10rps, and 4 concurrent diff --git a/kube-client/src/client/auth/oauth.rs b/kube-client/src/client/auth/oauth.rs index a3e9a25be..5478cf338 100644 --- a/kube-client/src/client/auth/oauth.rs +++ b/kube-client/src/client/auth/oauth.rs @@ -108,17 +108,17 @@ impl Gcp { "At least one of rustls-tls or openssl-tls feature must be enabled to use oauth feature" ); // Current TLS feature precedence when more than one are set: - // 1. openssl-tls - // 2. rustls-tls - #[cfg(feature = "openssl-tls")] - let https = - hyper_openssl::HttpsConnector::new().map_err(Error::CreateOpensslHttpsConnector)?; - #[cfg(all(not(feature = "openssl-tls"), feature = "rustls-tls"))] + // 1. rustls-tls + // 2. openssl-tls + #[cfg(feature = "rustls-tls")] let https = hyper_rustls::HttpsConnectorBuilder::new() .with_native_roots() .https_only() .enable_http1() .build(); + #[cfg(all(not(feature = "rustls-tls"), feature = "openssl-tls"))] + let https = + hyper_openssl::HttpsConnector::new().map_err(Error::CreateOpensslHttpsConnector)?; let client = hyper::Client::builder().build::<_, hyper::Body>(https); diff --git a/kube-client/src/client/auth/oidc.rs b/kube-client/src/client/auth/oidc.rs index ed2906135..8bbf74c23 100644 --- a/kube-client/src/client/auth/oidc.rs +++ b/kube-client/src/client/auth/oidc.rs @@ -251,12 +251,12 @@ compile_error!( "At least one of rustls-tls or openssl-tls feature must be enabled to use refresh-oidc feature" ); // Current TLS feature precedence when more than one are set: -// 1. openssl-tls -// 2. rustls-tls -#[cfg(feature = "openssl-tls")] -type HttpsConnector = hyper_openssl::HttpsConnector; -#[cfg(all(not(feature = "openssl-tls"), feature = "rustls-tls"))] +// 1. rustls-tls +// 2. openssl-tls +#[cfg(feature = "rustls-tls")] type HttpsConnector = hyper_rustls::HttpsConnector; +#[cfg(all(not(feature = "rustls-tls"), feature = "openssl-tls"))] +type HttpsConnector = hyper_openssl::HttpsConnector; /// Struct for refreshing the ID token with the refresh token. #[derive(Debug)] @@ -300,15 +300,14 @@ impl Refresher { let client_id = get_field(Self::CONFIG_CLIENT_ID)?.into(); let client_secret = get_field(Self::CONFIG_CLIENT_SECRET)?.into(); - - #[cfg(feature = "openssl-tls")] - let https = hyper_openssl::HttpsConnector::new()?; - #[cfg(all(not(feature = "openssl-tls"), feature = "rustls-tls"))] + #[cfg(feature = "rustls-tls")] let https = hyper_rustls::HttpsConnectorBuilder::new() .with_native_roots() .https_only() .enable_http1() .build(); + #[cfg(all(not(feature = "rustls-tls"), feature = "openssl-tls"))] + let https = hyper_openssl::HttpsConnector::new()?; let https_client = hyper::Client::builder().build(https); diff --git a/kube-client/src/client/builder.rs b/kube-client/src/client/builder.rs index 68a5d19ea..bfaa945c5 100644 --- a/kube-client/src/client/builder.rs +++ b/kube-client/src/client/builder.rs @@ -72,20 +72,26 @@ impl TryFrom for ClientBuilder, Response use tracing::Span; let default_ns = config.default_namespace.clone(); + let auth_layer = config.auth_layer()?; let client: hyper::Client<_, hyper::Body> = { let mut connector = HttpConnector::new(); connector.enforce_http(false); // Current TLS feature precedence when more than one are set: - // 1. openssl-tls - // 2. rustls-tls + // 1. rustls-tls + // 2. openssl-tls // Create a custom client to use something else. // If TLS features are not enabled, http connector will be used. - #[cfg(feature = "openssl-tls")] - let connector = config.openssl_https_connector_with_connector(connector)?; - #[cfg(all(not(feature = "openssl-tls"), feature = "rustls-tls"))] + #[cfg(feature = "rustls-tls")] let connector = config.rustls_https_connector_with_connector(connector)?; + #[cfg(all(not(feature = "rustls-tls"), feature = "openssl-tls"))] + let connector = config.openssl_https_connector_with_connector(connector)?; + #[cfg(all(not(feature = "rustls-tls"), not(feature = "openssl-tls")))] + if auth_layer.is_none() || config.cluster_url.scheme() == Some(&http::uri::Scheme::HTTPS) { + // no tls stack situation only works on anonymous auth with http scheme + return Err(Error::TlsRequired); + } let mut connector = TimeoutConnector::new(connector); @@ -106,7 +112,7 @@ impl TryFrom for ClientBuilder, Response let service = ServiceBuilder::new() .layer(stack) - .option_layer(config.auth_layer()?) + .option_layer(auth_layer) .layer(config.extra_headers_layer()?) .layer( // Attribute names follow [Semantic Conventions]. diff --git a/kube-client/src/error.rs b/kube-client/src/error.rs index c527978a4..bc15beb2c 100644 --- a/kube-client/src/error.rs +++ b/kube-client/src/error.rs @@ -71,6 +71,10 @@ pub enum Error { #[error("rustls tls error: {0}")] RustlsTls(#[source] crate::client::RustlsTlsError), + /// Missing TLS stacks when TLS is required + #[error("TLS required but no TLS stack selected")] + TlsRequired, + /// Failed to upgrade to a WebSocket connection #[cfg(feature = "ws")] #[cfg_attr(docsrs, doc(cfg(feature = "ws")))] diff --git a/kube/Cargo.toml b/kube/Cargo.toml index 48fcd3d70..d035d08ac 100644 --- a/kube/Cargo.toml +++ b/kube/Cargo.toml @@ -16,18 +16,24 @@ rust-version = "1.64.0" edition = "2021" [features] -default = ["client", "openssl-tls"] +default = ["client", "rustls-tls"] + +# default features +client = ["kube-client/client", "config"] +config = ["kube-client/config"] rustls-tls = ["kube-client/rustls-tls"] + +# alternative features openssl-tls = ["kube-client/openssl-tls"] + +# auxiliary features ws = ["kube-client/ws", "kube-core/ws"] oauth = ["kube-client/oauth"] oidc = ["kube-client/oidc"] gzip = ["kube-client/gzip"] -client = ["kube-client/client", "config"] jsonpatch = ["kube-core/jsonpatch"] admission = ["kube-core/admission"] derive = ["kube-derive", "kube-core/schema"] -config = ["kube-client/config"] runtime = ["kube-runtime"] unstable-runtime = ["kube-runtime/unstable-runtime"]