Skip to main content
logoTetrate Enterprise Gateway for Envoy (TEG)Version: v1.0.0

Expose Your Application on a Shared Gateway

In this guide, we'll expose an app using a shared gateway. This means that all apps and teams will share one physical set of Envoy gateways (probably deployed by the platform team), rather than each having their own which they manage themselves.

To use this article, first

For the purposes of this article it's assumed that your cluster follows the standard multi-tenancy pattern: each team of people has their own namespace(s). At first glance, a shared gateway should be no different to dedicated gateways -- each team deploys an HTTPRoute for their app(s) and attaches them to the single, shared Gateway resource. Indeed to a first approximation this is true, but the Gateway API (the spec that defines types like Gateway and HTTPRoute) was designed with multi-tenancy security in mind, and enforces a permissions system. This article will use the following representative cluster layout:

  • namespace ingress: contains the single, shared Gateway resource, which results in a single, shared Deployment of Envoys. In a production cluster this would be managed by the platform team, but in this guide we'll deploy everything ourselves.
  • namespace team1: the namespace for engineering team 1, who wish to offer a service. In a production cluster this is likely all the people on team 1 will have k8s RBAC permissions to, but again in this guide we will omnipotently deploy everything.
  • namespace team2: engineering team 2 are also deploying some apps.

Let's get set up by making those namespaces for this how-to:

kubectl create namespace ingress
kubectl create namespace team1
kubectl label namespace team1 purpose=workloads
kubectl create namespace team2
kubectl label namespace team2 purpose=workloads

Since everything will be in different namespaces, we will have to add fields and resources to our normal objects, in order to explicitly allow such cross-namespace configuration, as you'll see in this guide.

  1. Deploy Shared Gateway Instance

    First, we deploy a Gateway resource, which will result in a set of running Envoy proxies to handle incoming requests.

    cat <<EOF | kubectl apply -f -
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
    name: shared-gateway
    namespace: ingress
    spec:
    gatewayClassName: teg
    listeners:
    - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
    namespaces:
    from: Selector
    selector:
    matchLabels:
    purpose: workloads
    EOF

    Notice this is the same as the Gateway from the previous article, but with the addition of the allowedRoutes section. This allows HTTPRoutes in different namespaces, specifically any with the label purpose=workloads to attach themselves to this Gateway; without this they would be denied.

  2. Route Traffic to Team 2

    By way of a slightly contrived example, imagine that team 1 wants to offer a service, but that by luck they don't need to write any code, because team 2 has already deployed some Pods doing exactly what they want. So, we deploy the HTTPRoute below. This lives in team 1's namespace (as it's them offering the service at this location), but attaches to the previously-deployed Gateway ingress in a different namespace: ingress. Any traffic matched by this route gets sent out-of-namespace, to Service httpbin in namespace team2.

    cat <<EOF | kubectl apply -f -
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
    namespace: team1
    name: httpbin
    spec:
    parentRefs:
    - group: gateway.networking.k8s.io
    kind: Gateway
    namespace: ingress
    name: ingress
    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /httpbin
    filters:
    - type: URLRewrite
    urlRewrite:
    path:
    type: ReplacePrefixMatch
    replacePrefixMatch: /
    backendRefs:
    - group: ""
    kind: Service
    namespace: team2
    name: httpbin
    port: 8000
    EOF
  3. Accept Traffic from Team 1

    To complete our test setup, let's quickly deploy a copy of httpbin to team 2's namespace.

    kubectl apply -n team2 -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml

    This includes a Service also called httpbin, and we already have the HTTPRoute pointing at it. So the only thing that remains is to give that HTTPRoute permission to send traffic over here. This is done with a ReferenceGrant resource, which lives in the target namespace, with the Service which is allowing access to itself. The ReferenceGrant below allows access to any Service in its namespace, from any HTTPRoute in team 1's namespace.

    cat <<EOF | kubectl apply -f -
    apiVersion: gateway.networking.k8s.io/v1beta1
    kind: ReferenceGrant
    metadata:
    namespace: team2
    name: httpbin-route-service
    spec:
    from:
    - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: team1
    to:
    - group: ""
    kind: Service
    EOF
  4. Test Connectivity

    See the instructions in the dedicated gateway article for how to call httpbin.

    note

    In this case, the shared gateway's Service has the following labels: kubectl get service -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-namespace=ingress,gateway.envoyproxy.io/owning-gateway-name=shared-gateway