diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 94ed1fe..297dc55 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,16 +26,22 @@ jobs: platform: - runner: ubuntu-22.04 target: x86_64 + maturin_args: "" - runner: ubuntu-22.04 target: x86 + maturin_args: "" - runner: ubuntu-22.04 target: aarch64 + maturin_args: --no-default-features --features openssl-tls,kubederive,ws,latest,runtime - runner: ubuntu-22.04 target: armv7 + maturin_args: --no-default-features --features openssl-tls,kubederive,ws,latest,runtime - runner: ubuntu-22.04 target: s390x + maturin_args: "" - runner: ubuntu-22.04 target: ppc64le + maturin_args: "" steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -45,7 +51,7 @@ jobs: uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} - args: --release --out dist --find-interpreter + args: --release --out dist --find-interpreter ${{ matrix.platform.maturin_args }} sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} manylinux: auto before-script-linux: | @@ -83,12 +89,16 @@ jobs: platform: - runner: ubuntu-22.04 target: x86_64 + maturin_args: "" - runner: ubuntu-22.04 target: x86 + maturin_args: "" - runner: ubuntu-22.04 target: aarch64 + maturin_args: --no-default-features --features openssl-tls,kubederive,ws,latest,runtime - runner: ubuntu-22.04 target: armv7 + maturin_args: --no-default-features --features openssl-tls,kubederive,ws,latest,runtime steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -98,7 +108,7 @@ jobs: uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} - args: --release --out dist --find-interpreter + args: --release --out dist --find-interpreter ${{ matrix.platform.maturin_args }} sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} manylinux: musllinux_1_2 before-script-linux: | diff --git a/Cargo.lock b/Cargo.lock index 4ad0e4b..7444187 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "portforward" -version = "0.7.4" +version = "0.7.5" dependencies = [ "anyhow", "env_logger", @@ -1626,6 +1626,7 @@ checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", diff --git a/Cargo.toml b/Cargo.toml index 1faa402..fb8d32f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,10 @@ name = "portforward" crate-type = ["cdylib"] [features] -default = ["openssl-tls", "kubederive", "ws", "latest", "runtime"] +default = ["rustls-tls", "kubederive", "ws", "latest", "runtime"] kubederive = ["kube/derive"] openssl-tls = ["kube/client", "kube/openssl-tls"] -rustls-tls = ["kube/client", "kube/rustls-tls"] +rustls-tls = ["kube/client", "kube/rustls-tls", "kube/ring"] runtime = ["kube/runtime", "kube/unstable-runtime"] ws = ["kube/ws"] latest = ["k8s-openapi/v1_32"] diff --git a/src/portforward.rs b/src/portforward.rs index b2bbcd1..98ac290 100644 --- a/src/portforward.rs +++ b/src/portforward.rs @@ -91,11 +91,51 @@ async fn load_config( options.context = Some(kube_context.to_string()); } - let client_config = kube::config::Config::from_custom_kubeconfig(kube_config, &options).await?; + // Some clusters (e.g., AKS private link) require a specific TLS SNI that + // is provided via `tls-server-name` in kubeconfig. kube-rs' OpenSSL backend + // does not honor this, which then causes hostname mismatch errors when + // connecting to the apiserver. We default to rustls and also explicitly set + // tls_server_name when it is missing from the resolved client config. + let tls_server_name = resolve_tls_server_name(&kube_config, &options); + + let mut client_config = + kube::config::Config::from_custom_kubeconfig(kube_config, &options).await?; + + if client_config.tls_server_name.is_none() { + if let Some(server_name) = tls_server_name { + debug!("setting tls_server_name from kubeconfig: {}", server_name); + client_config.tls_server_name = Some(server_name); + } + } Ok(client_config) } +fn resolve_tls_server_name( + kube_config: &kube::config::Kubeconfig, + options: &kube::config::KubeConfigOptions, +) -> Option { + let context_name = options + .context + .as_deref() + .or(kube_config.current_context.as_deref())?; + + let context = kube_config + .contexts + .iter() + .find(|c| c.name == context_name)?; + let context = context.context.as_ref()?; + let cluster_name = context.cluster.as_str(); + + let cluster = kube_config + .clusters + .iter() + .find(|c| c.name == cluster_name)?; + + let cluster = cluster.cluster.as_ref()?; + cluster.tls_server_name.clone() +} + /// Tries to find a pod by the given or name or checks if it is a service /// and if a pod matches the selector of the service. ///