Cilium Gateway API 外部鉴权实战:用 ExternalAuth 过滤器把 HTTP/gRPC 鉴权下沉到独立 Auth Service
2026/9/14 17:48:08 网站建设 项目流程

Cilium Gateway API 外部鉴权实战:用 ExternalAuth 过滤器把 HTTP/gRPC 鉴权下沉到独立 Auth Service

【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium

导读

本文基于 Cilium 仓库中examples/kubernetes/gateway/external-authz示例,完整讲解如何在 Cilium Gateway API 体系中通过HTTPRouteExternalAuth过滤器,把请求鉴权前置到独立的外部鉴权服务(auth service)。你将掌握:最小可用的 HTTP/gRPC 双协议 ext_authz 测试服务的构建与部署方法、五种典型HTTPRoute配置(HTTP 鉴权、共享配置复用、鉴权参数变体、gRPC 鉴权、无鉴权对照),以及 Cilium Operator 侧 ExternalAuth 过滤器的解析与下发链路,从而能够在自己的集群中快速复现、验证并扩展这套外部鉴权方案。

目录结构与示例定位

示例代码位于仓库的 examples/kubernetes/gateway/external-authz 目录,共四个文件:

文件作用
README.md示例说明:构建、部署、验证步骤与预期行为
manifests.yaml一次性清单:命名空间、Deployment/Service、Gateway 与 5 条 HTTPRoute
main.go最小鉴权服务实现:同时提供 HTTP 与 gRPC 两种 ext_authz 端点
Dockerfile多阶段构建:Go 编译 + 无发行版的 static 运行镜像

这个示例属于 Cilium 对 Gateway APIExternalAuth过滤器的实验性支持。文档 nginx-annotations-migration.rst 在迁移对照表中也把ExternalAuth-style HTTPRoute filter 标注为实验特性,因此本文所有配置均以当前仓库为准,并在文末给出使用边界提示。

测试鉴权服务:一个最小可用的 ext_authz 实现

示例中的 auth service(ext-authz-test)是一个刻意保持极简的鉴权服务,它的全部行为可以概括为三句话:

  • HTTP ext_authz 端点监听8080端口;
  • gRPC ext_authz 端点监听9000端口;
  • 两种协议下:总是放行(always allow)、每个请求打一行日志、并在放行响应中注入X-Test-Authz响应头。

之所以"总是放行",是为了让使用者把注意力完全集中在 Gateway API 配置与请求转发路径上,而不是纠缠于鉴权策略本身——你完全可以在main.goCheck中加上自己的 token 校验逻辑,把它改造成真实的鉴权服务。

入口与双协议启动流程

从源码看,main.go 的启动逻辑非常清晰(main函数,L49-L108):

  • 端口可通过环境变量HTTP_PORT(默认8080)与GRPC_PORT(默认9000)覆盖;
  • 通过signal.NotifyContext监听SIGINT/SIGTERM实现优雅退出;
  • 使用errgroup并发启动两个服务器:
    • http.Server挂在/healthz(就绪/存活探针)与/(鉴权处理)两个路由上;
    • gRPC 服务器同时注册了envoy.service.auth.v3.Authorization服务(authv3.RegisterAuthorizationServer)和grpc.health.v1.Health健康检查服务;
  • 收到退出信号后,先GracefulStopgRPC,再以 5 秒超时关闭 HTTP。

HTTP 鉴权行为(/路由)

newHTTPMux(L110-L128)中的处理逻辑如下:

  • /healthz返回200 ok,供 Deployment 的readinessProbelivenessProbe使用;
  • 其余所有路径(/兜底)视为鉴权请求:打印一行结构化日志(method、path、host、排序后的 header 列表),随后设置X-Test-Authz: allowed-http响应头,返回200 allowed

gRPC 鉴权行为(Check方法)

gRPC 端实现了 Envoy ext_authz 标准接口Check(L130-L159):

  • CheckRequest.Attributes.Request.Http中提取 method、path、host 与 headers 并打印日志;
  • 返回CheckResponseStatusOKHttpResponseOkHttpResponse,其中通过Headers注入x-test-authz: allowed-grpc,并通过DynamicMetadata写入result=allowed

