DEV Community

Kahiro Okina
Kahiro Okina

Posted on

[Gateway API] How to Extend Existing HTTPRoute Without Modification Using GEP-1294

Prerequisites

Please use a Service Mesh such as Istio that implements GEP-1294: xRoutes Mesh Binding.

Introduction

Without using Shadow Service, you can extend existing HTTPRoute without touching a single line of YAML using only Gateway API standard features. The key point is the specification that allows you to specify a Kubernetes Service in the parentRefs of HTTPRoute (GEP-1294).

According to GEP-1294's Route presence, when you create an xRoute with a Service as the parentRef, the implicit delivery (normal Service→Endpoints routing) for that Service is not "added" but can be replaced by the Route. Furthermore, Namespace boundaries states that a producer route with a Service in the same Namespace as parentRef SHOULD be applied to all inbound requests to that Service.

Benefits

  • You can customize even when existing tools don't provide fine-grained HTTPRoute customization
  • Can be achieved using only Gateway API specifications

Illustrated Diagram

image

Show Mermaid

flowchart LR
  classDef parent stroke-dasharray: 4 2;
  classDef note fill:#FFF9C4,stroke:#FBC02D,color:#5D4037,stroke-width:1px;

  subgraph Ingress
    GW[Gateway]
  end

  subgraph app-ns
    HR_a["HTTPRoute a (existing)"]
    SVC_a[(Service a)]
    DEP_a[Deployment a]
  end

  subgraph ext-ns
    HR_b["HTTPRoute b (new)"]
    SVC_b[(Service b)]
    DEP_b[Deployment b]
  end

  GW e1@--> HR_a
  HR_a -.parent ref: registration.-> GW
  HR_a e2@--> SVC_a
  SVC_a -."selector: Note 1".-x DEP_a
  SVC_a e4@--> HR_b
  HR_b -."parentRef: override".-> SVC_a
  HR_b e5@--> SVC_b
  SVC_b e6@--> DEP_b

  subgraph Notes
    direction TB
    N1["Note 1: A Route that uses a Service as parentRef **replaces** the implicit routing of that Service (GEP-1294 'Route presence'). Therefore, the selector on the Service side does not affect the final destination determination."]:::note
  end

  e1@{ animate: true }
  e2@{ animate: true }
  e4@{ animate: true }
  e5@{ animate: true }
  e6@{ animate: true }
Enter fullscreen mode Exit fullscreen mode

Use Cases

  • Without modifying HTTPRoute managed by a Controller, you can switch the destination Service or add necessary L7 controls (match/header manipulation/retry/split, etc.) using another HTTPRoute with Service as parent
  • "I want to control all inbound traffic to that Service" → producer route
  • "I want to redirect only outbound traffic from my Namespace" → consumer route

Additional Explanation

In GEP-1294, the meaning changes based on the relationship between the Namespace of the Service pointed to by parentRef and the Namespace of the Route. There is no explicit field in the API; this is purely a distinction in behavior.

This enables the following scenarios:

  • When you want to control the entire ingress point, define a producer route on the target Service side
  • When you want to control only outbound traffic from your Namespace, create a consumer route in a different Namespace

In both cases, this can be achieved by adding routes without touching existing HTTPRoute.

producer route (control ingress side)

Application conditions:
When parentRef points to a Service in the same Namespace

Behavior:

  • SHOULD be applied to all inbound requests to that Service (including calls from other Namespaces)
  • Route rules take precedence over the Service's default routing
  • Note: Some implementations apply this at the client-side proxy, so it may not affect clients outside the mesh

The diagram shown earlier illustrates this producer route.

consumer route (control egress side)

Application conditions:
When parentRef points to a Service in a different Namespace

Behavior:

  • Applied only to egress traffic from within the Namespace where the Route exists (limited scope)
  • Provides boundaries to prevent unrelated third-party Namespaces from arbitrarily changing traffic between different Namespaces

① A certain namespace has HTTPRoute a applied (consumer route / normal path)

image

flowchart LR
  classDef note fill:#FFF9C4,stroke:#FBC02D,color:#5D4037,stroke-width:1px;

  subgraph app-ns
    SVC_a[(Service a)]
    DEP_a[Deployment a]
  end

  subgraph other-ns["other-ns (e.g., some ns)"]
    CALL_other["Pod/Sidecar (caller)"]
    HR_a["HTTPRoute a (consumer)"]
  end

  CALL_other e1@--> HR_a
  HR_a -."parentRef: app-ns / Service a (different NS ↔ consumer)".-> SVC_a
  HR_a e2@--> SVC_a
  SVC_a e3@--> DEP_a

  e1@{ animate: true }
  e2@{ animate: true }
  e3@{ animate: true }
