Envoy Gateway Multi-Namespace and Team Ownership: allowedRoutes, ReferenceGrant, and Cross-Team Governance

5 min read

One of Gateway API's biggest values isn't just prettier YAML — it finally answers a long-standing question clearly:

Who owns the ingress layer? Who can change routes? Can namespaces cross-reference each other?

Many Ingress pain points came from everything being tangled together. Platform teams feared unauthorized exposure changes. App teams felt like they had to file a ticket just to change a path. Everyone ended up torturing each other in YAML.

Gateway API's approach is to say clearly:

  • Platform ingress can be independently governed
  • Routes can be delegated to app teams
  • Cross-namespace behavior requires explicit authorization

This post explains that ownership model from top to bottom.

Start Here: Attachment and Reference Are Two Different Things

This is the most commonly confused distinction.

Route Attachment to Gateway / Listener

This is governed by:

  • The route's parentRefs
  • The listener's allowedRoutes

In other words, whether a route can attach to a listener isn't determined by ReferenceGrant — it's determined by whether the listener is willing to accept it.

Cross-Namespace Resource References

This is where ReferenceGrant typically comes in. For example:

  • A Gateway wants to reference a Secret in another namespace
  • An HTTPRoute wants to reference a Service in another namespace

This isn't a question of attachment — it's "has the target namespace formally authorized you to use its resources?"

Many people collapse these two into one and start questioning their sanity. The rules aren't that mysterious once the roles are separated.

allowedRoutes: Decide Who Can Attach First

The most restrictive listener looks like this:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gateway
  namespace: infra
spec:
  gatewayClassName: eg
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      allowedRoutes:
        namespaces:
          from: Same

This means only routes in the infra namespace can attach to this listener.

To allow specific team namespaces to attach, use Selector:

allowedRoutes:
  namespaces:
    from: Selector
    selector:
      matchLabels:
        shared-gateway-access: "true"

This pattern works well for platform teams. You don't have to lock everything down completely, but you also don't have to open it with from: All and let everyone in unrestricted. You can require namespaces to earn the right to attach by carrying a specific label.

💡 allowedRoutes is per-listener, not per-Gateway. Different listeners can have different access policies.

ReferenceGrant: The Target Namespace Decides

If a route or gateway wants to reference resources in another namespace, the target namespace has to authorize it.

For example, an app team's HTTPRoute wants to send traffic to a backend in a shared namespace:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
  namespace: team-a
spec:
  parentRefs:
    - name: shared-gateway
      namespace: infra
      sectionName: https
  rules:
    - backendRefs:
        - name: shared-api
          namespace: shared-services
          port: 8080

The shared-services namespace would need a ReferenceGrant:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-team-a-route
  namespace: shared-services
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: team-a
  to:
    - group: ""
      kind: Service

In plain English:

  • HTTPRoute resources in team-a
  • Are authorized to reference Service resources in shared-services

Note that the authorization is created by the target namespace, not the source. The source can't just declare it wants access — the resource owner decides. This design is actually quite principled, because owners should control who accesses their resources.

Common Platform/App Team Split

The most common ownership model looks like this:

Platform Team Owns

  • GatewayClass
  • Gateway
  • Listener
  • TLS certificate management
  • allowedRoutes policies

App Teams Own

  • HTTPRoute / GRPCRoute in their own namespaces
  • Backend services
  • Routing rule adjustments

The benefits of this split are clear:

  • Platform team guards the ingress exposure and security boundary
  • App teams can still move fast on their own traffic rules
  • No one needs to touch the shared Gateway just to change a path

You can also give all permissions to one group — that's not wrong. But in larger teams, that model tends to evolve into "anyone can change it, so when it breaks no one knows who did what."

Practical Advice: How to Avoid Multi-Tenant Disasters

Genuinely useful habits:

  • Shared Gateways default to allowedRoutes.namespaces.from: Selector
  • Namespace label conventions should be stable — don't change team-a to allow-gw next month
  • All cross-namespace backend/Secret references go through ReferenceGrant
  • Route names should indicate team or service — e.g., team-a-api-route
  • Shared listeners should always use sectionName to prevent routes attaching to the wrong entry

One principle worth keeping:

If you can enforce a boundary at the ingress layer, don't rely on documentation and hoping people self-govern.

In engineering, process beats verbal agreement. That sounds like advice from someone much older, but it's genuinely useful.

One-Line Summary

In Gateway API, allowedRoutes controls "who can enter," and ReferenceGrant controls "what they can access once inside." Keep those two straight, and multi-namespace and multi-team scenarios become much more manageable.

Next Step

With ownership and permissions sorted, the final piece is: When config doesn't take effect, which status, which condition, and which command do you look at first?

Next post covers debugging and status interpretation: 👉 Status and Debugging Handbook