Traffic Extension

TrafficExtension provides a mechanism to extend the functionality provided by the Istio proxy through WebAssembly or Lua filters.

This API supersedes WasmPlugin by providing a unified mechanism for configuring multiple extension types (WebAssembly and Lua) while maintaining consistent targeting and configuration patterns.

The order of execution (as part of Envoy’s filter chain) is determined by phase and priority settings, allowing the configuration of complex interactions between user-supplied WebAssembly or Lua and Istio’s internal filters.

Examples:

AuthN Filter deployed to ingress-gateway that implements an OpenID flow and populates the Authorization header with a JWT to be consumed by Istio AuthN.

apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: openid-connect
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  phase: AUTHN
  wasm:
    url: file:///opt/filters/openid.wasm
    sha256: 1ef0c9a92b0420cf25f7fe5d481b231464bc88f486ca3b9c83ed5cc21d2f6210
    pluginConfig:
      openid_server: authn
      openid_realm: ingress

This is the same as the last example, but using an OCI image.

apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: openid-connect
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  phase: AUTHN
  wasm:
    url: oci://private-registry:5000/openid-connect/openid:latest
    imagePullPolicy: IfNotPresent
    imagePullSecret: private-registry-pull-secret
    pluginConfig:
      openid_server: authn
      openid_realm: ingress

This is the same as the last example, but using VmConfig to configure environment variables in the VM.

apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: openid-connect
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  phase: AUTHN
  wasm:
    url: oci://private-registry:5000/openid-connect/openid:latest
    imagePullPolicy: IfNotPresent
    imagePullSecret: private-registry-pull-secret
    pluginConfig:
      openid_server: authn
      openid_realm: ingress
    vmConfig:
      env:
      - name: POD_NAME
        valueFrom: HOST
      - name: TRUST_DOMAIN
        value: "cluster.local"

This is also the same as the last example, but the Wasm module is pulled via https and updated for each time when this plugin resource is changed.

apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: openid-connect
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  phase: AUTHN
  wasm:
    url: https://private-bucket/filters/openid.wasm
    imagePullPolicy: Always
    pluginConfig:
      openid_server: authn
      openid_realm: ingress
    vmConfig:
      env:
      - name: POD_NAME
        valueFrom: HOST
      - name: TRUST_DOMAIN
        value: "cluster.local"

And a more complex example that deploys three TrafficExtensions and orders them using phase and priority. The (hypothetical) setup is that the openid-connect filter performs an OpenID Connect flow to authenticate the user, writing a signed JWT into the Authorization header of the request, which can be verified by the Istio authn plugin. Then, the acl-check plugin kicks in, passing the JWT to a policy server, which in turn responds with a signed token that contains information about which files and functions of the system are available to the user that was previously authenticated. The acl-check filter writes this token to a header. Finally, the check-header filter verifies the token in that header and makes sure that the token’s contents (the permitted ‘function’) matches its plugin configuration.

The resulting filter chain looks like this: -> openid-connect -> istio.authn -> acl-check -> check-header -> router

apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: openid-connect
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  phase: AUTHN
  wasm:
    url: oci://private-registry:5000/openid-connect/openid:latest
    imagePullPolicy: IfNotPresent
    imagePullSecret: private-registry-pull-secret
    pluginConfig:
      openid_server: authn
      openid_realm: ingress
apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: acl-check
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  phase: AUTHZ
  priority: 1000
  wasm:
    url: oci://private-registry:5000/acl-check/acl:latest
    imagePullPolicy: Always
    imagePullSecret: private-registry-pull-secret
    pluginConfig:
      acl_server: some_server
      set_header: authz_complete
apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: check-header
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  phase: AUTHZ
  priority: 10
  wasm:
    url: oci://private-registry:5000/check-header:latest
    imagePullPolicy: IfNotPresent
    imagePullSecret: private-registry-pull-secret
    pluginConfig:
      read_header: authz_complete
      verification_key: a89gAzxvls0JKAKIJSBnnvvvkIO
      function: read_data

Gateway with Lua filter for conditional header modification:

apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: conditional-header-modifier
  namespace: istio-ingress
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  match:
  - mode: CLIENT
    ports:
    - number: 8080
    - number: 8081
  phase: AUTHN
  priority: 1000
  lua:
    inlineCode: |
      function envoy_on_request(request_handle)
        local domain = "foo.com"
        local headers = request_handle:headers()
        local host = headers:get(":authority")
        local existing_auth_header = headers:get("Authorization")
        if (domain ~= nil and host ~= nil and existing_auth_header == nil and domain == host) then
          local bearer_token = headers:get("cookie"):match("foo")
          if (bearer_token ~= nil) then
            headers:add("Authorization", "Bearer " .. bearer_token)
          end
        end
      end

Waypoint with Lua filter for header count logging:

apiVersion: extensions.istio.io/v1alpha1
kind: TrafficExtension
metadata:
  name: reviews-header-logger
  namespace: default
spec:
  targetRefs:
  - kind: Service
    name: reviews
  match:
  - mode: SERVER
  phase: STATS
  lua:
    inlineCode: |
      function envoy_on_request(request_handle)
        local headers = request_handle:headers()
        local num_headers = 0
        for key, value in pairs(headers) do
          num_headers = num_headers + 1
        end
        request_handle:logInfo("Request to reviews service has " .. num_headers .. " headers")
      end

TrafficExtension

+kubebuilder:validation:XValidation:message=“only one of targetRefs or selector can be set”,rule="(has(self.selector)?1:0)+(has(self.targetRefs)?1:0)<=1" +kubebuilder:validation:XValidation:message=“exactly one of wasm or lua must be set”,rule=“has(self.wasm) != has(self.lua)”

FieldDescription

