MastertheMesh
Solo Enterprise for Istio · Reference
Visual reference

Gateway API on Istio Ambient — what attaches to what

TO
Tom O'Rourke
EMEA Field CTO · Solo.io

Same handful of Kubernetes Gateway API kinds (Gateway, HTTPRoute, GRPCRoute, TCPRoute, TLSRoute, ReferenceGrant, GatewayClass) — but on Ambient each one lives somewhere different. A map of the three deployment shapes: edge ingress, in-mesh waypoint, east-west gateway — which kind attaches where, which gatewayClassName picks which proxy, and how parentRefs / targetRefs actually resolve.

Gateway API ingress waypoint east-west HTTPRoute

In sidecar Istio, the routing surface was Gateway + VirtualService. Ambient flips that to Kubernetes Gateway API — same job, different shape. HTTPRoute takes over from VirtualService, and a Gateway resource — with the right gatewayClassName — declares the proxy itself. Edge ingress, in-mesh waypoint, or east-west gateway.

The catch: one Gateway kind drives three completely different deployments. The kind doesn't change — the gatewayClassName does (istio / istio-waypoint / istio-eastwest). And what can attach to each one is different too: full L7 route set at the edge, every L7 kind plus authZ on a waypoint, and L4-only on the east-west side. The diagram below is the cheat sheet, with copy-paste YAML for each shape. Version note: GatewayClass names track Solo's Istio distribution (1.26+); upstream Istio uses the same istio / istio-waypoint names, and istio-eastwest is the Solo convention for the east-west class.

Edge · waypoint · east-west — where each kind lives

GATEWAY API · KIND × LOCATION cluster: workload EDGE INGRESS gatewayClassName: istio WAYPOINT (IN-MESH) gatewayClassName: istio-waypoint EAST-WEST gatewayClassName: istio-eastwest Internet north → south Ingress Gateway Envoy · Deployment listens :80 · :443 created by Gateway CR ATTACHES HERE Gateway declares the proxy + listeners HTTPRoute path / header / method matching TLSRoute SNI-based TLS passthrough TCPRoute · GRPCRoute protocol-specific routes parentRefs → this Gateway namespace: bookinfo · ambient enrolled Service reviews Waypoint Envoy · :15008 ztunnel (per-node) HBONE · mTLS · L4 ATTACHES HERE Gateway istio-waypoint · :15008 HBONE HTTPRoute parentRefs → waypoint Gateway GRPCRoute explicit gRPC method matching AuthorizationPolicy · RequestAuthentication targetRefs → waypoint Gateway L7 — JWT, header, method, path East-West Gateway Envoy · :15008 HBONE SNI-routed · L4 only created by Gateway CR Other cluster east ↔ west ATTACHES HERE Gateway istio-eastwest · :15008 HBONE No HTTPRoute attaches here east-west is L4 / SNI-routed only — the HBONE tunnel is decrypted at the remote ztunnel, then routed normally (L7 lives on the remote waypoint) apply HTTPRoute over there, not here CLUSTER BOUNDARY North-south external → app via Ingress Gateway · L7 In-mesh L7 service → service via Waypoint · L7 + AuthZ Cross-cluster mesh ↔ mesh via East-West Gateway · L4 HBONE Same Gateway kind everywhere — what changes is gatewayClassName, istio.io/waypoint-for label, and listener port / protocol
edge ingress · gatewayClassName: istio waypoint · gatewayClassName: istio-waypoint east-west · gatewayClassName: istio-eastwest cluster / namespace boundary

Read it like this: three columns, three deployments of the same Envoy binary — and each one is declared with a different gatewayClassName. Edge ingress takes north-south traffic and speaks the full L7 route set (HTTPRoute, TLSRoute, TCPRoute, GRPCRoute). Waypoints sit inside a workload namespace and handle L7 routing plus authZ (HTTPRoute, AuthorizationPolicy targetRefs, RequestAuthentication targetRefs). The east-west gateway is deliberately dumb — L4 only. It terminates HBONE from a peer cluster, hands the inner connection to the local ztunnel, and lets the remote waypoint do all the L7 work.

The Gateway API kinds, by where they live

