配置 Envoy Gateway 扩展

概述

Envoy Gateway 支持通过 EnvoyExtensionPolicy 进行流量扩展。你可以将扩展策略附加到 GatewayHTTPRoute,在不修改应用代码的情况下,在 Envoy 数据平面中运行自定义逻辑。

本文档介绍三种扩展机制:

扩展机制典型使用场景配置资源
LuaFilter轻量级请求或响应处理,例如添加请求头或简单路由逻辑EnvoyExtensionPolicy.spec.lua
Wasm编译为 WebAssembly 模块的可移植扩展逻辑EnvoyExtensionPolicy.spec.wasm
Dynamic Module从共享对象文件加载的原生 Envoy 扩展,例如 WAF 模块EnvoyProxy.spec.dynamicModulesEnvoyExtensionPolicy.spec.dynamicModule

先决条件

在配置扩展之前,请确保你已完成以下任务:

  1. 安装或升级 Envoy Gateway 到支持所需扩展字段的版本。本文档中的 Dynamic Module 示例需要 Envoy Gateway 1.8 或更高版本。
  2. 配置 EnvoyGatewayCtl
  3. 配置 Gateway
  4. 配置 Route
  5. 准备一个后端 Service 和一个将流量转发到该 Service 的 HTTPRoute

本文档中的示例使用以下资源名称。请将其替换为你环境中的名称。

资源示例值
Namespacegateway-demo
Gatewayexample-gateway
Gateway 监听器http
HTTPRouteexample-route
主机名www.example.com
NOTE

扩展制品必须可被 Envoy 数据平面访问。对于 Wasm 模块,请使用 Envoy pods 可以访问的 HTTP 或 OCI 源。对于 Dynamic Modules,请确保在 Envoy 启动之前已将共享对象文件挂载到 Envoy 容器中。

配置 LuaFilter 扩展

使用 LuaFilter 进行轻量级请求或响应处理。以下示例将一个 Lua 脚本附加到 example-route,并添加 x-gwext-lua: enabled 响应头。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
  name: lua-header-policy
  namespace: gateway-demo
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: example-route
  lua:
    - type: Inline
      inline: |
        function envoy_on_response(response_handle)
          response_handle:headers():add("x-gwext-lua", "enabled")
        end

应用该策略:

kubectl apply -f lua-header-policy.yaml

验证该路由是否返回响应头:

curl -i -H "Host: www.example.com" http://<GATEWAY_ADDRESS>/<PATH>

预期响应头:

x-gwext-lua: enabled

有关更多 Lua 配置选项,请参见上游 Envoy Gateway Lua task

配置 Wasm 扩展

当扩展逻辑打包为 WebAssembly 模块时,请使用 Wasm。以下示例从 HTTP 端点加载上游 Envoy Gateway Wasm task 中使用的示例 Wasm 模块,并将其附加到 example-route

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
  name: wasm-header-policy
  namespace: gateway-demo
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: example-route
  wasm:
    - name: wasm-filter
      rootID: my_root_id
      code:
        type: HTTP
        http:
          url: https://raw.githubusercontent.com/envoyproxy/examples/main/wasm-cc/lib/envoy_filter_http_wasm_example.wasm
          sha256: 79c9f85128bb0177b6511afa85d587224efded376ac0ef76df56595f1e6315c0

应用该策略:

kubectl apply -f wasm-header-policy.yaml

验证 Wasm 过滤器是否运行。此示例中使用的测试模块会添加 x-wasm-custom: FOO 响应头:

curl -i -H "Host: www.example.com" http://<GATEWAY_ADDRESS>/<PATH>

预期响应头:

x-wasm-custom: FOO

本示例中的 Wasm 模块 URL 来自上游 Envoy Gateway Wasm task。若要构建并使用你自己的 Wasm 过滤器,请遵循上游任务,并将 urlsha256 替换为你自己的制品详细信息。

有关更多 Wasm 配置选项,请参见上游 Envoy Gateway Wasm task

配置 Dynamic Module WAF 扩展

当你需要从共享对象文件加载原生 Envoy 扩展时,请使用 Dynamic Module。此示例使用带有 Coraza WAF 规则的 Composer Dynamic Module。WAF 过滤器会阻止可疑请求,例如 SQL 注入载荷。

Dynamic Module 配置分为两部分:

  1. 配置 EnvoyProxy.spec.dynamicModules,以允许 Envoy 加载该模块。
  2. 配置 EnvoyExtensionPolicy.spec.dynamicModule,将该模块作为 HTTP 过滤器附加。

准备辅助镜像

Composer 模块以 libcomposer.so 形式提供。准备一个包含该文件的辅助镜像,并且能够在 init container 中运行 shell 命令。

Kubernetes 1.35 或更高版本支持将 OCI 镜像或制品直接作为 image 卷挂载。如果运行此配置的所有集群都使用 Kubernetes 1.35 或更高版本,你可以将 Composer 制品挂载为 image 卷,而不是在 init container 中复制文件。

此示例使用 init container 和共享的 emptyDir 卷,以兼容较旧的 Kubernetes 版本。采用这种方式时,init container 会将 libcomposer.so 从辅助镜像复制到共享卷中,而 Envoy 容器则从同一卷加载该模块。

