Istio 1.7: Fails to create a wasm filter

I’m trying to create a simple envoy filter with wasm (say, the dummy “add_header” filter that can be found all over the web). I’ve compiled a wasm file, added through a cm / volume, and configured pod annotations to mount it, so far so good.

When I create the EnvoyFilter however, I get this error:

Error adding/updating listener(s) virtualInbound: Unable to create Wasm HTTP filter

Running istioctl proxystatus shows sync issue (status is “STALE (Never Acknowledged)”). The config diff shows huge part missing, basically all the “virtualInbound” listener, which seems to confirm the error message.

So, perhaps I have some mistake in my EnvoyFilter? Here it is:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: ui-examplefilter
  namespace: default
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
            subFilter:
              name: envoy.router
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.wasm
        config:
          config:
            name: example-filter
            rootId: add_header
            configuration: "what ever you want"
            vmConfig:
              vmId: example-filter
              runtime: envoy.wasm.runtime.v8
              code:
                local:
                  filename: /var/local/lib/wasm-filters/optimized.wasm
  workloadSelector:
    labels:
      app: ui
      version: base

Any idea?
I’ve also seen this bug report that perhaps is related: https://github.com/istio/istio/issues/24743 but still trying to get it work with no luck… any help appreciated!

(The more I read the ramifications of that github issue, the more I think it’s related … so does it mean wasm filters doesn’t work at all in 1.7 ?)

Hi,

I am also facing the same issue. Here’s my filter which I applied.

kubectl apply -f-<<EOF
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: details-v1-filterproject
  namespace: default
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
            subFilter:
              name: envoy.router
    patch:
      operation: INSERT_BEFORE
      value:
        config:
          config:
            configuration: filter-project-cm
            name: filter-project
            rootId: my_root_id
            vmConfig:
              code:
                local:
                  filename: /var/local/lib/wasm-filters/filter-project.wasm
              runtime: envoy.wasm.runtime.v8
              vmId: filter-project
        name: envoy.filters.http.wasm
  workloadSelector:
    labels:
      app: details
      version: v1
EOF

And i am getting warnings like (istio-proxy logs which i got from details-v1-xxxxx pod):
1. Warning 1

2020-09-08T04:53:20.318162Z warning envoy config StreamAggregatedResources gRPC config stream closed: 13,

2. Warning 2
|2020-09-08T04:53:20.515961Z|warning|envoy config|gRPC config for type.googleapis.com/envoy.config.listener.v3.Listener rejected: Error adding/updating listener(s) virtualInbound: Unable to create Wasm HTTP filter|

After searching for a long time, I saw a related post in stackoverflow:

Then I modified and applied my EnvoyFilter as shown below:

kubectl apply -f-<<EOF
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: details-v1-filterproject
  namespace: default
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
            subFilter:
              name: envoy.router
    patch:
      operation: INSERT_BEFORE
      value:
        config:
          config:
            name: filter-project
            rootId: my_root_id
            configuration: filter-project-cm            
            vm_config:
              runtime: envoy.wasm.runtime.v8
              vmId: filter-project
              code:
                local:
                  filename: /var/local/lib/wasm-filters/filter-project.wasm
              allow_precompiled: false
        name: envoy.filters.http.wasm
  workloadSelector:
    labels:
      app: details
      version: v1
EOF

Then verified the istio-proxy log. Now only the Warning 1 is listed
i.e. StreamAggregatedResources gRPC config stream closed: 13,

Warning 2 “virtualInbound: Unable to create Wasm HTTP filter” is gone.

Still filter is not working.

In Istio 1.7, replace your configuration. I used

          value:
            config:
              # 'configuration' available at root_context.getConfiguration()
              configuration:
                '@type': type.googleapis.com/google.protobuf.StringValue
                value: "dummy"
              # root_id MUST match registerRootContext() 2nd param
              root_id: heartbeat
              vm_config:
                code:
                  # See https://github.com/envoyproxy/envoy-wasm/blob/febea239e9db174ed0f6c35c690e56b3a483722b/api/envoy/config/core/v3/base.proto
                  local:
                    filename: /var/local/wasm/new-filter.wasm
                runtime: envoy.wasm.runtime.v8
                vm_id: myheartbeat
1 Like

When I changed the ‘configuration:’ I am not able to add the EnvoyFilter.

It shows error:
error: error parsing STDIN: error converting YAML to JSON: yaml: line 26: mapping values are not allowed in this context

I didn’t actually test that with 1.7, I might have tested that against Master (future 1.8).

You can see a working example if you do kubectl -n istio-system get envoyfilter stats-filter-1.7 -o yaml. The last time I got stuck I just started with that and moved my filter pieces over one section at a time.

I am not able to understand the value: "dummy"?. I have removed this and applied the EnvoyFilter and the filter created with no errors. But still it is not working.