One card per location. Each one tells you what attaches there and gives you a YAML sample to copy and adapt — the colour stripe matches the column in the diagram above.

🌐 Edge ingress gateway.networking.k8s.io/v1

North-south ingress. Pick gatewayClassName: istio and istiod spins up an Envoy Deployment + Service (LoadBalancer by default) — nothing to install yourself. Every L7 route kind attaches via parentRefs.

Gateway · edge ingress with HTTP + HTTPS listenersistiod creates the Envoy Deployment
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: bookinfo-gateway
  namespace: bookinfo
spec:
  gatewayClassName: istio
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Same
    - name: https
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: bookinfo-tls
      allowedRoutes:
        namespaces:
          from: Same
HTTPRoute · path-prefix match to a backend ServiceparentRefs → ingress Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: productpage
  namespace: bookinfo
spec:
  parentRefs:
    - name: bookinfo-gateway
      kind: Gateway
  hostnames:
    - bookinfo.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /productpage
      backendRefs:
        - name: productpage
          port: 9080
TLSRoute · SNI-based passthrough for a backendterminate at the workload, not the edge
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
  name: api-passthrough
  namespace: bookinfo
spec:
  parentRefs:
    - name: bookinfo-gateway
      kind: Gateway
      sectionName: https-passthrough
  hostnames:
    - secure-api.example.com
  rules:
    - backendRefs:
        - name: secure-api
          port: 443

🛡️ Waypoint gateway.networking.k8s.io/v1

The in-mesh L7 proxy. This is where HTTP routing, JWT validation, and L7 authZ actually happen for a Service, namespace, or workload. Declared with gatewayClassName: istio-waypoint — and the istio.io/waypoint-for label is what decides its scope. Get that label wrong and the waypoint quietly does nothing.

Gateway · waypoint for one Serviceistiod creates the waypoint Deployment
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: reviews-waypoint
  namespace: bookinfo
  labels:
    # Scope: service | workload | all
    istio.io/waypoint-for: service
spec:
  gatewayClassName: istio-waypoint
  listeners:
    - name: mesh
      port: 15008
      protocol: HBONE
HTTPRoute · 80/20 canary at the waypointparentRefs → waypoint Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: reviews-split
  namespace: bookinfo
spec:
  parentRefs:
    - name: reviews-waypoint
      kind: Gateway
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /reviews
      backendRefs:
        - name: reviews-v1
          port: 9080
          weight: 80
        - name: reviews-v2
          port: 9080
          weight: 20
GRPCRoute · explicit gRPC method matchingparentRefs → waypoint Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: reviews-grpc
  namespace: bookinfo
spec:
  parentRefs:
    - name: reviews-waypoint
      kind: Gateway
  rules:
    - matches:
        - method:
            service: reviews.v1.ReviewsService
            method:  GetReview
      backendRefs:
        - name: reviews-v1
          port: 9080
AuthorizationPolicy · L7 authZ on the waypointtargetRefs → waypoint Gateway
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: reviews-read-only
  namespace: bookinfo
spec:
  # Attach to the waypoint, NOT the workload. This is what makes
  # the policy run at L7 instead of being enforced by ztunnel at L4.
  targetRefs:
    - kind: Gateway
      group: gateway.networking.k8s.io
      name: reviews-waypoint
  action: ALLOW
  rules:
    - to:
        - operation:
            methods: ["GET"]
            paths:   ["/reviews/*"]

🔁 East-west gateway.networking.k8s.io/v1

The bridge between meshes — and intentionally the least clever of the three. gatewayClassName: istio-eastwest, one HBONE listener on :15008, done. Don't bother trying to attach an HTTPRoute here — it won't do anything. East-west is SNI-routed at L4; the inner connection is decrypted at the remote ztunnel, and any L7 work lives on the destination waypoint.

Gateway · east-west gateway on :15008 HBONEistiod-gloo creates the Envoy Deployment + LB Service
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: istio-eastwestgateway
  namespace: istio-eastwest
spec:
  gatewayClassName: istio-eastwest
  listeners:
    - name: hbone
      port: 15008
      protocol: HBONE
      tls:
        mode: Passthrough
      allowedRoutes:
        namespaces:
          from: All
