Integrating Envoy Gateway as an Ingress Gateway in Istio Service Mesh

This article describes how to integrate Envoy Gateway as an ingress gateway in the Istio service mesh to enhance application security and accessibility.

Copyright Notice
This is an original article by Jimmy Song. You may repost it, but please credit this source: https://jimmysong.io/en/blog/envoy-gateway-integration-istio-mesh/
Click to show the outline

Istio provides robust and flexible support for ingress gateways, utilizing the Envoy proxy in its sidecar mode. While Istio focuses on managing communication between services within a cluster, Envoy Gateway is designed to expose applications to the external world, handle user requests, and support advanced features such as OIDC single sign-on. By combining the capabilities of the Istio service mesh with the advanced gateway features of Envoy Gateway, overall application accessibility and security can be enhanced.

The following diagram illustrates the traffic path of the ingress gateway in the Istio mesh.

image
Istio Ingress Gateway Traffic Path

The next diagram shows how traffic flows from the edge of the Istio mesh into the internal network after introducing the Envoy Gateway.

image
Traffic Path After Introducing Envoy Gateway

Preparing for Interoperability between Envoy Gateway and Istio

To use Envoy Gateway as an ingress gateway for Istio, consider the following key points:

  • Avoid enabling the Ingress Gateway during Istio installation. We’ll manually install and configure Envoy Gateway as the ingress gateway.
  • Since both Istio and Envoy Gateway use Envoy as a proxy, ensure Istio injects the Envoy sidecar into the Envoy Gateway’s gateway pods to allow secure communication with Istio’s data plane.
  • Configure the routing type of the Envoy proxy created by Envoy Gateway as Service instead of Endpoint to ensure proper routing.

Follow the quick start documentation to install Envoy Gateway. Label the namespace of the Envoy Gateway to ensure the data plane gets the Istio sidecar injection:

kubectl label namespace envoy-gateway-system --overwrite=true istio-injection=enabled

Configure the Envoy Gateway’s sidecar to not intercept incoming gateway traffic. The injected sidecar ensures that the components of Envoy Gateway and its created proxies are included in the Istio mesh and mount the correct certificates for secure communication.

spec:
  ports:
  - port: 18000
    appProtocol: tls

Apply the patch:

kubectl patch service -n envoy-gateway-system envoy-gateway --type strategic --patch-file control-plane-tls.yaml

Configure Envoy Gateway to not intercept inbound traffic:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: data-plane-sidecars
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        pod:
          annotations:
            traffic.sidecar.istio.io/includeInboundPorts: ""
  routingType: Service

Apply the configuration:

kubectl apply -f teg-sidecars-no-inbound.yaml

Modify the GatewayClass configuration to apply the sidecar configuration to all EnvoyProxy in the Envoy Gateway data plane:

spec:
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    namespace: envoy-gateway-system
    name: data-plane-sidecars

Apply the patch:

kubectl patch gatewayclass teg --patch-file gtwcls-use-envoyproxy.yaml --type merge

Installing Istio

Deploy Istio using the minimal profile to avoid deploying the Ingress Gateway:

istioctl install --set profile=minimal -y

Restarting the Envoy Gateway Control Plane

With Istio’s sidecar injection ready, restart all Envoy Gateway control plane pods:

for d in envoy-gateway envoy-ratelimit teg-envoy-gateway teg-redis;
	do kubectl rollout restart deployment -n envoy-gateway-system $d; done

Deploying a Test Application

Deploy test applications after installing Istio to ensure they also receive sidecar injections:

kubectl create namespace httpbin
kubectl label namespace httpbin --overwrite=true istio-injection=enabled
kubectl apply -n httpbin -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml

Configuring Envoy Gateway

Now configure the Envoy Gateway to handle edge traffic:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: apps
  namespace: httpbin
spec:
  gatewayClassName: teg
  listeners:
  - name: http
    protocol: HTTP
    port: 80

Apply the configuration:

kubectl apply -f apps-gateway.yaml

Deploy the application gateway, which includes the following containers:

  • istio-init: Injected by Istio to modify pod iptables.
  • envoy: Controlled by Envoy Gateway, acting as the ingress gateway.
  • istio-proxy: Injected by Istio, responsible for communication with internal cluster pods.
  • shutdown-manager: Controlled by Envoy Gateway, responsible for pod lifecycle management.

Create an HTTP route:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: httpbin
spec:
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: apps
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /httpbin/
    filters:
    - type: URLRewrite
      urlRewrite:
        hostname: httpbin.httpbin.svc.cluster.local
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - kind: Service
      name: httpbin
      port: 8000

Apply the route configuration:

kubectl apply -f httpbin-route.yaml

Sending a Test Request

Get the load balancer IP address of the gateway and send a test request:

export GATEWAY_URL=$(kubectl get svc -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-name=apps -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')
curl -v -H Host:www.example.com http://$GATEWAY_URL/httpbin/get

You should see a correct response from the httpbin service:

*   Trying 34.41.0.90:80...
* Connected to 34.41.0.90 (34.41.0.90) port 80
> GET /httpbin/get HTTP/1.1
> Host:www.example.com
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< server: envoy
< date: Wed, 31 Jul 2024 08:21:58 GMT
< content-type: application/json
< content-length: 282
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 11
<
{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Host": "www.example.com",
    "User-Agent": "curl/8.7.1",
    "X-Envoy-Attempt-Count": "1",
    "X-Envoy-External-Address": "123.120.227.173"
  },
  "origin": "123.120.227.173",
  "url": "http://www.example.com/get"
}
* Connection #0 to host 34.41.0.90 left intact

Enabling Strict mTLS

Enable strict mTLS by applying the following configuration:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: "default"
  namespace: "istio-system"
spec:
  mtls:
    mode: STRICT

Apply the configuration:

kubectl apply -f strict-mtls.yaml

Enabling TLS for the Gateway

Create the root certificate and private key for service signing:

mkdir example_certs
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs/example.com.key -out example_certs/example.com.crt

Create the certificate and private key for www.example.com:

openssl req -out example_certs/www.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs/www.example.com.key -subj "/CN=www.example.com/O=www organization"
openssl x509 -req -sha256 -days 365 -CA example_certs/example.com.crt -CAkey example_certs/example.com.key -set_serial 0 -in example_certs/www.example.com.csr -out example_certs/www.example.com.crt

Create a secret for the ingress gateway:

kubectl create -n httpbin secret tls httpbin-credential --key=example_certs/www.example.com.key --cert=example_certs/www.example.com.crt

Configure the ingress gateway:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: apps
  namespace: httpbin
spec:
  gatewayClassName: teg
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - name: httpbin-credential

Apply the configuration:

kubectl apply -f tls-apps-gateway.yaml

Send a test request:

curl -v -H Host:www.example.com --resolve "www.example.com:443:$GATEWAY_URL" --cacert example_certs/example.com.crt "https://www.example.com:443/httpbin/get"

You should be able to access the httpbin service within the mesh via HTTPS.

Conclusion

By integrating Envoy Gateway as an ingress gateway in your Istio service mesh, you can leverage the best of both worlds: Istio’s robust service mesh capabilities and Envoy Gateway’s advanced gateway features. This setup enhances the security, scalability, and flexibility of your applications, providing a seamless and secure user experience. With careful configuration and the right tools, managing traffic in and out of your service mesh becomes more efficient and effective, ensuring your applications are always accessible and secure.

Last updated on Sep 16, 2024