From 9079aa8b032fe5ade7d54d7e06ae706eac8a32c1 Mon Sep 17 00:00:00 2001 From: Casey Davenport Date: Thu, 21 May 2026 16:59:47 -0700 Subject: [PATCH] Strip apiserver endpoint env vars on non-cluster-host Typha by default (#4846) * Strip apiserver endpoint env vars on non-cluster-host Typha by default The non-cluster-host Typha is pod-networked, so it should use the default in-cluster kubernetes Service that kubelet injects into every pod. Always strip the inherited KUBERNETES_SERVICE_HOST/PORT (which come from the host-network endpoint, e.g. proxy.local on MKE) and only override when a pod-network endpoint is explicitly configured. * Use slices.DeleteFunc instead of bespoke env-var filter helper --- pkg/render/typha.go | 14 +++++++++----- pkg/render/typha_test.go | 28 +++++++++++++++++++++------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/pkg/render/typha.go b/pkg/render/typha.go index 89e706590b..4b85676b2b 100644 --- a/pkg/render/typha.go +++ b/pkg/render/typha.go @@ -16,6 +16,7 @@ package render import ( "fmt" + "slices" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -689,11 +690,14 @@ func (c *typhaComponent) typhaEnvVarsNonClusterHost() []corev1.EnvVar { envVars = replaceOrAppendEnvVar(envVars, "TYPHA_CLIENTURISAN", c.cfg.TLS.NodeNonClusterHostURISAN) // NCH Typha runs pod-networked, so the host-network apiserver endpoint - // (e.g. MKE's proxy.local) may not be reachable. - if podEp := c.cfg.K8sServiceEpPodNetwork; podEp.Host != "" && podEp.Port != "" { - envVars = replaceOrAppendEnvVar(envVars, "KUBERNETES_SERVICE_HOST", podEp.Host) - envVars = replaceOrAppendEnvVar(envVars, "KUBERNETES_SERVICE_PORT", podEp.Port) - } + // (e.g. MKE's proxy.local) may not be reachable. Strip the inherited env + // vars so we fall back to the default kubernetes Service that kubelet + // injects into every pod, then re-add a pod-network endpoint if one was + // configured explicitly. + envVars = slices.DeleteFunc(envVars, func(e corev1.EnvVar) bool { + return e.Name == "KUBERNETES_SERVICE_HOST" || e.Name == "KUBERNETES_SERVICE_PORT" + }) + envVars = append(envVars, c.cfg.K8sServiceEpPodNetwork.EnvVars()...) // Tell the health aggregator to listen on all interfaces. envVars = append(envVars, corev1.EnvVar{Name: "TYPHA_HEALTHHOST", Value: "0.0.0.0"}) diff --git a/pkg/render/typha_test.go b/pkg/render/typha_test.go index 1dc4a30d71..767c062bed 100644 --- a/pkg/render/typha_test.go +++ b/pkg/render/typha_test.go @@ -199,20 +199,20 @@ var _ = Describe("Typha rendering tests", func() { Expect(d.Spec.Template.Spec.Containers[0].ReadinessProbe.ProbeHandler.HTTPGet.Host).To(BeEmpty()) }) - It("should override KUBERNETES_SERVICE_HOST/PORT on the non-cluster-host Typha when a pod-network endpoint is configured", func() { + It("should strip the host-network apiserver endpoint from the non-cluster-host Typha and fall back to the default Service", func() { cfg.K8sServiceEp = k8sapi.ServiceEndpoint{Host: "proxy.local", Port: "6444"} - cfg.K8sServiceEpPodNetwork = k8sapi.ServiceEndpoint{Host: "10.96.0.1", Port: "443"} component := render.Typha(&cfg) resources, _ := component.Objects() + // NCH Typha is pod-networked; let kubelet's default service injection take over. d := rtest.GetResource(resources, "calico-typha-noncluster-host", "calico-system", "apps", "v1", "Deployment").(*appsv1.Deployment) - Expect(d.Spec.Template.Spec.Containers[0].Env).To(ContainElements( - corev1.EnvVar{Name: "KUBERNETES_SERVICE_HOST", Value: "10.96.0.1"}, - corev1.EnvVar{Name: "KUBERNETES_SERVICE_PORT", Value: "443"}, - )) + for _, e := range d.Spec.Template.Spec.Containers[0].Env { + Expect(e.Name).ToNot(Equal("KUBERNETES_SERVICE_HOST")) + Expect(e.Name).ToNot(Equal("KUBERNETES_SERVICE_PORT")) + } - // The host-networked Typha should still use the host-network endpoint. + // The host-networked Typha still gets the configured endpoint. dMain := rtest.GetResource(resources, "calico-typha", "calico-system", "apps", "v1", "Deployment").(*appsv1.Deployment) Expect(dMain.Spec.Template.Spec.Containers[0].Env).To(ContainElements( corev1.EnvVar{Name: "KUBERNETES_SERVICE_HOST", Value: "proxy.local"}, @@ -220,6 +220,20 @@ var _ = Describe("Typha rendering tests", func() { )) }) + It("should respect an explicit pod-network apiserver endpoint on the non-cluster-host Typha when configured", func() { + cfg.K8sServiceEp = k8sapi.ServiceEndpoint{Host: "proxy.local", Port: "6444"} + cfg.K8sServiceEpPodNetwork = k8sapi.ServiceEndpoint{Host: "10.96.0.1", Port: "443"} + + component := render.Typha(&cfg) + resources, _ := component.Objects() + + d := rtest.GetResource(resources, "calico-typha-noncluster-host", "calico-system", "apps", "v1", "Deployment").(*appsv1.Deployment) + Expect(d.Spec.Template.Spec.Containers[0].Env).To(ContainElements( + corev1.EnvVar{Name: "KUBERNETES_SERVICE_HOST", Value: "10.96.0.1"}, + corev1.EnvVar{Name: "KUBERNETES_SERVICE_PORT", Value: "443"}, + )) + }) + It("should use custom client common name when specified for non-cluster host Typha deployment", func() { cfg.TLS.NodeNonClusterHostCommonName = "custom-nch-cn" component := render.Typha(&cfg)