Why there's no HTTPRoute heredesign note · not YAML
# East-west traffic from another cluster arrives as an HBONE tunnel.
# The east-west gateway terminates TLS, reads the SPIFFE identity and
# the inner destination service from the SNI, and forwards the inner
# connection to the local ztunnel — which then delivers it to the
# real workload (or to that workload's waypoint, if it has one).
#
# So:
#   * The gateway does NO HTTP parsing.
#   * Routing decisions are SNI-based, made by Envoy automatically
#     from the inner CONNECT headers — no HTTPRoute needed.
#   * Any L7 policy (HTTPRoute, AuthorizationPolicy with method/path,
#     RequestAuthentication) belongs on the DESTINATION waypoint.
#
# If you find yourself wanting to attach an HTTPRoute to an east-west
# Gateway, that's the signal you actually want a waypoint Gateway
# (istio-waypoint) instead.

Attachment cheat sheet — parentRefs vs targetRefs

These two are not the same thing. Routes (HTTPRoute, GRPCRoute, TCPRoute, TLSRoute) use parentRefs — "I'm a routing rule on this Gateway." Policies (AuthorizationPolicy, RequestAuthentication) use targetRefs — "apply this policy to whatever is on the other end of this reference." Routes attach upward; policies attach sideways.

Resource Attaches via Common targets Notes
HTTPRoute parentRefs Gateway (ingress · waypoint) Path / header / method matching, weighted backendRefs, filters.
GRPCRoute parentRefs Gateway (ingress · waypoint) gRPC service + method matching — cleaner than path-matching on HTTPRoute.
TCPRoute parentRefs Gateway (ingress only) Raw TCP forwarding to a backend Service / port.
TLSRoute parentRefs Gateway (ingress only) SNI-based TLS passthrough — terminate at the workload, not the edge.
AuthorizationPolicy targetRefs Gateway (waypoint) · Service L4 rules → enforced by ztunnel; L7 rules require a waypoint target.
RequestAuthentication targetRefs Gateway (waypoint) JWT issuer / audience / JWKS — populates request.auth.*.
ReferenceGrant n/a — grant resource Route ⇄ backend Service across namespaces Required when a Route in namespace A references a Service in namespace B.
GatewayClass cluster-scoped picks the controller Names a controller (e.g. istio.io/gateway-controller). One per class.

HTTPRoute vs the legacy VirtualService

For anything new on Ambient, reach for HTTPRoute first. It's portable, it's what every other Gateway API controller speaks, and both Solo and upstream Istio ship new features there before VirtualService. That said — VirtualService still earns its keep for a few things: advanced fault injection (delay + abort with percentage selectors), regex header matching, percentage-sampled mirroring, and routing to DestinationRule subsets by name. If you don't need any of those, use HTTPRoute.

Same 80 / 20 canary split, written both ways. Collapse one to focus on the other.

HTTPRoute · 80 / 20 canarymodern · portable
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: reviews-split
  namespace: bookinfo
spec:
  parentRefs:
    - name: reviews-waypoint
      kind: Gateway
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /reviews
      backendRefs:
        - name: reviews-v1
          port: 9080
          weight: 80
        - name: reviews-v2
          port: 9080
          weight: 20
VirtualService · same split, legacyneeds a DestinationRule for subsets
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: reviews-split
  namespace: bookinfo
spec:
  hosts:
    - reviews
  http:
    - match:
        - uri:
            prefix: /reviews
      route:
        - destination:
            host: reviews
            subset: v1
          weight: 80
        - destination:
            host: reviews
            subset: v2
          weight: 20
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: reviews
  namespace: bookinfo
spec:
  host: reviews
  subsets:
    - name: v1
      labels: { version: v1 }
    - name: v2
      labels: { version: v2 }

CLI — inspect Gateway API resources

🔍 kubectl + istioctl moves day 2

The signal that matters most is the Programmed condition on a Gateway. If it's True, istiod has built an Envoy config and the route attachments have resolved. If it's not, start digging there.

kubectl get · list every Gateway API resource in the clusterfast overview
# Everything Gateway-API in every namespace
kubectl get gateway,httproute,grpcroute,tcproute,tlsroute,referencegrant -A

