对消费者进行身份验证

简介

Envoy AI Gateway 会在边缘对每个推理请求进行身份验证,并将调用方的身份传递给下游策略。身份验证通过 Envoy Gateway 的 SecurityPolicy 资源进行配置,该资源会附加到由 AIGatewayRoute 生成的 HTTPRoute。在识别出调用方后,会将选定的 claim 复制到请求头中,供 token 配额和使用量计量以每租户键的形式消费。

这会把每个消费者的凭证,例如 SSO (Single Sign-On) token 或 API key,转换为配额和计量可以作用的身份。这是共享网关上多租户模型服务的基础。

使用场景

  • 开发人员从平台 Identity Provider 获取 JWT (JSON Web Token) 并使用它调用网关,从而让网关按用户强制执行 token 配额。
  • CI 作业提供 service-account token,使自动化流量被归属到某个团队,而不是某个个人。
  • 无法运行交互式登录的机器消费者提供静态 API key,并将其映射到已知租户。

前提条件

  1. 已安装 Envoy AI Gateway。它由 Alauda AI 通过 default AmlCluster 中的 envoyAIGateway 组件进行管理;请参阅安装 Alauda AIEnvoy AI Gateway 简介
  2. 一个 AIGatewayRoute 已经将请求路由到一个或多个后端。
  3. 对于 OIDC/JWT 路径:需要一个可访问 JWKS 端点的 OIDC issuer。平台内置的身份提供方 Dex 是默认选项;任何其他 OIDC issuer(Keycloak、Auth0、Okta、GitHub OIDC、企业 Entra ID 租户)也都可以,只要网关能够访问其 /.well-known/openid-configuration 和 JWKS URL。
  4. 对于 API key 路径:集群需要具备在网关命名空间中创建 Secret 对象的权限。
NOTE

请在专用命名空间中创建 GatewayAIGatewayRoute(例如 maas-system),而不是在 Envoy Gateway 控制平面命名空间 envoy-gateway-system 中创建。放置在控制平面命名空间中的网关可能不会在其监听器上应用 AI Gateway 请求处理过滤器和 SecurityPolicy,这会悄然破坏路由和策略强制执行。请参阅Envoy AI Gateway

步骤

使用 OIDC 或 JWT 进行身份验证

验证由 OIDC issuer 签发的 token。平台内置的 Dex 是默认 issuer;它还可以代理外部身份来源,例如 LDAP 或其他 OIDC provider,以便这些用户获取平台 token。这些连接器在平台 IdP (Identity Provider) 管理中进行配置。有关平台 IdP 配置,请参阅 Identity Providers

任何具有可访问 JWKS 端点的 OIDC issuer 都可以使用。当消费者不是平台用户时,请将下面的 issuerremoteJWKS.uri 替换为你选择的 issuer,例如企业 Keycloak realm 或 SaaS IdP,这样网关就能接受其 token,而无需平台账户。

将网关指向 OIDC issuer,并将其 claim 映射到身份请求头:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: maas-oidc-auth
  namespace: <your-namespace>
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <aigatewayroute-name>  # HTTPRoute generated by your AIGatewayRoute
  jwt:
    providers:
      - name: platform-idp
        issuer: https://<platform-address>/dex
        audiences:
          - <gateway-client-id>   # reject tokens minted for other clients of the same issuer
        remoteJWKS:
          uri: https://<platform-address>/dex/keys
        claimToHeaders:
          - claim: sub      # caller identity, used as the per-user quota and metering key
            header: x-user-id
          - claim: groups    # single-valued group/department claim (see note: array claims are unsupported)
            header: x-user-group
          - claim: namespace  # custom scalar claim for per-namespace chargeback; absent by default (see note)
            header: x-user-namespace
          - claim: email
            header: x-user-email
  • <platform-address>:平台访问地址。Dex 将其 issuer 发布在 /dex,JWKS 发布在 /dex/keys
  • <aigatewayroute-name>:由你的 AIGatewayRoute 生成的 HTTPRoute 名称。
  • audiences:网关接受的 token audience。对于共享 IdP,如果省略此项,则会接受来自同一 issuer 的任何有效 token——包括为其他客户端签发的 token——这些 token 仍然会解析为 x-user-id 并消耗配额。请将其设置为网关 token 签发所针对的 client ID;如果你的 issuer 未设置可区分的 aud,请为网关注册一个专用 client。
  • claimToHeaders:身份与策略之间的桥梁。生成的请求头(x-user-idx-user-groupx-user-namespace)会成为 token 配额的选择器键,以及使用量计量和 chargeback 的标签值。
NOTE

claimToHeaders 仅支持标量 claim(string、int、double、bool);不支持数组类型的 claim,且不会填充请求头。标准 OIDC groups claim 通常是数组——若要将其用作 x-user-group/department,请从 IdP 连接器中暴露一个单值 claim(例如 primary-group 或专用的 department claim)并进行映射。如果 x-user-group 仍为空,则按部门的计量、配额和层级会悄然退回为不分组。

namespace 不是标准 OIDC claim:必须在上游 IdP 连接器中添加,默认情况下不存在。x-user-namespaceMetering Token Usage 消费的按命名空间 chargeback 键;仅在按命名空间或租户计费时映射它。对于平台默认不发出的其他任意属性,例如 subscription tier,如需将其作为策略键,请在连接器中添加该 claim,并通过额外的 claimToHeaders 条目进行映射。

