Third-party gateways and ingress controllers
Integrate a third-party in-cluster ingress controller into your ambient mesh to secure traffic between the ingress and backend services with mTLS.
If you already have an in-cluster ingress controller that does not use the Kubernetes Gateway API, you can add it to your ambient mesh to secure traffic between the ingress and your backend services with mTLS.
Warning
Cloud load balancers cannot be added to the mesh using this method.
Before you begin
Deploy your ingress controller and configure it to route traffic to the service you want to expose. Refer to your controller’s documentation for installation and setup.
Add the ingress controller to the mesh
Label the ingress controller’s namespace to add it to the ambient mesh, just like any other workload.
kubectl label ns <ingress-namespace> istio.io/dataplane-mode=ambientBefore labeling the namespace, ztunnel logs show a successful connection but no mutual TLS (some fields are elided):
access connection complete src.workload="my-ingress-controller-cbcf8bf58-95vh2" dst.workload="productpage-v1-dffc47f64-gdqdz" direction="inbound"
After labeling the namespace, the logs confirm mTLS is enabled:
access connection complete src.workload="my-ingress-controller-cbcf8bf58-95vh2" src.identity="spiffe://cluster.local/ns/ingress-ns/sa/ingress" dst.workload="productpage-v1-dffc47f64-gdqdz" dst.identity="spiffe://cluster.local/ns/default/sa/bookinfo-productpage" direction="outbound"
Note that the log shows dst.workload, not dst.service. Most ingress controllers do their own internal load balancing and send traffic directly to Pods rather than to a Service. If you have a waypoint attached to the destination service, it will not be used unless the ingress is configured to send to Services instead.
Configure the ingress to send to Services (optional)
To ensure waypoint policies are applied, configure your ingress controller to route to Service IPs rather than Pod IPs. Since this is not standardized across controllers, consult your controller’s documentation for the specific configuration.
After making this change, the logs show dst.service instead of dst.workload, confirming traffic is hitting the Service:
access connection complete src.workload="my-ingress-controller-cbcf8bf58-95vh2" src.identity="spiffe://cluster.local/ns/ingress-ns/sa/ingress" dst.service="productpage.default.svc.cluster.local" dst.workload="productpage-v1-dffc47f64-gdqdz" dst.identity="spiffe://cluster.local/ns/default/sa/bookinfo-productpage" direction="outbound"
The following table shows implementation-specific configurations for some common controllers.
| Implementation | Configuration |
|---|---|
| ingress-nginx (archived) | Set the nginx.ingress.kubernetes.io/service-upstream: "true" annotation on Ingress resources |
| Emissary-Ingress | No changes required; sending to Services is the default behavior |
| F5 NGINX | Set the use-cluster-ip field in the VirtualServer configuration |
Skip inbound capture (optional)
When you add the ingress namespace to the mesh, ztunnel also intercepts inbound traffic to the ingress workload from external clients. While ztunnel handles this efficiently, external clients may not support mTLS, and ingress workloads are commonly the most performance-sensitive.
To disable mTLS for inbound traffic while keeping mTLS on the backend connections between the ingress and your mesh services, add the ambient.istio.io/bypass-inbound-capture: "true" annotation to the ingress controller pods.
Tip
Only use this annotation if the workload exclusively receives traffic from clients outside the mesh.