通过EnvoyFilter资源自定义请求头和响应头

您可以使用EnvoyFilter资源来自定义请求头和响应头。EnvoyFilter资源允许您直接修改Istio代理(Envoy)的配置,从而可以在请求或响应流经代理时添加、删除或修改头信息。

前提条件

部署httpbin应用

步骤一:定义Envoy过滤器模板

ASM支持通过Envoy过滤器模板创建Envoy过滤器。同一个Envoy过滤器模板可以被用来创建多个Envoy过滤器,应用于不同的工作负载和命名空间,从而提高配置的复用性和管理效率。

Envoy过滤器模板YAML示例如下。更多信息,请参见Envoy过滤器(Envoy Filter)CRD说明

展开查看EnvoyFilter YAML

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)
                -- 从请求头中获取一个header key和value。
                local header_key = "x-custom-request-header" -- 您想获取的请求头的key。
                local header_value = request_handle:headers():get(header_key)
                if header_value then
                  -- 写入到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)
                -- 从动态元数据中获取Otel Baggage。
                local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.filters.http.lua") or {}
                local baggage = metadata["otel.baggage"]
                if baggage then
                  -- 把Otel Baggage写入到另一个header中。
                  local new_header_key = "x-custom-response-header" -- 新的响应头的key。
                  response_handle:headers():add(new_header_key, baggage)
                end
              end
  workloadSelector:
    labels:
      app: httpbin
      version: v1

  • 如果您使用的Istio1.9及以上版本,请根据版本替换proxyVersion字段。

  • 如果您使用的Istio为1.8及以下版本,请根据版本替换proxyVersion字段,并且替换上述EnvoyFilter中的envoy.filters.network.http_connection_managerenvoy.http_connection_managerenvoy.filters.http.routerenvoy.routertype.googleapis.com/envoy.extensions.filters.http.lua.v3.Luatype.googleapis.com/envoy.config.filter.http.lua.v2.Lua

步骤二:使用Envoy过滤器模板创建EnvoyFilter资源

Envoy过滤器模板创建成功后,您需要将Envoy过滤器模板绑定至指定的工作负载或命名空间,从而使Envoy过滤器仅在指定工作负载或命名空间上生效。绑定工作负载或命名空间后,将自动创建Envoy过滤器。

创建出来的EnvoyFilter资源定义内容类似如下,它表示向进入工作负载的请求添加一个自定义请求头,并向发出的响应添加一个自定义响应头。以下示例将创建一个名为custom-header-filter的EnvoyFilter资源,并配置了Lua过滤器来添加请求和响应头。它会对所有带有app: my-app标签的工作负载生效。

展开查看EnvoyFilter YAML

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)
                -- 从请求头中获取一个header key和value
                local header_key = "x-custom-request-header" -- 你想获取的请求头的key。
                local header_value = request_handle:headers():get(header_key)
                if header_value then
                  -- 写入到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)
                -- 从动态元数据中获取Otel Baggage。
                local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.filters.http.lua") or {}
                local baggage = metadata["otel.baggage"]
                if baggage then
                  -- 把Otel Baggage写入到另一个header中。
                  local new_header_key = "x-custom-response-header" -- 新的响应头的key。
                  response_handle:headers():add(new_header_key, baggage)
                end
              end
  workloadSelector:
    labels:
      app: httpbin
      version: v1

重要

EnvoyFilter是一种强大但复杂的配置方式,它直接修改了底层Envoy配置。因此,在使用EnvoyFilter时,建议您熟悉Envoy的配置模型,谨慎地修改以避免潜在的副作用。同时,EnvoyFilter的配置可能会随着Istio版本的升级而变动,请注意兼容性问题。

步骤三:通过日志查看自定义请求和响应内容

ASM提供自定义日志格式的功能,支持从请求Header、响应Header、Envoy内置值中取值。具体操作,请参见自定义日志格式

如下所示,新增3个字段用于在日志中显示内容,具体设置如下:

变量名称

类型

变量值

my-x-custom-request-header

请求属性

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

baggage-from-request

请求属性

%REQ(baggage)%

my-x-custom-response-header

响应属性

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

查看httpbin Pod的日志内容,可以看到类似如下内容:

{
    "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",
}