TIP

若要在不中断流量的情况下逐步发布,请先设置 jwt.optional: true 并进行观测。待所有消费者都提供有效 token 后再将其移除。

使用 API key 进行身份验证

NOTE

如果 Gateway 创建在 Envoy Gateway 控制平面命名空间 envoy-gateway-system 中,则 apiKeyAuth(与 model routing 类似)不会被强制执行:SecurityPolicy 会报告 Accepted=True,但错误的 key 或缺失的 key 仍然会返回 200,并且不会注入 x-user-id。这与上文前提条件说明中的控制平面 listener-skip 问题相同——不是 Envoy Gateway 版本 bug。解决方法是将 GatewayAIGatewayRoute 创建在专用命名空间中(推荐,例如 maas-system),这样 apiKeyAuth 就能原生强制执行,无需补丁。可通过一次无 key 请求进行验证:专用命名空间中的网关会返回 401。如果网关必须保留在 envoy-gateway-system 中,请参阅本节末尾的受支持修复方案。

对于无法执行 OIDC 流程的机器消费者,请改用静态 API key 进行验证。这里没有签发服务:集群管理员为每个消费者生成一个随机字符串,将其存储在 Secret 中,并通过带外方式共享。网关的数据平面通过在该 Secret 中查找所提供的值来验证每个请求。

为每个消费者生成一个 key,并将它们存储在同一个 Opaque Secret 中。每个 data-map key 都是下游策略看到的客户端标识符;每个 value 都是消费者提供的API key

kubectl -n <your-namespace> create secret generic maas-api-keys \
  --from-literal=alice="$(openssl rand -hex 32)" \
  --from-literal=ci-runner="$(openssl rand -hex 32)"

使用 SecurityPolicy 将该 Secret 绑定到路由:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: maas-apikey-auth
  namespace: <your-namespace>
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <aigatewayroute-name>  # HTTPRoute generated by your AIGatewayRoute
  apiKeyAuth:
    credentialRefs:
      - name: maas-api-keys     # Secret whose data keys are the client identifiers
    extractFrom:
      - headers:
          - X-API-Key           # dedicated header avoids the "Bearer " prefix problem of Authorization
    forwardClientIDHeader: x-user-id  # matched client identifier is injected as this header for downstream policies
    sanitize: true              # strip the raw API key from the request before it reaches the model backend
  • credentialRefs:一个或多个保存凭证的 Opaque Secret。每个 data-map key 都是客户端标识符,每个 value 都是字面量 API key。添加消费者只需对一个条目执行 kubectl patch;撤销则只需删除一个 key。
  • extractFrom:Envoy 从哪里读取所提供的 key。该过滤器执行字面字符串比较,因此建议使用专用请求头,例如 X-API-Key。复用 Authorization 需要将 value 及其 Bearer 前缀一并存储,这与同一网关上的 OIDC 路径会产生冲突。
  • forwardClientIDHeader:将匹配到的客户端标识符传递给上游以及后续过滤器的请求头。请使用与 OIDC claimToHeaders 目标相同的名称(x-user-id),这样 token 配额和使用量计量在两条认证路径上就能看到一致的 key。
  • sanitize:防止原始 API key 泄漏到模型后端或在下游日志中被记录。

如果网关必须保留在 envoy-gateway-system,则 apiKeyAuth 的强制执行会受到上述相同控制平面 listener-skip 的影响,并且没有任何按路由的 SecurityPolicy 更改可以修复它。受支持的修复方案是将 GatewayAIGatewayRoute 移动到专用命名空间(首选),或者将 Envoy AI Gateway 升级到一个缩小 listener-skip 范围的版本(v0.6.0 / Alauda release-0.6.0-alauda)。手工编写的 EnvoyPatchPolicy 也许可以作为临时权宜之计,通过修改监听器过滤器链来实现,但它具有版本相关性且较为脆弱——它依赖于运行中的 Envoy 构建的精确过滤器布局——因此不建议用于生产环境。

验证

确认策略已被接受。SecurityPolicy 状态是 ancestor-scoped,因此 jsonpath 比大多数资源要多一层:

kubectl get securitypolicy <policy-name> -n <your-namespace> \
  -o jsonpath='{.status.ancestors[*].conditions[?(@.type=="Accepted")].status}'

当策略已编程时,该命令会返回 True

对于 OIDC 路径,使用有效 token 发送请求,并确认上游服务接收到 x-user-idx-user-groupx-user-email 请求头。

对于 API key 路径,发送匹配的 X-API-Key,并确认上游看到的 x-user-id 已设置为匹配到的客户端标识符:

curl -sS -H "X-API-Key: <alice-key>" \
  https://<gateway-host>/v1/chat/completions \
  -d '{"model":"<model>","messages":[{"role":"user","content":"ping"}]}'

错误的 key 或缺失的 key 会在请求到达任何后端之前,由网关返回 401 Unauthorized

了解更多

下一步

在身份请求头传播完成后,请配置配置 token 配额以强制执行按租户的 token 预算。