Criteria used to select the specific set of pods/VMs on which this configuration should be applied. If omitted, this configuration will be applied to all workload instances in the same namespace. If the TrafficExtension is present in the config root namespace, it will be applied to all applicable workloads in any namespace.

At most, only one of selector or targetRefs can be set for a given policy.

The targetRefs specifies a list of resources the policy should be applied to. The targeted resources specified will determine which workloads the policy applies to.

Currently, the following resource attachment types are supported:

  • kind: Gateway with group: gateway.networking.k8s.io in the same namespace.
  • kind: GatewayClass with group: gateway.networking.k8s.io in the root namespace.
  • kind: Service with group: "" or group: "core" in the same namespace. This type is only supported for waypoints.
  • kind: ServiceEntry with group: networking.istio.io in the same namespace.

If not set, the policy is applied as defined by the selector. At most one of the selector and targetRefs can be set.

NOTE: Waypoint proxies are required to use this field for policies to apply; selector policies will be ignored.

Determines where in the filter chain this TrafficExtension is to be injected.

Determines ordering of TrafficExtensions in the same phase. When multiple TrafficExtensions are applied to the same workload in the same phase, they will be applied by priority, in descending order. If priority is not set, or two TrafficExtensions exist with the same value, the ordering will be deterministically derived from the creationTimestamp, then name and namespace of the TrafficExtensions. Defaults to 0.

Specifies the criteria to determine which traffic is passed to TrafficExtension. If a traffic satisfies any of TrafficSelectors, the traffic passes the TrafficExtension.

WebAssembly filter configuration.

Lua filter configuration.

ExecutionPhase

ExecutionPhase specifies where in the filter chain a TrafficExtension is injected.

NameDescription
UNSPECIFIED

Control plane decides where to insert the extension. This will generally be at the end of the filter chain, right before the Router. Do not specify ExecutionPhase if the extension is independent of others.

AUTHN

Insert before Istio authentication filters.

AUTHZ

Insert before Istio authorization filters and after Istio authentication filters.

STATS

Insert before Istio stats filters and after Istio authorization filters.

WasmConfig

WasmConfig configures a WebAssembly filter.

Example:

wasm:
  url: oci://gcr.io/myproject/filter:v1.0.0
  sha256: abc123...
  imagePullPolicy: IfNotPresent
  imagePullSecret: gcr-secret
  pluginName: my-filter
  pluginConfig:
    key1: value1
    key2: value2
  failStrategy: FAIL_CLOSE
  vmConfig:
    env:
    - name: SOME_ENV_VAR
      value: some_value
  type: HTTP
FieldDescription
string
Required

URL of a Wasm module or OCI container. If no scheme is present, defaults to oci://, referencing an OCI image. Other valid schemes are file:// for referencing .wasm module files present locally within the proxy container, and http[s]:// for .wasm module files hosted remotely.

string

SHA256 checksum that will be used to verify Wasm module or OCI container. If the url field already references a SHA256 (using the @sha256: notation), it must match the value of this field. If an OCI image is referenced by tag and this field is set, its checksum will be verified against the contents of this field after pulling.

The pull behaviour to be applied when fetching Wasm module by either OCI image or http/https. Only relevant when referencing Wasm module without any digest, including the digest in OCI image URL or sha256 field in vm_config. Defaults to IfNotPresent, except when an OCI image is referenced in the url and the latest tag is used, in which case Always is the default, mirroring Kubernetes behaviour.

Credentials to use for OCI image pulling. Name of a Kubernetes Secret in the same namespace as the TrafficExtension that contains a Docker pull secret which is to be used to authenticate against the registry when pulling the image.

The configuration that will be passed on to the plugin.

The plugin name to be used in the Envoy configuration (used to be called rootID). Some .wasm modules might require this value to select the Wasm plugin to execute.

Specifies the failure behavior for the plugin due to fatal errors.

Configuration for a Wasm VM. More details can be found here.

Specifies the type of Wasm Extension to be used.

LuaConfig

LuaConfig configures a Lua filter.

Lua filters provide a lightweight alternative to WebAssembly for simple request/response transformations. The Lua code is executed inline within the Envoy proxy.

Example: Simple header manipulation

lua:
  inlineCode: |
    function envoy_on_request(request_handle)
      request_handle:headers():add("x-custom-header", "custom-value")
    end

The Lua script must define one or both of the following functions:

  • envoy_on_request(request_handle): Called when a request is received
  • envoy_on_response(response_handle): Called when a response is received

The request_handle and response_handle provide access to headers, body, metadata, and other request/response attributes. See the Envoy Lua filter documentation for the complete API: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter

FieldDescription
string
Required

The inline Lua code to be executed. The code must be a valid Lua script that defines the appropriate callback functions (envoy_on_request and/or envoy_on_response).

The maximum size is 64KB. For larger or more complex filters, use a WebAssembly filter instead.

TrafficSelector

TrafficSelector provides a mechanism to select a specific traffic flow for which a TrafficExtension will be enabled. When all the sub conditions in the TrafficSelector are satisfied, the traffic will be selected.

FieldDescription

Criteria for selecting traffic by their direction. Note that CLIENT and SERVER are analogous to OUTBOUND and INBOUND, respectively. For the gateway, the field should be CLIENT or CLIENT_AND_SERVER. If not specified, the default value is CLIENT_AND_SERVER.

Criteria for selecting traffic by their destination port. More specifically, for the outbound traffic, the destination port would be the port of the target service. On the other hand, for the inbound traffic, the destination port is the port bound by the server process in the same Pod.

If one of the given ports is matched, this condition is evaluated to true. If not specified, this condition is evaluated to true for any port.

Was this information useful?
Do you have any suggestions for improvement?

Thanks for your feedback!