Envoy Gateway In Production

- kubernetes envoy proxy gateway

Previous post was about discovering a new offer in the Kubernetes Gateway space Envoy Gateway, in this one I’ll share some notes to make it to production.

Envoy Gateway is still rough on the edges, but remember Gateway is mainly a Kubernetes API frontend to provision Envoy Proxy, Envoy Proxy configuration can still be patched to enable a specific feature.

Enable Patching

Patching is not enabled by default, create this configmap if you need it:

apiVersion: v1
kind: ConfigMap
metadata:
  name: envoy-gateway-config
  namespace: envoy-gateway-system
data:
  envoy-gateway.yaml: |
    apiVersion: gateway.envoyproxy.io/v1alpha1
    kind: EnvoyGateway
    provider:
      type: Kubernetes
    gateway:
      controllerName: gateway.envoyproxy.io/gatewayclass-controller
    extensionApis:
      enableEnvoyPatchPolicy: true    

Compression

This very basic functionality is not enabled by default with 0.6.0 Gateway.

This is a patch for an HTTPS listener, the path to patch is different for HTTP "/default_filter_chain/filters/0/typed_config/http_filters/0")

Example patch to enable compression:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
  name: compressor-patch-policy
  namespace: envoy-gateway-system
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: eg
    namespace: envoy-gateway-system
  type: JSONPatch
  jsonPatches:
    - type: "type.googleapis.com/envoy.config.listener.v3.Listener"
      name: envoy-gateway-system/eg/https
      operation:
        op: add
        path: "/filter_chains/0/filters/0/typed_config/http_filters/0"
        value:
          name: "envoy.filters.http.compressor"
          typed_config:
            "@type": "type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor"
            compressor_library:
              name: text_optimized
              typed_config:
                "@type": "type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip"
                compression_level: BEST_SPEED
                compression_strategy: DEFAULT_STRATEGY
                memory_level: 8
                window_bits: 15
                chunk_size: 4096
            response_direction_config:
              remove_accept_encoding_header: true
              common_config:
                enabled:
                  default_value: true
                  runtime_key: response_direction_config_enabled
                content_type:
                  - "application/javascript"
                  - "application/json"
                  - "application/x-web-app-manifest+json"
                  - "application/xhtml+xml"
                  - "application/xml"
                  - "font/opentype"
                  - "image/svg+xml"
                  - "image/x-icon"
                  - "text/css"
                  - "text/html"
                  - "text/plain"
                  - "text/xml"
                min_content_length: 60
            request_direction_config:
              common_config:
                enabled:
                  default_value: false
                  runtime_key: request_direction_config_enabled

You should see a response with an content-encoding: gzip header for an HTTP/1.1 request with a Accept-Encoding: deflate, gzip header.

It should be easier in future releases with this PR.

Logging

To enable JSON logging:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: custom-proxy-config
  namespace: envoy-gateway-system
spec:
  telemetry:
    accessLog:
      settings:
        - format:
            type: JSON
            json:
              authority: "%REQ(:AUTHORITY)%"
              bytes_received: "%BYTES_RECEIVED%"
              bytes_sent: "%BYTES_SENT%"
              duration: "%DURATION%"
              method: "%REQ(:METHOD)%"
              path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
              status: "%RESPONSE_CODE%"
              upstream_host: "%UPSTREAM_HOST%"
              user_agent: "%REQ(USER-AGENT)%"
              x_forwarded_for: "%REQ(X-FORWARDED-FOR)%"
              x_request_id: "%REQ(X-REQUEST-ID)%"
              cluster: "%UPSTREAM_CLUSTER%"
          sinks:
            - type: File
              file:
                path: /dev/stdout

Note the cluster field, it will really help debugging your installation, it will generate the path to the actual container: "cluster":"httproute/mycontainer/mycontainer-backend-open-0/rule/0"

CORS

Enabling CORS is well documented but one detail was missing, if for some reasons the routes are matching on methods (GET,PUT …), Envoy Proy won’t be able to respond to preflights HTTP requests, since they are using OPTIONS:

The documentation has been updated to reflect that: The targeted HTTPRoute or the HTTPRoutes that the targeted Gateway routes to must allow the OPTIONS method for the CORS filter to work. Otherwise, the OPTIONS request won’t match the routes and the CORS filter won’t be invoked.

Regex Match

By default, Envoy Proxy is using a very small limit for the regex complexity, it means a very simple regex may be rejected.
Envoy Gateway (as of 0.6.0) does not increase that value, you should probably do it by changing the default bootstrap config.

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: custom-proxy-config
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        replicas: 1
  bootstrap:
    type: Merge
    value: |
      layered_runtime:
        layers:
        - name: "static-runtime"
          static_layer:
            re2.max_program_size.error_level: 1000      

The error message would be something like this:

[2024-01-30 17:38:22.851][1][warning][config] [source/extensions/config_subscription/grpc/grpc_subscription_impl.cc:138] gRPC config for type.googleapis.com/envoy.config.route.v3.RouteConfiguration rejected: regex '^\/v1\/myapi\/([0-9a-z-]{36})\/blah$' RE2 program size of 120 > max program size of 100 set for the error level threshold. Increase configured max program size if necessary.

It could be a huge issue, if for some reasons Envoy is restarting, it will respond with 404 for all the routes until the route is removed or the threshold is increased.

Tracking the issue.

External Authorization (Authz)

I’m running a 0.6.0 fork with gRPC authz enabled.

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: myapp-back-policy
  namespace: myapp
spec:
  extAuthz:
    grpcURI: http://ext-auth.envoy-gateway-system.svc.cluster.local:10003
  jwt:
    providers:
    - audiences:
      - https://my.domain.net
      issuer: https://id.issuer-domain.net
      name: auth0
      remoteJWKS:
        uri: https://myid.issuer-domain.net/.well-known/jwks.json
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: myapp-back-authz-0

Note that in 0.6.0, after the JWT filter the payload was not forwarded anymore, it has been changed since (my fork includes that change).

An Extauthz solution by the Gateway team is in development and should land soon.

OIDC OpenID

Not in 0.6.0 but already merged, OIDC is supported to protect an HTTPRoute via a SecurityPolicy.

With Azure (but similar with other providers), register an application, get the client ID (application ID) and the client secret, the issuer URL is built with the UUID from “Directory (tenant) ID”. Set the Redirect URL to this form: https://myapp.mydomain.com/oauth2/callback

On Envoy Gateway side, apply the SecurityPolicy:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: myapp-oidc-policy
  namespace: myapp
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: myapp-public-route
  oidc:
    provider:
      issuer: "https://login.microsoftonline.com/4410211c-15ba-427f-bdf7-cd04eb1aa75c/v2.0"
    clientID: "4e550576-cd82-4566-a00c-4c1fee89f808"
    clientSecret:
      name: "myapp-oidc-client-secret"

Envoy Gateway will try to discover most of the configuration (Authorization & Token Endpoints) from the provider’s Well-Known Configuration Endpoint.

Conclusion

Most if not any of those required extra configurations will probably disappear with the 1.0 release.
Gateway is flexible enough to provide those already.

For now using the main trunk is surely not for everyone, but 1.0.0 will definetly be prod ready.