# Just Gateways, with the controller and Programmed condition
kubectl get gateway -A -o wide

# Which GatewayClasses are registered? (controllers Solo / upstream Istio install)
kubectl get gatewayclass
# Expect to see: istio · istio-waypoint · istio-eastwest
kubectl describe gateway · read attached routes + Programmed conditionthe single most useful command
# Look at the Status block at the bottom: it lists every Route that
# attached, every listener's resolved Hostnames, and the Programmed
# condition (True = Envoy config has been generated).
kubectl -n bookinfo describe gateway reviews-waypoint

# Same for routes — the .status.parents[] array shows which Gateway
# each route ended up attaching to.
kubectl -n bookinfo describe httproute reviews-split
istioctl proxy-config · read the live Envoy configingress or waypoint Envoy
# Route table on the ingress Envoy
istioctl proxy-config routes deploy/bookinfo-gateway -n bookinfo

# Same for a waypoint
istioctl proxy-config routes deploy/reviews-waypoint -n bookinfo

# Listeners + clusters, in case the route table is empty
istioctl proxy-config listeners deploy/reviews-waypoint -n bookinfo
istioctl proxy-config clusters  deploy/reviews-waypoint -n bookinfo
kubectl get gatewayclass · confirm the controllers are registeredprereq for any Gateway CR
kubectl get gatewayclass -o custom-columns=\
NAME:.metadata.name,\
CONTROLLER:.spec.controllerName,\
ACCEPTED:.status.conditions[?\(@.type==\"Accepted\"\)].status

# Expected:
# NAME             CONTROLLER                      ACCEPTED
# istio            istio.io/gateway-controller     True
# istio-waypoint   istio.io/mesh-controller        True
# istio-eastwest   istio.io/eastwest-controller    True

Full reference table

One row per Gateway API kind, plus the three Ambient GatewayClasses at the bottom. The Where it lives column is the load-bearing one — it tells you which proxy actually serves the resource.

Resource Kind API What it does Where it lives
Gateway gateway-api gateway.networking.k8s.io/v1 Declares a proxy + listeners. The proxy is created by istiod based on gatewayClassName. edge · waypoint · east-west
HTTPRoute gateway-api gateway.networking.k8s.io/v1 HTTP routing: path / header / method matching, weighted backends, filters, rewrites. ingress Envoy · waypoint
GRPCRoute gateway-api gateway.networking.k8s.io/v1 gRPC service + method matching. Cleaner than path-matching on HTTPRoute for gRPC. ingress Envoy · waypoint
TCPRoute gateway-api gateway.networking.k8s.io/v1alpha2 Raw TCP forwarding from a listener to a backend Service. ingress Envoy
TLSRoute gateway-api gateway.networking.k8s.io/v1alpha2 SNI-based TLS passthrough — terminate at the workload, not the gateway. ingress Envoy
ReferenceGrant gateway-api gateway.networking.k8s.io/v1beta1 Permits a Route in namespace A to reference a backend Service in namespace B. istiod (admission)
GatewayClass gateway-api gateway.networking.k8s.io/v1 Cluster-scoped. Names a controller — one row per implementation. cluster-scoped
istio edge GatewayClass Edge ingress class. istiod creates an Envoy Deployment + LB Service per Gateway. controller: istio.io/gateway-controller
istio-waypoint waypoint GatewayClass In-mesh waypoint class. :15008 HBONE listener; scope via istio.io/waypoint-for. controller: istio.io/mesh-controller
istio-eastwest east-west GatewayClass East-west gateway class (Solo distribution). Single :15008 HBONE listener, L4 only. controller: istio.io/eastwest-controller

Where to go from here

Read this alongside the Istio Ambient CRDs visual map — it covers every CRD istiod watches, and Gateway API is one column of that bigger map. If you want to know what an east-west Gateway actually does on the wire once a packet lands on :15008, jump over to HBONE east-west.

For the kind definitions themselves, the upstream Gateway API site is the source of truth. Solo's GatewayClass names (istio, istio-waypoint, istio-eastwest) match upstream Istio for the first two — the east-west class is the Solo addition.