Dm3Ch
Dm3Ch

Reputation: 761

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:

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

Upvotes: 3

Views: 4734

Answers (2)

Chris
Chris

Reputation: 5623

The problem is your todo #TODO: Understand name compose logic. You need to set this name value to the name of the route of the VirtualService. Also you need to use a typed_per_filter_config with a type LuaPerRoute.

If your VirtualService looks something like that:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews-route
spec:
  hosts:
  - reviews.prod.svc.cluster.local
  http:
  - name: "reviews-v2-routes"
    route:
    - destination:
        host: reviews.prod.svc.cluster.local
        subset: v2
  - name: "reviews-v1-route"
    route:
    - destination:
        host: reviews.prod.svc.cluster.local
        subset: v1

than your EnvoyFilter needs to be setup like this:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: dumb-filter
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: HTTP_ROUTE
    match:
      routeConfiguration:
        vhost:
          route:
            # name from virtual service route that the filter should apply to
            name: reviews-v1-route
    patch:
      operation: MERGE
      value:
        # 'custom' as prefix, can be anything
        name: custom.dumb-filter
        # set lua per route filter
        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:headers():add("dm3ch-test", "dm3ch")
                  response_handle:logErr("Bye Bye.")
                end

Note:

This requires a lua filter that is already applied because the LuaPerRoute will only overwrite an existing one.

Upvotes: 2

Dm3Ch
Dm3Ch

Reputation: 761

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

Upvotes: 6

Related Questions