Envoy Gateway Multi-Namespace y Propiedad de Equipos: allowedRoutes, ReferenceGrant y Gobernanza entre Equipos
Uno de los mayores valores de Gateway API no es solo un YAML más bonito — finalmente responde con claridad una pregunta que llevaba mucho tiempo sin respuesta:
¿Quién es dueño de la capa de ingreso? ¿Quién puede cambiar rutas? ¿Pueden los namespaces hacer referencias cruzadas entre sí?
Muchos de los problemas de Ingress venían de que todo estaba enredado. Los equipos de plataforma temían cambios de exposición no autorizados. Los equipos de aplicación sentían que tenían que abrir un ticket solo para cambiar un path. Todos terminaban torturándose mutuamente con YAML.
El enfoque de Gateway API es decir claramente:
- El ingreso de plataforma puede gobernarse de forma independiente
- Las rutas pueden delegarse a los equipos de aplicación
- El comportamiento entre namespaces requiere autorización explícita
Este artículo explica ese modelo de propiedad de arriba a abajo.
Empezar aquí: Adjunción y Referencia son dos cosas distintas
Esta es la confusión más común.
Adjunción de Route a Gateway / Listener
Esto está gobernado por:
- El
parentRefsde la ruta - El
allowedRoutesdel listener
En otras palabras, si una ruta puede adjuntarse a un listener no lo determina ReferenceGrant — lo determina si el listener está dispuesto a aceptarla.
Referencias de recursos entre namespaces
Aquí es donde ReferenceGrant suele entrar en juego.
Por ejemplo:
- Un
Gatewayquiere referenciar unSecreten otro namespace - Un
HTTPRoutequiere referenciar unServiceen otro namespace
Esto no es una cuestión de adjunción — es "¿ha autorizado formalmente el namespace de destino que uses sus recursos?"
Muchas personas mezclan ambos conceptos y empiezan a cuestionarse su cordura. Las reglas no son tan misteriosas una vez que se separan los roles.
allowedRoutes: Decide quién puede adjuntarse primero
El listener más restrictivo tiene este aspecto:
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: SameEsto significa que solo las rutas en el namespace infra pueden adjuntarse a este listener.
Para permitir que namespaces de equipos específicos se adjunten, usa Selector:
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"Este patrón funciona muy bien para los equipos de plataforma.
No tienes que bloquear todo completamente, pero tampoco tienes que abrirlo con from: All y dejar entrar a todos sin restricción. Puedes exigir que los namespaces se ganen el derecho de adjuntarse llevando una etiqueta específica.
💡
allowedRouteses por listener, no por Gateway. Distintos listeners pueden tener diferentes políticas de acceso.
ReferenceGrant: El namespace de destino decide
Si una ruta o un gateway quiere referenciar recursos en otro namespace, el namespace de destino tiene que autorizarlo.
Por ejemplo, el HTTPRoute de un equipo de aplicación quiere enviar tráfico a un backend en un namespace compartido:
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: 8080El namespace shared-services necesitaría un 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: ServiceEn términos simples:
- Los recursos
HTTPRouteenteam-a - Están autorizados a referenciar recursos
Serviceenshared-services
Nótese que la autorización es creada por el namespace de destino, no por el origen. El origen no puede simplemente declarar que quiere acceso — el propietario del recurso decide. Este diseño es bastante principista, porque los propietarios deberían controlar quién accede a sus recursos.
División común entre equipos de plataforma y aplicación
El modelo de propiedad más común tiene este aspecto:
El equipo de plataforma posee
GatewayClassGatewayListener- Gestión de certificados TLS
- Políticas de
allowedRoutes
Los equipos de aplicación poseen
HTTPRoute/GRPCRouteen sus propios namespaces- Servicios de backend
- Ajustes de reglas de enrutamiento
Los beneficios de esta división son claros:
- El equipo de plataforma protege la exposición de ingreso y el perímetro de seguridad
- Los equipos de aplicación pueden seguir moviéndose rápido con sus propias reglas de tráfico
- Nadie necesita tocar el Gateway compartido solo para cambiar un path
También puedes dar todos los permisos a un solo grupo — eso no está mal. Pero en equipos más grandes, ese modelo tiende a evolucionar hacia "cualquiera puede cambiarlo, así que cuando se rompe nadie sabe quién hizo qué."
Consejos prácticos: Cómo evitar desastres multi-tenant
Hábitos genuinamente útiles:
- Los Gateways compartidos usan por defecto
allowedRoutes.namespaces.from: Selector - Las convenciones de etiquetas de namespace deben ser estables — no cambies
team-aaallow-gwel mes que viene - Todas las referencias cross-namespace a backends o Secrets pasan por
ReferenceGrant - Los nombres de las rutas deben indicar el equipo o servicio — por ejemplo,
team-a-api-route - Los listeners compartidos deben usar siempre
sectionNamepara evitar que las rutas se adjunten a la entrada equivocada
Un principio que vale la pena mantener:
Si puedes hacer cumplir un límite en la capa de ingreso, no te apoyes en la documentación y en esperar que las personas se autogestionen.
En ingeniería, el proceso supera al acuerdo verbal. Eso suena como un consejo de alguien mucho mayor, pero es genuinamente útil.
Resumen en una línea
En Gateway API, allowedRoutes controla "quién puede entrar", y ReferenceGrant controla "a qué pueden acceder una vez dentro."
Mantén esos dos conceptos claros y los escenarios multi-namespace y multi-equipo se vuelven mucho más manejables.
Siguiente paso
Con la propiedad y los permisos resueltos, la pieza final es: Cuando la configuración no surte efecto, ¿qué status, qué condition y qué comando miras primero?
El próximo artículo cubre el debugging e interpretación de status: 👉 Manual de Status y Debugging