Enter fullscreen mode Exit fullscreen mode

② A specific namespace has HTTPRoute b applied (consumer route / destination redirect)

flowchart LR
  classDef note fill:#FFF9C4,stroke:#FBC02D,color:#5D4037,stroke-width:1px;

  subgraph app-ns
    SVC_a[(Service a)]
  end

  subgraph ext-ns
    SVC_b[(Service b)]
    DEP_b[Deployment b]
  end

  subgraph dev-ns["dev-ns (specific ns)"]
    CALL_dev["Pod/Sidecar (caller)"]
    HR_b["HTTPRoute b (consumer)"]
  end

  CALL_dev e1@--> HR_b
  HR_b -."parentRef: app-ns / Service a (different NS ↔ consumer)".-> SVC_a
  HR_b e2@--> SVC_b
  SVC_b e3@--> DEP_b

  e1@{ animate: true }
  e2@{ animate: true }
  e3@{ animate: true }
Enter fullscreen mode Exit fullscreen mode

Example

  1. Set up HTTPRoute normally with Gateway API
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gw
  namespace: app-ns
spec:
  gatewayClassName: istio
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      hostname: example.local
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: route-a
  namespace: app-ns
spec:
  hostnames:
    - example.local
  parentRefs:
    - name: gw
      namespace: app-ns
  rules:
    - backendRefs:
        - kind: Service
          name: svc-a
          port: 80
          weight: 100
---
apiVersion: v1
kind: Service
metadata:
  name: svc-a
  namespace: app-ns
spec:
  selector:
    app: a
  ports:
    - name: http
      port: 80
      targetPort: 5678
Enter fullscreen mode Exit fullscreen mode
  1. Without modifying the above HTTPRoute or Service, override the entire ingress point with a producer route (place route-b in app-ns)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: route-b
  namespace: app-ns  # Same NS, so this is a producer route
spec:
  parentRefs:
    - group: ""
      kind: Service
      name: svc-a
      namespace: app-ns
      port: 80
  rules:
    - backendRefs:
        - kind: Service
          name: svc-b
          namespace: ext-ns  # Switch to destination in different NS
          port: 80
          weight: 100
---
apiVersion: v1
kind: Service
metadata:
  name: svc-b
  namespace: ext-ns
spec:
  selector:
    app: b
  ports:
    - name: http
      port: 80
      targetPort: 5678
Enter fullscreen mode Exit fullscreen mode

Since the above is a producer route (parent reference in the same NS), the rules of route-b are applied to all inbound requests to svc-a, and the destination is redirected to svc-b (depending on the mesh implementation, the application point may be on the client side).

Note on ReferenceGrant

GEP-1294 explicitly states that in the Mesh (East/West) context, Routes with Service as parentRef can reference backendRefs in different Namespaces and are allowed without ReferenceGrant:

https://github.com/kubernetes-sigs/gateway-api/blob/main/geps/gep-1294/index.md#route-presence:~:text=Routes%20of%20either,a%20broader%20scope

Routes of either type can send traffic to backendRefs in any namespace. Unlike Gateway bound routes, this is allowed without a ReferenceGrant. In Gateway-bound routes (North-South), routes are opt-in; by default, no Services are exposed (often to the public internet), and a service producer must explicitly opt-in by creating a route themselves, or allowing another namespace to via ReferenceGrant. For mesh, routes augment existing Services, rather than exposing them to a broader scope.

Additionally, it notes that access control should be handled by mechanisms such as NetworkPolicy:

https://github.com/kubernetes-sigs/gateway-api/blob/main/geps/gep-1294/index.md#route-presence:~:text=As%20a%20result%2C%20a%20ReferenceGrant%20is%20not%20required%20in%20most%20mesh%20implementations.%20Access%20control%2C%20if%20desired%2C%20is%20handled%20by%20other%20mechanism%20such%20as%20NetworkPolicy

As a result, a ReferenceGrant is not required in most mesh implementations. Access control, if desired, is handled by other mechanism such as NetworkPolicy

Therefore, the configuration in this article where a producer route in app-ns routes to svc-b in ext-ns does not require ReferenceGrant.

Summary

I introduced a method to change the reference destination of HTTPRoute generated by other controllers using GEP-1294, achieving the same result as if you had written additional configuration in the HTTPRoute, when you cannot customize that HTTPRoute directly.

I hope this serves as a reference for someone.

Reference Links

Top comments (0)