Open-Source Wikis

/

Istio

/

Features

/

Wasm and EnvoyFilter

istio/istio

Wasm and EnvoyFilter

Active contributors: hzxuzhonghu, howardjohn, ramaraochavali

What users do

Extend the data plane beyond what stable Istio APIs offer:

  • WasmPlugin — load a Wasm binary into the proxy; runs as an HTTP filter.
  • EnvoyFilter — patch arbitrary Envoy config with surgical precision.

These are the escape hatches. The recommendation is "use stable APIs first; reach for these only when the stable APIs don't cover your case."

WasmPlugin

The WasmPlugin CRD references a Wasm binary by URL or OCI image and configures it for a workload set:

apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
  name: my-filter
  namespace: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  url: oci://example.com/my-filter:v1
  phase: AUTHN
  pluginConfig:
    foo: bar

How it gets to the proxy

The Wasm binary is not served inline through xDS. Instead:

  1. The WasmPlugin reconciles into a EnvoyFilter-equivalent ECDS (Extension Config Discovery Service) entry.
  2. ECDS resources reference a URI (http://, oci://, file://).
  3. Istiod compiles the listener with a placeholder filter that loads its config from ECDS.
  4. The proxy's pilot-agent fetches the actual binary from the URI (the agent's "Wasm fetch loop") and stores it on disk.
  5. Envoy loads the binary from disk.

This indirection avoids streaming MB-sized binaries through xDS and lets the proxy cache them across restarts.

Where it's implemented

  • Schema: extensions/v1alpha1/wasm.proto.
  • Reconciliation: pilot/pkg/networking/core/extension/wasmplugin.go. Builds the ECDS entries.
  • xDS surface: pilot/pkg/xds/ecds.go. The ECDS generator.
  • Agent fetcher: pkg/wasm/. Fetch, verify, cache.
  • Image format: OCI artifact format with the application/vnd.wasm.config.v1+json manifest type. Compatible with Docker registries.

Phases

WasmPlugin.spec.phase:

Phase When the filter runs
AUTHN Before AuthN filters (rare)
AUTHZ Between AuthN and AuthZ
STATS Before stats — useful for custom metrics
UNSPECIFIED_PHASE Just before the router

Order within a phase is by priority (higher → earlier).

Trade-offs

  • Wasm runtime cost — WAVM/V8 compile is slow at startup. Each Envoy worker thread compiles independently. Long startup tails on cold restarts.
  • Memory — each thread holds its own Wasm VM instance.
  • Debugging — Envoy's Wasm logs are noisy. Print-line debugging from a Wasm plugin requires the host's logging via the proxy_log ABI.
  • API churn — proxy-wasm spec is stable, but tooling (tinygo, Rust SDK) evolves quickly.

EnvoyFilter

The bare-metal escape hatch. EnvoyFilter lets users patch any aspect of the Envoy config Istio generates.

Anatomy of a patch

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: custom-headers
spec:
  workloadSelector:
    labels:
      app: my-app
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: SIDECAR_INBOUND
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.lua
          typed_config:
            '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
            inline_code: |
              function envoy_on_response(handle)
                handle:headers():add("x-custom", "value")
              end

How it's applied

The patcher is in pilot/pkg/networking/core/envoyfilter/. After Istio builds the standard config, it walks each EnvoyFilter and applies its patches:

  • applyTo selects what type of node (LISTENER, CLUSTER, ROUTE, FILTER_CHAIN, HTTP_FILTER, NETWORK_FILTER, ROUTE_CONFIGURATION, VIRTUAL_HOST, BOOTSTRAP).
  • match.context filters by proxy role (SIDECAR_INBOUND, SIDECAR_OUTBOUND, GATEWAY, ANY).
  • match.proxy.proxyVersion filters by Envoy version regex.
  • patch.operation is one of MERGE, ADD, REMOVE, INSERT_BEFORE, INSERT_AFTER, INSERT_FIRST, REPLACE.

Trade-offs

  • Tightly coupled to Envoy version — if the upstream Envoy proto for your filter changes between Istio releases, your EnvoyFilter silently breaks. Test against new Istio versions.
  • No validation of filter contents — Istio doesn't know your filter's schema. A typo lands as a runtime config error on the proxy, not a CRD validation error.
  • Privileged useEnvoyFilter can patch the cluster manager and bootstrap, which can break istiod ↔ proxy communication. Limit who can apply them.

When to use which

Need Use
HTTP filter logic in a high-level language WasmPlugin
Custom metrics with bespoke dimensions WasmPlugin (Stats phase)
Patch in a known Envoy filter (Lua, ext_authz, RBAC variants) EnvoyFilter
Tweak listener / cluster behavior EnvoyFilter
Bootstrap / startup config tweak EnvoyFilter, applyTo BOOTSTRAP
Anything covered by Telemetry, AuthorizationPolicy, RequestAuthentication, VirtualService, DestinationRule, Gateway, Sidecar The stable API

Key source files

File Purpose
pilot/pkg/networking/core/extension/wasmplugin.go WasmPlugin → ECDS
pilot/pkg/xds/ecds.go ECDS generator
pkg/wasm/ Agent-side Wasm fetcher/cache
pilot/pkg/networking/core/envoyfilter/patch.go EnvoyFilter applier
pilot/pkg/networking/core/envoyfilter/cluster_patch.go Cluster patches
pilot/pkg/networking/core/envoyfilter/listener_patch.go Listener patches
pilot/pkg/networking/core/envoyfilter/route_patch.go Route patches
pilot/pkg/model/envoyfilter.go EnvoyFilter resolution

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Wasm and EnvoyFilter – Istio wiki | Factory