Designing External Traffic with Cilium Gateway API

Cilium Gateway API Part 1: Choosing an External Entry Point

Understand the Cilium Gateway API data path and choose between LoadBalancer, NodePort, and hostNetwork exposure.

Verified Source

Key takeaways

  • Cilium translates Gateway and HTTPRoute resources into Envoy configuration, while its eBPF data path delivers incoming connections to per-node Envoy
  • The external entry point must be chosen explicitly from a LoadBalancer Service, a NodePort consumed by an external appliance, or a direct hostNetwork listener
  • A highly available Kubernetes control plane and a highly available application ingress path solve different problems
  • A public IP missing from eth0 can still be associated with a node vNIC by provider SDN or virtual networking, so inspect the observed destination and provider requirements first
  • Production readiness requires routing, advertisement, policy, client-IP trust, and failure removal checks in addition to address allocation

Gateway API separates roles from the data path

  • Gateway API separates infrastructure ownership from application routing ownership

    • Platform teams own the implementation, listeners, and TLS boundary through GatewayClass and Gateway
    • Application teams own host, path, and backend rules through HTTPRoute and GRPCRoute
  • Cilium Gateway API is integrated into the CNI data path rather than being only another ingress-controller Deployment

    • The Cilium operator validates resources and produces CiliumEnvoyConfig
    • Cilium eBPF intercepts Service or host-port traffic and transparently directs it to Envoy
    • Envoy performs L7 routing and TLS termination before opening a connection to the backend Service
Cilium Gateway API control plane and data plane

Establish the common prerequisites first

  • The examples are verified against Cilium 1.20.1 and Gateway API 1.6.1

    • Check the documentation and upgrade guide for the exact Cilium version deployed in the cluster
    • Treat CiliumGatewayClassConfig as a v2alpha1 API that can change between releases
  • Cilium Gateway API requires kube-proxy replacement and the L7 proxy

values-cilium-gateway.yaml
kubeProxyReplacement: true
l7Proxy: true
gatewayAPI:
  enabled: true
  • Install compatible Gateway API CRDs before enabling the controller
kubectl apply --server-side \
  -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.1/standard-install.yaml

helm upgrade cilium cilium/cilium \
  --namespace kube-system \
  --reuse-values \
  --values values-cilium-gateway.yaml
  • Verify both the Cilium settings and the GatewayClass after installation
cilium status --wait
kubectl -n kube-system exec ds/cilium -- \
  cilium-dbg config --all | rg 'KubeProxyReplacement|EnableL7Proxy'
kubectl get gatewayclass cilium

Choose exposure based on network ownership

EnvironmentRecommended entryAddress ownerCritical requirement
Managed cloud LB integrationLoadBalancer ServiceCloud controllerPermission to watch Services and create LBs
On-premises or bare-metal VIPLB IPAM plus BGP or L2Cilium and the networkConfigure both allocation and advertisement
Existing external LBNodePort or hostNetworkExternal LB operatorsSynchronize node targets, ports, and health checks
Per-node cloud public IPhostNetworkProvider SDN and DNSPer-node association and failed-address removal
  • LB IPAM allocates an address but does not make it reachable by itself

    • Use Cilium BGP Control Plane when routers can learn the Service VIP over BGP
    • Use L2 Announcements when the VIP is reachable on the same L2 network
    • Use hostNetwork and external DNS when provider SDN associates a distinct public IP with each node vNIC
  • hostNetwork and the default LoadBalancer Service mode are mutually exclusive

    • Host-network listeners bind on all interfaces of selected nodes
    • Review the current limitations before selecting it when TCPRoute or UDPRoute is required

Keep one portable Gateway contract

  • The same Gateway and HTTPRoute contract can remain in place while the external exposure mode changes
gateway-and-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  namespace: edge
spec:
  gatewayClassName: cilium
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: wildcard-example-com
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: public
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: shop
spec:
  parentRefs:
    - name: public
      namespace: edge
  hostnames: ["shop.example.com"]
  rules:
    - backendRefs:
        - name: shop-web
          port: 8080
  • Cross-namespace attachment requires explicit permission on both sides
    • allowedRoutes must admit the Route namespace
    • Cross-namespace backend or Secret references require a ReferenceGrant owned by the target namespace

Design policy and client-IP trust together

  • Cilium policy sees the external-to-Envoy and Envoy-to-backend segments as distinct flows

    • The first segment is commonly world to the ingress identity
    • The second segment is the ingress identity to the application identity
  • An external LB requires an explicit authority for client IP information

    • Keep the default remote-address behavior when an L4 LB preserves source IP
    • Trust X-Forwarded-For only when a controlled L7 LB creates it and direct node access is blocked
    • An Internet client must never be able to inject a header that bypasses IP policy or audit attribution

Verify the request path, not just the address

  • Programmed=True confirms that configuration was produced, not that the Gateway is reachable from the Internet
kubectl get gateway -A
kubectl describe gateway -n edge public
kubectl get httproute -A
kubectl get svc -n edge
kubectl -n kube-system logs ds/cilium-envoy --since=10m
  • Test DNS, TCP, TLS, and HTTP from an external vantage point
dig +short shop.example.com
nc -vz shop.example.com 443
openssl s_client -connect shop.example.com:443 -servername shop.example.com </dev/null
curl --fail-with-body --resolve shop.example.com:443:203.0.113.10 \
  https://shop.example.com/healthz

References