I am referring the below links to create a sample filter and deploy it to Istio with the help of proxy-wasm-sdk C++

https://translate.google.com/translate?hl=en&sl=zh-CN&tl=en&u=https%3A%2F%2Fdeveloper.aliyun.com%2Farticle%2F768596&prev=search

https://banzaicloud.com/blog/envoy-wasm-filter/#:~:text=Envoy%20Proxy%20runs%20WASM%20filters,the%20Envoy%20Proxy%20WASM%20SDK.

I am using Istio version 1.7.0 in minikube

Ed: That worked for me, thanks so much!

@annmanvel the value: "dummy" is whatever parameter you want to pass down to the wasm code. I don’t think it’s used in the wasm code you showed, so I guess you can ignore it. In the wasm example I’m using, it’s being caught in an onConfigure hook (I used wasme init as a starting point : https://docs.solo.io/web-assembly-hub/latest/reference/cli/wasme_init )

1 Like

Can you please share the whole EnvoyFilter you used?

Sure, but it’s more or less what @ed.snible was showing, so perhaps the error you have is due to the wasm code, not the EnvoyFilter resource.
Here’s my EnvoyFilter:

  apiVersion: networking.istio.io/v1alpha3
  kind: EnvoyFilter
  metadata:
    name: ui-examplefilter
    namespace: default
  spec:
    configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: SIDECAR_INBOUND
        listener:
          filterChain:
            filter:
              name: envoy.http_connection_manager
              subFilter:
                name: envoy.router
      patch:
        operation: INSERT_BEFORE
        value:
          name: example-filter
          typed_config:
            '@type': type.googleapis.com/udpa.type.v1.TypedStruct
            type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
            value:
              config:
                # 'configuration' available at root_context.getConfiguration()
                configuration:
                  '@type': type.googleapis.com/google.protobuf.StringValue
                  value: my-config
                # root_id MUST match registerRootContext() 2nd param
                root_id: add_header
                vm_config:
                  code:
                    local:
                      filename: /var/local/lib/wasm-filters/optimized.wasm
                  runtime: envoy.wasm.runtime.v8
                  vm_id: my-example
    workloadSelector:
      labels:
        app: ui
        version: base

Beside that, I used wasme for filter scaffolding:

wasme init ./new-filter

Here’s the generated code, in assembly script:

export * from "@solo-io/proxy-runtime/proxy";
import { RootContext, Context, RootContextHelper, ContextHelper, registerRootContext, FilterHeadersStatusValues, stream_context } from "@solo-io/proxy-runtime";

class AddHeaderRoot extends RootContext {
  configuration : string;

  onConfigure(): bool {
    let conf_buffer = super.getConfiguration();
    let result = String.UTF8.decode(conf_buffer);
    this.configuration = result;
    return true;
  }

  createContext(): Context {
    return ContextHelper.wrap(new AddHeader(this));
  }
}

class AddHeader extends Context {
  root_context : AddHeaderRoot;
  constructor(root_context:AddHeaderRoot){
    super();
    this.root_context = root_context;
  }
  onResponseHeaders(a: u32): FilterHeadersStatusValues {
    const root_context = this.root_context;
    if (root_context.configuration == "") {
      stream_context.headers.response.add("hello", "world!");
    } else {
      stream_context.headers.response.add("hello", root_context.configuration);
    }
    return FilterHeadersStatusValues.Continue;
  }
}

registerRootContext(() => { return RootContextHelper.wrap(new AddHeaderRoot()); }, "add_header");

(What it does is just adding a header)

Building:
npm install && npm run asbuild

Send as a config map:

kubectl create cm -n default example-filter --from-file=build/optimized.wasm

Add these pod annotations to mount this configmap

sidecar.istio.io/userVolume: '[{"name":"wasmfilters-dir","configMap": {"name": "example-filter"}}]'
sidecar.istio.io/userVolumeMount: '[{"mountPath":"/var/local/lib/wasm-filters","name":"wasmfilters-dir"}]'

Then, apply the envoyfilter:
kubectl apply -f ./envoyfilter.yml -n default

And finally, curling the service to check that the header is added (it is).

# curl -L -v http://ui.default:8080
[...]
< content-type: text/html;charset=UTF-8
< x-envoy-upstream-service-time: 58
< hello: my-config
* Server envoy is not blacklisted
< server: envoy
[...]

Thanks. I shall go through the same.

I saw the issue https://github.com/istio/istio/issues/23580 contains that how to translate the from the Mixer API into Envoy API for Wasm filter. May similar with this issue. And there is a reference wiki https://github.com/istio/istio/wiki/Enabling-Envoy-Authorization-Service-and-gRPC-Access-Log-Service-With-Mixer