这套响应结构与 Envoy 的envoy.service.auth.v3协议完全对齐,而 Cilium 代理正是通过内置 Envoy 来执行 Gateway API 过滤器(在 pkg/envoy/resource/envoy.go 中注册了envoy.extensions.filters.http.ext_authz/v3过滤器),因此示例服务可以无缝对接。

构建镜像并导入 kind 集群

README 针对本地kind集群给出了两条命令:

docker build -t external-authz-test:dev -f examples/kubernetes/gateway/external-authz/Dockerfile . kind load docker-image external-authz-test:dev

第一条命令用 Dockerfile 构建镜像:第一阶段基于golang:1.26,关闭 CGO 并启用 vendor 模式编译external-authz二进制;第二阶段基于cgr.dev/chainguard/static:latest无发行版基础镜像,仅拷贝二进制作为入口,镜像体积小、攻击面小。第二条命令把本地镜像导入 kind 节点,避免触发镜像拉取(Deployment 中imagePullPolicy: IfNotPresentimage: external-authz-test:dev正是为此设计)。

如果不是 kind,而是其他集群,需要把external-authz-test:dev推送到集群可访问的镜像仓库,并相应修改 manifests.yaml 中的镜像地址。

部署前置条件

README 明确列出两条假设,缺一不可:

  1. Cilium Gateway API 支持已启用(例如 Helm 安装时开启gatewayAPI.enabled=true);
  2. 已安装带ExternalAuth支持的 Gateway API CRDs

这是因为ExternalAuth过滤器尚属实验特性,需要安装了对应实验版 CRD schema 的 Gateway API 版本,标准 GA 版 CRD 中可能并不包含该过滤器字段。因此,请先确认集群中的gateway.networking.k8s.io/v1CRD 已支持HTTPRouteFilterExternalAuth,再执行部署。

一键部署完整演示环境

满足前置条件后,执行 README 中的单条命令即可创建整套资源:

kubectl apply -f examples/kubernetes/gateway/external-authz/manifests.yaml

该清单(manifests.yaml)会依次创建:

  • 命名空间gateway-external-authz-demo
  • auth-serviceext-authz-testDeployment(1 副本,暴露http:8080grpc:9000,带/healthz就绪与存活探针)+ 同名 Service(http:8080grpc:9000两个端口);
  • echo 后端:基于gcr.io/k8s-staging-gateway-api/echo-basic镜像的echoDeployment + Service(8080端口);
  • 一个 Gatewayext-authz-gateway,使用gatewayClassName: cilium,HTTP 监听器web(端口 80,允许同命名空间路由);
  • 五条 HTTPRoute,覆盖五类鉴权场景。

下面逐个拆解这五条 HTTPRoute 的配置。

