All Products
Search
Document Center

Alibaba Cloud Service Mesh:Customize request headers and response headers by using the EnvoyFilter CRD

Last Updated:Feb 28, 2024

You can customize request headers and response headers by using the EnvoyFilter CustomResourceDefinition (CRD). The EnvoyFilter CRD allows you to directly modify the configuration of an Istio proxy (Envoy). This way, you can add, delete, or modify request or response headers when a request or response flows through the proxy.

Prerequisites

The HTTPBin application is deployed. For more information, see Deploy the HTTPBin application.

Step 1: Define an Envoy filter template

ASM allows you to create an Envoy filter by using an Envoy filter template. The same Envoy filter template can be used to create multiple Envoy filters, and these Envoy filters can be applied to different workloads and namespaces. This makes configurations reusable and improves management efficiency.

The following YAML code provides an example of an Envoy filter template. For more information, see Envoy Filter.

Expand to view the YAML code of an Envoy filter template

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: custom-header-filter
  namespace: my-namespace
spec:
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: SIDECAR_INBOUND
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
        proxy:
          proxyVersion: ^1\.20.*
      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)
                -- Obtain a header key and a value from the request header. 
                local header_key = "x-custom-request-header" -- The key of the request header you want to obtain. 
                local header_value = request_handle:headers():get(header_key)
                if header_value then
                  -- Write data to Otel Baggage.
                  local baggage = header_key .. "=" .. header_value
                  request_handle:headers():add("baggage", baggage)
                  request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.lua", "otel.baggage", baggage)
                end
              end

              function envoy_on_response(response_handle)
                -- Obtain the Otel Baggage from the dynamic metadata. 
                local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.filters.http.lua") or {}
                local baggage = metadata["otel.baggage"]
                if baggage then
                  -- Write the Otel Baggage to another header. 
                  local new_header_key = "x-custom-response-header" -- The key of the new response header. 
                  response_handle:headers():add(new_header_key, baggage)
                end
              end
  workloadSelector:
    labels:
      app: httpbin
      version: v1

  • If Istio 1.9 or later is used, replace the value of the proxyVersion field with the Istio version.

  • If Istio 1.8 or an earlier version is used, replace the value of the proxyVersion field with the Istio version. In addition, replace envoy.filters.network.http_connection_manager with envoy.http_connection_manager, envoy.filters.http.router with envoy.router, and type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua with type.googleapis.com/envoy.config.filter.http.lua.v2.Lua in the preceding YAML code.

Step 2: Create an Envoy filter by using the Envoy filter template

After you create an Envoy filter template, you must bind the Envoy filter template to workloads or namespaces. This way, the Envoy filters created based on this template take effects only on the specified workloads or namespaces. After you bind the Envoy filter template to workloads or namespaces, ASM automatically creates Envoy filters by using the template.

The created Envoy filter adds a custom request header to a request that enters the workload and adds a custom response header to a response that will be sent by the workload. The following example creates an Envoy filter named custom-header-filter and configures a Lua filter to add request headers and response headers. The Envoy filter takes effect for all workloads with the app: my-app label.

Expand to view the YAML code of an Envoy filter

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: custom-header-filter
  namespace: my-namespace
spec:
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: SIDECAR_INBOUND
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
        proxy:
          proxyVersion: ^1\.20.*
      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)
                -- Obtain a header key and a value from the request header.
                local header_key = "x-custom-request-header" -- the key of the request header you want to obtain. 
                local header_value = request_handle:headers():get(header_key)
                if header_value then
                  -- Write data to Otel Baggage. 
                  local baggage = header_key .. "=" .. header_value
                  request_handle:headers():add("baggage", baggage)
                  request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.lua", "otel.baggage", baggage)
                end
              end

              function envoy_on_response(response_handle)
                -- Obtain the Otel Baggage from the dynamic metadata. 
                local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.filters.http.lua") or {}
                local baggage = metadata["otel.baggage"]
                if baggage then
                  -- Write the Otel Baggage to another header. 
                  local new_header_key = "x-custom-response-header" -- The key of the new response header. 
                  response_handle:headers():add(new_header_key, baggage)
                end
              end
  workloadSelector:
    labels:
      app: httpbin
      version: v1

Important

EnvoyFilter is a powerful but complex configuration method. It directly modifies the underlying Envoy configurations. Therefore, when you use EnvoyFilter, we recommend that you be familiar with the configuration model of Envoy and modify it with caution to avoid potential errors. Meanwhile, the configuration of EnvoyFilter may change with the upgrade of Istio versions. Pay attention to compatibility issues.

Step 3: View custom request headers and response headers in access logs

ASM allows you to customize log formats. The custom expressions of access logs can obtain values from request headers, response headers, and Envoy built-in values. For more information, see Customize the format of access logs.

The following table lists three new fields, which are used to display content in access logs.

Field

Type

Value

my-x-custom-request-header

Request attribute

%REQ(x-custom-request-header)%

baggage-from-request

Request attribute

%REQ(baggage)%

my-x-custom-response-header

Response attribute

%RESP(x-custom-response-header)%

Check the access logs of the HTTPBin pod and you can see content similar to the following:

{
    "bytes_received": "0",
    "bytes_sent": "490",
    "duration": "1",
    "istio_policy_status": "-",
    "method": "GET",
    "path": "/headers",
    "protocol": "HTTP/1.1",
    "response_code": "200",
    "response_flags": "-",
    "my-x-custom-request-header": "xxx",
    "baggage-from-request": "x-custom-request-header=xxx",
    "my-x-custom-response-header": "x-custom-request-header=xxx",
}