Egress Gateways Made Easy

August 19, 2024
John Howard

For years, Istio has provided an option to deploy an “Egress Gateway“. This powerful mechanism allows directing outbound traffic through a Gateway, where various policies can be uniformly enforced. This includes authorization, auditing, observability, and more.

While quite powerful, setting up an egress gateway has historically been extremely complex. Even just the most basic case of directing traffic for a certain domain through the egress gateway requires carefully sequencing 5 different Istio objects together (see documentation), and that is before we add any advanced policies we want to apply to the traffic!

With Istio’s ambient mode and Gloo Mesh, the configuration required to setup an egress gateway is now much easier to implement and provides better functionality and management of traffic across a mesh. Let’s take a deeper look in this blog.

Image 1: Configuration required, for each destination, to setup an Egress Gateway

Istio’s ambient mode and Gloo Mesh makes it easy

With ambient mode, controlling egress traffic is a breeze. In ambient mode, the classic sidecar proxy is removed, in favor of standalone “waypoint” proxies which can handle traffic on behalf of a service. This looks like:

Services can bind to a waypoint, and Istio will automatically send all traffic to those services through the waypoint.

The trick here is that we can actually bind external domains (represented by ServiceEntry) to a waypoint as well, just like we can for our internal services. Just like with Services, traffic to these domains will automatically be directed through the waypoint proxy.

Image 2: Configuration required, for each destination, to setup an Egress Gateway with Istio ambient mode

This dramatically simplifies the configuration of egress gateways, while still allowing the same rich functionality. Traffic observability can then be visualized via Gloo Mesh.

Trying it out

First, on a cluster with Istio ambient mode installed, I’ll deploy a test application and enroll the namespace into the ambient mesh:


$ kubectl apply -f shell.yaml 
$ kubectl label namespace default istio.io/dataplane-mode=ambient


Out of the box with Istio’s ambient mode, I get automatic mutual TLS when communicating within the mesh. Extending Istio’s ambient mode, Gloo Mesh adds HTTP telemetry.

We can see from our test application, we can still send outbound traffic like normal:


$ curl httpbin.org/get
{
  "headers": {
    "Host": "httpbin.org",
  },
  "url": "http://httpbin.org/get"
}

Our goal is to make this traffic go through an egress gateway, so we can uniformly apply policies and access control to this traffic. With Istio’s ambient mode, this is much easier than it was with sidecars.

First, we just need to deploy a waypoint proxy to serve as our egress gateway. We only need to do this once, and can share it for each additional domain we want to capture traffic for.


$ kubectl create namespace istio-egress
$ istioctl waypoint apply --enroll-namespace --namespace istio-egress

Next, I’ll create a ServiceEntry for httpbin.org:


apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: httpbin.org
  namespace: istio-egress
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS

This is all we need! Because we deployed the waypoint with –enroll-namespace, all services in the istio-egress namespace will automatically be attached to the waypoint. If we wanted to opt-in on a per-service basis, we could explicitly label each Service/ServiceEntry with istio.io/use-waypoint: waypoint.

Now that the ServiceEntry is attached to the waypoint proxy, the previous call will automatically traverse the waypoint proxy we deployed without changes to our application, all secured by mutual TLS:


$curl httpbin.org/get -v
...
* Request completely sent off
< HTTP/1.1 200 OK
< server: istio-envoy
...

{
  ...
}

We can now also observe the traffic, with logs, traces, and metrics. For instance, inspecting the waypoint logs will show the request we just made:


$ kubectl logs waypoint-6c6b888f4f-wmnr2 --tail=1
[2024-07-18T16:13:58.198Z] "GET /get HTTP/1.1" 200 - via_upstream - "-" 0 252 260 259 "-" 
"curl/8.7.1" "8d80cec6-8fc1-40de-8bc7-19716f173bbd" "httpbin.org" "3.234.9.11:80" inbound-vip|80|http|httpbin.org; 10.244.0.24:41160 240.240.0.2:80 10.244.0.18:41548 - default

Policy Enforcement

One of the most powerful features of an egress gateway is the ability to enforce access control policies for all outbound traffic. These can range from simple domain allowlists, to complex policies such as data loss prevention.

In this example, we will limit low-privilege applications to only to the /get endpoint, but allow a high-privilege application to access all of httpbin.org. To do this, we apply an AuthorizationPolicy attached to the ServiceEntry we created.


apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: httpbin
  namespace: istio-egress
spec:
  targetRefs:
  - kind: ServiceEntry
    group: networking.istio.io
    name: httpbin.org
  action: ALLOW
  rules:
  # Our admin application can access anything
  - from:
    - source:
        principals: ["cluster.local/ns/default/sa/admin"]
  # Everything else is only allowed to access /get
  - to:
    - operation:
        methods: ["GET"]
        paths: ["/get"]

Now from my low privilege application, we can see our POST requests are denied:


$ curl httpbin.org/get
{
  "headers": {
    "Host": "httpbin.org",
  },
  "url": "http://httpbin.org/get"
}
$ curl -X POST httpbin.org/post
RBAC: access denied

From my admin application, the POST request succeeds as expected:


$ curl httpbin.org/post -X POST
{
  "headers": {
    "Host": "httpbin.org",
  },
  "url": "http://httpbin.org/post"
}

TLS Origination

Another common policy to apply at an egress gateway is TLS origination. In the above examples, while all of our communication inside the mesh is automatically encrypted by Istio, once the request leaves the cluster it is in plaintext over the public internet – a major security issue.

Fortunately, we can add TLS at the egress waypoint before the request leaves the cluster. This is especially useful when communicating with services that require client authentication, where access can be controlled in a centralized manner; in this example, however, we will just do simple TLS.

This just requires a small change to the ServiceEntry and a DestinationRule configuration


apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: httpbin.org
  namespace: istio-egress
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP
    targetPort: 443 # New: send traffic originally for port 80 to port 443
  resolution: DNS
---
# New: add TLS to requests to `httpbin.org`
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: httpbin.org-tls
  namespace: istio-egress
spec:
  host: httpbin.org
  trafficPolicy:
    tls:
      mode: SIMPLE

Now all of our requests are automatically upgraded to HTTPS, without any changes to our application:


$ curl httpbin.org/get
{
  "headers": {
    "Host": "httpbin.org",
  },
  "url": "https://httpbin.org/get"
}

Learn More About Gloo Mesh

Managing egress traffic and applying usage, security, and other organizational policies is critical in most enterprises. Additionally, doing this is fairly fragmented. With Istio and Gloo Mesh, we make configuring traffic, whether it’s ingress, east/west, or egress traffic, consistent throughout. Other things you should think about include how this fits in with defense in depth techniques like CNI / Network Policy.

At Solo.io we are working with customers deploying mesh technologies to consistently manage application traffic in all directions. We’ve built Gloo Mesh to simplify this and avoid the pitfalls that may arise. Istio ambient mode represents further simplification and is available in Gloo Mesh. Learn more about Gloo Mesh today.

Cloud connectivity done right