Search code examples
istioenvoyproxy

Unable to make lua-based EnvoyFilter to work


I'm trying to make EnvoyFilters work in my installation. For test purposes I'm trying to set lua filter that logs dumb message and adds header to the resonse.

Here's my configuration:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: dumb-filter
  namespace: istio-system
spec:
  # workloadSelector:
  #   labels:
  #     istio: ingressgateway
  configPatches:
  # - applyTo: VIRTUAL_HOST
  - applyTo: HTTP_ROUTE
    match:
      context: GATEWAY
      # context: ANY
      routeConfiguration:
        vhost:
          # name: "<domain>:443"
          route:
            #TODO: Understand name compose logic
            name: https.443.https.geth-dedicated.default
    patch:
      operation: MERGE
      value:
        name: envoy.filters.http.lua
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
          inlineCode: |
            function envoy_on_response(response_handle)
              response_handle:headers():add("dm3ch-test", "dm3ch")
              response_handle:logErr("Bye Bye.")
            end

For now I see no log message or test header in response. I already tried:

  • create EnvoyFilter object in both application and istio-system namespace (where istio gateway pods lives)
  • specifying workloadSelector (I have verified that istio gateway pod have istio: ingressgateway label)
  • changing context from "GATEWAY" to "ANY"
  • changing applyTo to VIRTUAL_HOST and HTTP_ROUTE modes
  • verified that route name is actually https.443.https.geth-dedicated.default using istioctl proxy-config route <gateway_pod> command.
  • adding vhost.name setting and commenting vhost.route.name

Istio version info:

❯ istioctl version
client version: 1.11.4
control plane version: 1.12.0-alpha.1
data plane version: 1.12.0-alpha.1 (1 proxies)

route configuration json:

❯ istioctl proxy-config route istio-ingress-675cb54bc9-5r8cs.istio-system --name https.443.https.geth-dedicated.default -o json
[
    {
        "name": "https.443.https.geth-dedicated.default",
        "virtualHosts": [
            {
                "name": "<domain>:443",
                "domains": [
                    "<domain>",
                    "<domain>:*"
                ],
                "routes": [
                    {
                        "match": {
                            "prefix": "/",
                            "caseSensitive": true
                        },
                        "route": {
                            "cluster": "outbound|8545||geth-dedicated.default.svc.cluster.local",
                            "timeout": "0s",
                            "retryPolicy": {
                                "retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
                                "numRetries": 2,
                                "retryHostPredicate": [
                                    {
                                        "name": "envoy.retry_host_predicates.previous_hosts"
                                    }
                                ],
                                "hostSelectionRetryMaxAttempts": "5",
                                "retriableStatusCodes": [
                                    503
                                ]
                            },
                            "hashPolicy": [
                                {
                                    "connectionProperties": {
                                        "sourceIp": true
                                    }
                                }
                            ],
                            "maxGrpcTimeout": "0s"
                        },
                        "metadata": {
                            "filterMetadata": {
                                "istio": {
                                    "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/geth-dedicated"
                                }
                            }
                        },
                        "decorator": {
                            "operation": "geth-dedicated.default.svc.cluster.local:8545/*"
                        }
                    }
                ],
                "includeRequestAttemptCount": true
            }
        ],
        "validateClusters": false

I would be glad if anyone could consult me what am I doing wrong or how can I better debug why filter is not applied.

P.S. My goal is to invoke custom logic during request/response handling on ingressgateway istio deployment for specific virtualservice only


Solution

  • Chris answer was very useful, but unfortunately it wasn't not full. :(

    Here what I've found:

    • It's not possible to use type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua filter on HTTP_ROUTE (but it's possible to use LuaPerRoute)
    • type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute itself doesn't allow to define new lua filter, it allows only disable existing Lua filter or override it's source code envoy docs

    So to make lua custom logic that is applied to only one http route you need to define "global" Lua filter and override it's code for specific http route using LuaPerRoute filter.

    Here's my manifests that allowed me to make it work:

    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: geth-dedicated
      namespace: default
    spec:
      gateways:
      - geth-dedicated # I'm ommiting gateway creation in this snippet
      hosts:
      - <domain>
      http:
      - match:
        - uri:
            prefix: /
        name: geth-public
        route:
        - destination:
            host: geth-dedicated
            port:
              number: 8545
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: dumb-filter
      namespace: istio-system # Namespace where istio gateway pods are actually running
    spec:
      workloadSelector:
        labels:
          istio: ingressgateway
      configPatches:
      # Patch that creates "global" lua filter that does nothing useful
      - applyTo: HTTP_FILTER
        match:
          listener:
            filterChain:
              filter:
                name: envoy.filters.network.http_connection_manager
                subFilter:
                  name: envoy.filters.http.router
        patch:
          operation: INSERT_BEFORE
          value:
            name: envoy.lua
            typed_config:
              '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
              inlineCode: |
                function envoy_on_request(request_handle)
                  -- Empty lua function
                end
      # Filter for http route that overrides "global" filter lua source code
      - applyTo: HTTP_ROUTE
        match:
          context: GATEWAY
          routeConfiguration:
            vhost:
              route:
                name: geth-public # Corresponds to http[0].name in VirtualService
        patch:
          operation: MERGE
          value:
            name: envoy.lua
            typed_per_filter_config:
              envoy.filters.http.lua:
                '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
                source_code:
                  inline_string: |
                    function envoy_on_response(response_handle)
                      response_handle:logErr("Goodbye my brain.")
                      response_handle:headers():add("dm3ch-test", "dm3ch wins")
                    end