3

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

Jon Bates
  • 3,055
  • 2
  • 30
  • 48
Dm3Ch
  • 621
  • 1
  • 10
  • 26

2 Answers2

6

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
Jon Bates
  • 3,055
  • 2
  • 30
  • 48
Dm3Ch
  • 621
  • 1
  • 10
  • 26
  • Nice work! I actually never used the `LuaPerRoute` without a "global" lua filter so I wasn't aware of that it's required. If my answer was helpful please consider upvoting. TY! – Chris Oct 19 '21 at 20:37
  • Chris, yep I have just upvoted your answer – Dm3Ch Oct 19 '21 at 21:01
2

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.

Jon Bates
  • 3,055
  • 2
  • 30
  • 48
Chris
  • 5,109
  • 3
  • 19
  • 40
  • TY for answer, tried following, still no success - https://pastebin.com/78nRpPzs – Dm3Ch Oct 19 '21 at 17:58
  • Actually I found that it's shown in proxy-config route output, but still no effect. Here's an output after some experiments: https://pastebin.com/tfAjh1j0 Already tried same but with LuaPerRoute filter, no result too – Dm3Ch Oct 19 '21 at 18:31
  • TY, I've finally found what I've been missing – Dm3Ch Oct 19 '21 at 20:00