From 0c7bf05ff09a7ea9b2afce9a73ab3c494b148546 Mon Sep 17 00:00:00 2001 From: Seth Malaki Date: Mon, 6 Apr 2026 14:00:52 +0100 Subject: [PATCH 1/2] fix(istio): add BPF-compatible egress rules for ztunnel and istiod policies When BPF dataplane is enabled, CTLB (connect-time load balancing) must be disabled for Istio ambient mode compatibility. Without CTLB and kube-proxy, the service-based egress selector in the ztunnel policy doesn't resolve correctly for cross-node traffic, and istiod's egress policy doesn't allow return traffic to remote ztunnel pods. This causes all ztunnel pods except the one co-located with istiod to fail with "XDS client connection error connecting to istiod" because the calico-system default-deny blocks the unmatched traffic. Fix by adding pod-selector-based egress rules (conditioned on BPF mode) so that: - ztunnel can reach istiod pods directly by pod IP (not just via service selector) - istiod can send return traffic to ztunnel pods on remote nodes --- pkg/render/istio/istio.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pkg/render/istio/istio.go b/pkg/render/istio/istio.go index cd75d60643..255d4516d0 100644 --- a/pkg/render/istio/istio.go +++ b/pkg/render/istio/istio.go @@ -319,6 +319,20 @@ func (c *IstioComponent) istiodCalicoSystemPolicy() *v3.NetworkPolicy { }, } + // In BPF mode, CTLB is disabled for Istio ambient compatibility. Without CTLB and + // kube-proxy, return traffic (SYN-ACKs) from istiod to ztunnel on remote nodes may not + // be matched by conntrack. Allow explicit egress to ztunnel pods to ensure cross-node + // ztunnel<->istiod communication works. + if c.cfg.Installation.BPFEnabled() { + egressRules = append(egressRules, v3.Rule{ + Action: v3.Allow, + Protocol: &networkpolicy.TCPProtocol, + Destination: v3.EntityRule{ + Selector: networkpolicy.KubernetesAppSelector(IstioZTunnelDaemonSetName), + }, + }) + } + // * Port 15012, gRPC, XDS and CA services (TLS and mTLS) // ztunnel and waypoints connect to it to request certs and dataplane // info. @@ -385,6 +399,20 @@ func (c *IstioComponent) ztunnelCalicoSystemPolicy() *v3.NetworkPolicy { Destination: networkpolicy.CreateServiceSelectorEntityRule(c.cfg.IstioNamespace, IstioIstiodServiceName), }, } + + // In BPF mode, CTLB is disabled for Istio ambient compatibility. Service-based egress + // selectors don't resolve correctly for cross-node traffic without CTLB and kube-proxy. + // Add a direct pod-selector rule so ztunnel can reach istiod by pod IP on any node. + if c.cfg.Installation.BPFEnabled() { + egressRules = append(egressRules, v3.Rule{ + Action: v3.Allow, + Protocol: &networkpolicy.TCPProtocol, + Destination: v3.EntityRule{ + Selector: networkpolicy.KubernetesAppSelector(IstioIstiodDeploymentName), + }, + }) + } + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Installation.KubernetesProvider.IsOpenShift()) return &v3.NetworkPolicy{ From 403c2608b088aced920083d0a27e9a676ee9f51d Mon Sep 17 00:00:00 2001 From: Seth Malaki Date: Mon, 6 Apr 2026 14:33:48 +0100 Subject: [PATCH 2/2] test(istio): add render tests for BPF-conditional egress rules Verify that when BPF dataplane is enabled, the ztunnel and istiod policies include pod-selector-based egress rules for cross-node communication. Also verify these rules are absent when BPF is not enabled. --- pkg/render/istio/istio_test.go | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/pkg/render/istio/istio_test.go b/pkg/render/istio/istio_test.go index 335c07939c..5dbfabc125 100644 --- a/pkg/render/istio/istio_test.go +++ b/pkg/render/istio/istio_test.go @@ -279,6 +279,58 @@ var _ = Describe("Istio Component Rendering", func() { Expect(foundIstiodRule).To(BeTrue(), "Expected egress rule for istiod service") }) + It("should add pod-selector egress rules when BPF dataplane is enabled", func() { + bpfDataplane := operatorv1.LinuxDataplaneBPF + cfg.Installation.CalicoNetwork = &operatorv1.CalicoNetworkSpec{ + LinuxDataplane: &bpfDataplane, + } + _, component, err := istio.Istio(cfg) + Expect(err).ShouldNot(HaveOccurred()) + Expect(component.ResolveImages(getCalicoTestImageSet())).ShouldNot(HaveOccurred()) + objsToCreate, _ := component.Objects() + + // Verify istiod policy has egress rule to ztunnel pods + istiodPolicy, err := rtest.GetResourceOfType[*v3.NetworkPolicy](objsToCreate, istio.IstioIstiodPolicyName, istio.IstioNamespace) + Expect(err).ShouldNot(HaveOccurred()) + Expect(istiodPolicy.Spec.Egress).To(HaveLen(2)) + Expect(istiodPolicy.Spec.Egress[1].Destination.Selector).To(Equal( + networkpolicy.KubernetesAppSelector(istio.IstioZTunnelDaemonSetName), + )) + + // Verify ztunnel policy has pod-selector egress rule to istiod + ztunnelPolicy, err := rtest.GetResourceOfType[*v3.NetworkPolicy](objsToCreate, istio.IstioZTunnelPolicyName, istio.IstioNamespace) + Expect(err).ShouldNot(HaveOccurred()) + foundPodSelectorRule := false + for _, rule := range ztunnelPolicy.Spec.Egress { + if rule.Destination.Selector == networkpolicy.KubernetesAppSelector(istio.IstioIstiodDeploymentName) { + foundPodSelectorRule = true + break + } + } + Expect(foundPodSelectorRule).To(BeTrue(), "Expected pod-selector egress rule for istiod when BPF enabled") + }) + + It("should not add pod-selector egress rules when BPF dataplane is not enabled", func() { + _, component, err := istio.Istio(cfg) + Expect(err).ShouldNot(HaveOccurred()) + Expect(component.ResolveImages(getCalicoTestImageSet())).ShouldNot(HaveOccurred()) + objsToCreate, _ := component.Objects() + + // Verify istiod policy has only 1 egress rule (kube API server) + istiodPolicy, err := rtest.GetResourceOfType[*v3.NetworkPolicy](objsToCreate, istio.IstioIstiodPolicyName, istio.IstioNamespace) + Expect(err).ShouldNot(HaveOccurred()) + Expect(istiodPolicy.Spec.Egress).To(HaveLen(1)) + + // Verify ztunnel policy has no pod-selector rule for istiod + ztunnelPolicy, err := rtest.GetResourceOfType[*v3.NetworkPolicy](objsToCreate, istio.IstioZTunnelPolicyName, istio.IstioNamespace) + Expect(err).ShouldNot(HaveOccurred()) + for _, rule := range ztunnelPolicy.Spec.Egress { + Expect(rule.Destination.Selector).NotTo(Equal( + networkpolicy.KubernetesAppSelector(istio.IstioIstiodDeploymentName), + ), "Should not have pod-selector egress rule for istiod when BPF is not enabled") + } + }) + It("should set TRANSPARENT_NETWORK_POLICIES env var on ztunnel", func() { _, component, err := istio.Istio(cfg) Expect(err).ShouldNot(HaveOccurred())