场景一:HTTP ExternalAuth(/http-auth

第一条路由ext-authz-http展示了最基础的 HTTP 外部鉴权:

apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: ext-authz-http namespace: gateway-external-authz-demo spec: parentRefs: - name: ext-authz-gateway rules: - matches: - path: type: PathPrefix value: /http-auth filters: - type: ExternalAuth externalAuth: protocol: HTTP backendRef: name: auth-service port: 8080 http: path: /check allowedResponseHeaders: - X-Test-Authz backendRefs: - name: echo port: 8080

关键点:

  • filters[].type: ExternalAuth声明启用外部鉴权;
  • externalAuth.protocol: HTTP指定鉴权走 HTTP 协议,backendRef指向auth-service:8080(HTTP 端点);
  • externalAuth.http.path: /check指定 Cilium 向鉴权服务发送请求时使用的路径——注意示例服务对所有非/healthz路径一视同仁地放行,所以这里使用任意路径均可;
  • allowedResponseHeaders: [X-Test-Authz]声明允许把鉴权服务响应头中的X-Test-Authz透传回原始客户端。示例服务返回的X-Test-Authz: allowed-http正是通过这个白名单才能出现在最终响应里。

场景二:复用同一鉴权配置(/http-auth-shared

路由ext-authz-http-sharedExternalAuth配置与场景一完全相同(同一个 auth service、同一个/check路径、同一个响应头白名单),只是匹配路径换成了/http-auth-shared。它用于验证"一份鉴权配置被多个路由共享"的复用模型——在真实场景中,这通常意味着多组 API 路径共用同一套 OIDC/OAuth2 鉴权服务,无需重复配置。

场景三:鉴权参数变体(/http-auth-variant

路由ext-authz-http-variant复用了同一个鉴权服务,但换了不同的鉴权参数:

filters: - type: ExternalAuth externalAuth: protocol: HTTP backendRef: name: auth-service port: 8080 http: path: /variant-check allowedHeaders: - X-Debug-Token allowedResponseHeaders: - X-Test-Authz

与场景一/二对比,差异有两处:

  • http.path改为/variant-check,验证不同路由可以向鉴权服务请求不同鉴权路径;
  • 新增allowedHeaders: [X-Debug-Token],声明把客户端请求头X-Debug-Token转发给鉴权服务。这正是 README 验证步骤中使用curl -H 'X-Debug-Token: demo'的原因——带上这个头后,你可以在 auth-service 的日志里看到它被转发进来。

场景四:gRPC ExternalAuth(/grpc-auth

路由ext-authz-grpc演示 gRPC 协议的外部鉴权:

filters: - type: ExternalAuth externalAuth: protocol: GRPC backendRef: name: auth-service port: 9000 grpc: {} backendRefs: - name: echo port: 8080

关键差异:

  • externalAuth.protocol: GRPCbackendRef指向 auth-service 的 gRPC 端点9000
  • grpc: {}为 gRPC 鉴权配置占位(当前示例未启用额外选项,如allowedRequestHeaders等)。

此时 Cilium 代理会通过 Envoy 的 gRPC ext_authz 过滤器,把鉴权请求以envoy.service.auth.v3.Authorization/CheckRPC 发送给服务端,触发 main.go 中实现的Check方法。

场景五:无鉴权对照(/no-auth

路由no-auth没有配置任何过滤器,直接backendRefs指向 echo 后端。它是验证链路差异的"对照组":请求应当直接到达后端,auth-service 日志中不会出现对应记录。

验证:观察请求如何穿过鉴权过滤器

第一步:获取网关地址

kubectl -n gateway-external-authz-demo get gateway ext-authz-gateway

从输出中取得 Gateway 的对外地址(LoadBalancer IP 或主机名),记为GATEWAY_ADDRESS

第二步:发送五类请求

curl -i http://GATEWAY_ADDRESS/http-auth curl -i http://GATEWAY_ADDRESS/http-auth-shared curl -i -H 'X-Debug-Token: demo' http://GATEWAY_ADDRESS/http-auth-variant curl -i http://GATEWAY_ADDRESS/grpc-auth curl -i http://GATEWAY_ADDRESS/no-auth

注意第三条命令带了X-Debug-Token: demo请求头,用于验证场景三中allowedHeaders的转发行为。

第三步:观察鉴权日志

kubectl -n gateway-external-authz-demo logs deploy/ext-authz-test -f

日志以slog文本格式输出。HTTP 请求会打出http ext_authz request行,包含 method、path、host 以及排好序的 header 列表(flattenHTTPHeaders实现);gRPC 请求会打出grpc ext_authz request行,同样包含 method/path/host/headers。

预期行为对照表

请求路径auth-service 是否出现日志说明
/http-auth是(HTTP 请求)基础 HTTP 鉴权
/http-auth-shared是(HTTP 请求)同一鉴权配置被复用
/http-auth-variant是(HTTP 请求,auth path 为/variant-check同一服务、不同鉴权参数
/grpc-auth是(gRPC 请求)gRPC 协议外部鉴权
/no-auth无鉴权过滤器,直达后端

/http-auth的响应中能看到X-Test-Authz: allowed-http/grpc-auth的响应中能看到X-Test-Authz: allowed-grpc,说明allowedResponseHeaders白名单透传生效;同时/http-auth-variant的日志里应能看到X-Debug-Token=demo,证明allowedHeaders转发生效。

原理纵深:ExternalAuth 过滤器在 Cilium 中的处理链路

从 HTTPRoute 到内部模型

ExternalAuth过滤器并不是由数据面直接"魔法"处理的,它在 Cilium Operator 中先被转换成语义化的内部模型。核心实现在 operator/pkg/model/ingestion/gateway.go 的toHTTPExternalAuthFilter函数:

  • ExternalAuth过滤器存在但未指定端口,Operator 会打印告警并直接忽略该过滤器"ExternalAuth filter has no port specified; filter will be ignored");
  • 解析backendRef指向的服务与端口,解析跨命名空间引用及BackendTLSPolicy细节(addBackendTLSDetails);
  • 构造model.HTTPExternalAuthFilter,其字段与 CRD 一一对应:ProtocolHTTP/GRPC)、PathPrefix(对应http.path)、AllowedRequestHeaders(对应allowedHeaders)、AllowedResponseHeaders(对应allowedResponseHeaders),以及ForwardBody(当forwardBody.maxSize > 0时启用请求体转发)。

此外,operator/pkg/gateway-api/indexers/httproute.go 中实现了对ExternalAuth过滤器后端服务的索引(并有对应单测),保证当鉴权服务本身发生变化时,引用它的路由能被正确关联与更新。

从模型到 Envoy 过滤器

内部模型最终会在代理侧翻译为 Envoy 的 ext_authz HTTP 过滤器配置。Cilium 的 Envoy 资源注册文件 pkg/envoy/resource/envoy.go 与 cec_resource_parser.go 分别注册并导入了envoy.extensions.filters.http.ext_authz/v3(HTTP 过滤器)与envoy.extensions.filters.network.ext_authz/v3,而示例服务实现的正是这一套过滤器所需的envoy.service.auth.v3协议。整条链路可以概括为:

HTTPRoute(filters[].type=ExternalAuth) → Operator: toHTTPExternalAuthFilter() 解析为 model.HTTPExternalAuthFilter → 代理侧生成 Envoy ext_authz filter 配置 → Envoy 按 protocol 调用 auth-service 的 HTTP(8080) 或 gRPC(9000) 端点 → 鉴权服务返回 OK(放行) 或 NonOK(拒绝),响应头按白名单透传 → 放行后请求继续转发到 echo 后端

使用边界与后续扩展建议

  • 该特性在本文档语境下为实验性,字段语义可能随 Gateway API 上游演进调整,生产使用前请核对所用 Cilium 版本的 CRD 与升级说明;
  • 示例服务"总是放行",实际接入时可在Check(gRPC)或/处理器(HTTP)中增加 token/JWT 校验,并将Status置为PermissionDenied等非 OK 状态码即可实现拒绝语义;
  • 通过allowedHeaders可以把客户端身份头(如AuthorizationX-User)转发给鉴权服务;通过allowedResponseHeaders可以把鉴权服务产出的头(如X-Test-Authz、用户信息)透传给下游;
  • 若鉴权服务需要校验请求体(如签名类鉴权),可通过forwardBody开启请求体转发(maxSize限制大小)。

小结

examples/kubernetes/gateway/external-authz为 Cilium Gateway API 的ExternalAuth过滤器提供了一套完整、可复现的最小演示:一个同时暴露 HTTP/gRPC ext_authz 端点的测试服务、一份覆盖五类鉴权场景的清单,以及清晰的验证步骤。配合 Operator 侧的解析实现(toHTTPExternalAuthFilter)与代理侧的 Envoy ext_authz 过滤器,你可以快速理解"HTTPRoute 声明鉴权 → 控制器下发配置 → 代理调用外部鉴权服务 → 放行/拒绝"的完整链路,并以此为骨架搭建生产级的外部鉴权方案。

【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询