下面的 Dockerfile 会将 libcomposer.so 从 Composer 镜像复制到一个基于 Alpine 的普通镜像中:

ARG ALPINE_IMAGE=alpine:3.20

FROM ghcr.io/tetratelabs/built-on-envoy/composer:0.6.0-dev AS composer

FROM ${ALPINE_IMAGE}
COPY --from=composer /libcomposer.so /opt/envoy-dynamic-modules/libcomposer.so
RUN chmod 755 /opt/envoy-dynamic-modules/libcomposer.so

构建并将辅助镜像推送到集群可访问的 registry:

docker build -t <REGISTRY>/envoy-gateway-composer-provider:0.6.0-dev-alpine -f Dockerfile .
docker push <REGISTRY>/envoy-gateway-composer-provider:0.6.0-dev-alpine

配置 EnvoyProxy

创建或更新 Gateway 使用的 EnvoyProxy。以下配置使用 init container 将 libcomposer.so 复制到共享的 emptyDir 卷中。Envoy 容器挂载同一卷,并从 /etc/envoy/dynamic-modules/libcomposer.so 加载该模块。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: example-proxy
  namespace: gateway-demo
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        initContainers:
          - name: provide-dynamic-module
            image: <REGISTRY>/envoy-gateway-composer-provider:0.6.0-dev-alpine
            imagePullPolicy: IfNotPresent
            command:
              - sh
              - -ec
              - |
                cp /opt/envoy-dynamic-modules/libcomposer.so /etc/envoy/dynamic-modules/libcomposer.so
                chmod 755 /etc/envoy/dynamic-modules/libcomposer.so
            volumeMounts:
              - name: dynamic-modules
                mountPath: /etc/envoy/dynamic-modules
        pod:
          volumes:
            - name: dynamic-modules
              emptyDir: {}
        container:
          env:
            - name: GODEBUG
              value: "cgocheck=0"
          volumeMounts:
            - name: dynamic-modules
              mountPath: /etc/envoy/dynamic-modules
              readOnly: true
  dynamicModules:
    - name: composer
      source:
        type: Local
        local:
          path: /etc/envoy/dynamic-modules/libcomposer.so
      doNotClose: true
      loadGlobally: false

应用 EnvoyProxy

kubectl apply -f example-proxy.yaml

确保 Gateway 引用了此 EnvoyProxy

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
  namespace: gateway-demo
spec:
  gatewayClassName: <GATEWAY_CLASS_NAME>
  infrastructure:
    parametersRef:
      group: gateway.envoyproxy.io
      kind: EnvoyProxy
      name: example-proxy
  listeners:
    - name: http
      protocol: HTTP
      port: 80

附加 Coraza WAF 过滤器

创建一个 EnvoyExtensionPolicy,将 Composer 模块附加到 Gateway,并启用 Coraza WAF 规则:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
  name: coraza-waf-policy
  namespace: gateway-demo
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: example-gateway
  dynamicModule:
    - name: composer
      filterName: coraza-waf
      config:
        directives:
          - Include @coraza.conf
          - SecRuleEngine On
          - SecResponseBodyAccess Off
          - Include @crs-setup.conf
          - Include @owasp_crs/*.conf

应用该策略:

kubectl apply -f coraza-waf-policy.yaml

验证普通请求:

curl -i -H "Host: www.example.com" http://<GATEWAY_ADDRESS>/<PATH>

预期状态:

HTTP/1.1 200 OK

验证 WAF 是否会拦截类似 SQL 注入的请求:

curl -i -H "Host: www.example.com" "http://<GATEWAY_ADDRESS>/<PATH>?id=1'+OR+'1'%3D'1"

预期状态:

HTTP/1.1 403 Forbidden

有关更多 Dynamic Module 配置选项,请参见上游 Envoy Gateway Dynamic Modules task

检查扩展状态

应用扩展策略后,检查该策略是否被接受:

kubectl get envoyextensionpolicy -n gateway-demo
kubectl describe envoyextensionpolicy <POLICY_NAME> -n gateway-demo

如果策略未被接受,请检查 Envoy Gateway controller 日志以及生成的 Envoy pods:

kubectl logs -n envoy-gateway-system deploy/envoy-gateway
kubectl get pods -n gateway-demo
kubectl describe pod <ENVOY_POD_NAME> -n gateway-demo

对于 Dynamic Modules,还需要验证以下项目:

项目预期结果
辅助镜像集群可以拉取该镜像
Init containerInit container 成功完成
模块路径/etc/envoy/dynamic-modules/libcomposer.so 存在于 Envoy 容器中
EnvoyProxy.spec.dynamicModules[].nameEnvoyExtensionPolicy.spec.dynamicModule[].name 匹配

清理

当不再需要扩展策略时,请将其删除:

kubectl delete envoyextensionpolicy lua-header-policy -n gateway-demo
kubectl delete envoyextensionpolicy wasm-header-policy -n gateway-demo
kubectl delete envoyextensionpolicy coraza-waf-policy -n gateway-demo

如果你为 Dynamic Module 测试创建了专用的 EnvoyProxy,请在删除或更新引用它的 Gateway 之后将其删除:

kubectl delete envoyproxy example-proxy -n gateway-demo