通过 CredentialProvider 配置使用 Kubernetes Secret 中的 API Key 凭据

更新时间:
复制 MD 格式

通过 CredentialProvider 将存储在 Kubernetes Secret 中的 API Key 统一纳管,由平台按 Agent 身份为 AI Agent 工作负载按需下发凭据,应用侧无需在代码或镜像中内置明文密钥。

功能介绍

在 AI Agent 场景中,Agent 常需调用第三方服务(如 OpenAI、通义千问等 LLM 服务)的 API Key。若将真实 API Key 直接写入应用代码、镜像或环境变量,存在凭据泄露与难以轮转的风险。

CredentialProvider 是 ack-agent-identity 提供的凭据来源自定义资源(CRD)。当类型为 APIKey 且来源为 Kubernetes 时,它从集群内的 Kubernetes Secret 读取单个字段作为 API Key,并具备以下能力:

  • 真实 API Key 仅存储在集群侧的 Kubernetes Secret 中,由平台统一管理与轮转。

  • 通过 AgentRole / AgentRoleBinding 授权机制,精细控制每个 Agent 身份可获取哪些 CredentialProvider 的凭据。

  • secretRef.name 支持模板变量,可按 Agent 身份等上下文动态引用不同的 Secret,实现“一份配置、按身份取不同凭据”。

CredentialProviderAPIKey 外还支持 RAM 等类型,来源也支持 KMS、RRSA 等。本文仅介绍 type: APIKey + Kubernetes Secret 来源,其余类型与来源请参见对应文档。

涉及的资源对象

配置一个可用的 API Key 凭据下发,通常涉及以下 CR,下文操作步骤将依次创建:

资源对象

说明

AgentIdentity

定义 Agent 身份标识,是授权与凭据下发的主体。

CredentialProvider

定义凭据来源。本文中 type: APIKey,从 Kubernetes Secret 读取 API Key。

AgentRole

定义权限规则,声明允许获取哪个 CredentialProvider 的凭据。

AgentRoleBinding

将 AgentRole 绑定到指定 AgentIdentity,完成授权。

适用范围

  • 集群版本 >= 1.28。

  • 在集群组件管理页面,确认 ack-agent-identity 组件版本 >= 0.2.0。

配置 API Key 凭据

以下操作通过 kubectl 完成,执行前请确保已配置可访问目标 ACS 集群的 kubeconfig,具体操作请参见通过kubectl快速使用ACS

以下资源需创建在同一命名空间下:CredentialProvider 引用的 Secret 必须与其位于同一命名空间,暂不支持跨命名空间引用。

  1. 将以下内容保存为 llm-api-key-secret.yaml 并执行 kubectl apply -f llm-api-key-secret.yaml 命令,创建存储 API Key 的 Kubernetes Secret。

    apiVersion: v1
    kind: Secret
    metadata:
      name: llm-api-key
      namespace: <YOUR_NAMESPACE>
    type: Opaque
    stringData:
      apiKey: "sk-xxxxxxxxxxxxxxxx"   # 替换为真实 API Key
  2. 将以下内容保存为 agent-identity.yaml 并执行 kubectl apply -f agent-identity.yaml 命令,创建 AgentIdentity 定义 Agent 身份标识。

    apiVersion: agentidentity.alibabacloud.com/v1alpha1
    kind: AgentIdentity
    metadata:
      name: my-agent          # Agent 身份名称,后续授权中引用
      namespace: <YOUR_NAMESPACE>
    spec:
      description: "示例 AI Agent 身份"
  3. 将以下内容保存为 credential-provider-apikey.yaml 并执行 kubectl apply -f credential-provider-apikey.yaml 命令,创建 CredentialProvider 引用上述 Secret。

    apiVersion: agentidentity.alibabacloud.com/v1alpha1
    kind: CredentialProvider
    metadata:
      name: llm-api-key
      namespace: <YOUR_NAMESPACE>
    spec:
      type: APIKey
      apiKey:
        source:
          provider: Kubernetes
          kubernetes:
            secretRef:
              name: llm-api-key       # 步骤 1 创建的 Secret 名称
            keyName: apiKey           # Secret 中包含 API Key 的字段名
    secretRef.name 支持使用模板变量按上下文动态引用不同的 Secret,详见secretRef.name 模板变量
  4. 将以下内容保存为 agent-role-apikey.yaml 并执行 kubectl apply -f agent-role-apikey.yaml 命令,创建 AgentRole 和 AgentRoleBinding,授权 Agent 身份获取该 CredentialProvider 的凭据。

    apiVersion: agentidentity.alibabacloud.com/v1alpha1
    kind: AgentRole
    metadata:
      name: get-llm-key
      namespace: <YOUR_NAMESPACE>
    spec:
      rules:
      - effect: Allow
        action: "GetResourceCredential"
        resource: "CredentialProvider/llm-api-key"   # 上一步创建的 CredentialProvider 名称
    ---
    apiVersion: agentidentity.alibabacloud.com/v1alpha1
    kind: AgentRoleBinding
    metadata:
      name: my-agent-get-llm-key
      namespace: <YOUR_NAMESPACE>
    spec:
      agentRoleRef:
        apiGroup: agentidentity.alibabacloud.com
        kind: AgentRole
        name: get-llm-key
      subjects:
      - authorizationType: "Agent"
        agentAuthorizationConfiguration:
          agentName: my-agent                        # 与 AgentIdentity name 一致
  5. 确认 CredentialProvider 已就绪(Available 列为 True)。

    kubectl get credentialprovider llm-api-key -n <YOUR_NAMESPACE>

    预期输出:

    NAME          AVAILABLE   AGE
    llm-api-key   True        30s

    Available 不为 True,可通过 kubectl describe 查看 Conditions 中的原因,排查方法参见状态与排查

消费凭据

创建并授权好的 API Key 凭据,通常由 Agent Sandbox 出口流量凭据注入消费:在 SecurityProfile 的 tokenTransformation 规则中引用本 CredentialProvider,Sandbox 应用使用占位符 Token 发起请求,出口网关在转发时自动替换为真实 API Key。完整的端到端配置(SecurityProfile、SandboxSet、SandboxClaim 及验证)请参见Agent Sandbox出口流量配置凭据注入

secretRef.name 模板变量

secretRef.name 除填写固定的 Secret 名称外,还可嵌入 ${ack:agent-identity/...} 模板变量。系统在获取凭据时按当前请求身份上下文渲染出实际 Secret 名称,从而按身份下发不同凭据。例如 llm-api-key-${ack:agent-identity/agent-name} 会按 Agent 名称解析为 llm-api-key-my-agent

支持的模板变量如下:

模板变量

说明

${ack:agent-identity/agent-name}

当前 Agent 身份关联的 AgentIdentity 名称。

${ack:agent-identity/metadata/<key>}

身份 Token 签发时写入的自定义 metadata 中指定 Key 的值,<key> 替换为实际 Key 名称。

若模板变量在当前请求上下文中无法解析(例如引用的 metadata Key 不存在),凭据获取请求会失败并返回明确的错误信息,而非静默返回空值。

以下示例演示按租户动态引用不同的 Secret:不同租户的 API Key 分别存放在以 llm-api-key-<租户标识> 命名的 Secret 中,CredentialProvider 通过 ${ack:agent-identity/metadata/tenant-id} 在获取凭据时解析出对应的 Secret 名称。

metadata 的值来自身份 Token 签发时写入的自定义 metadata;在 Agent Sandbox 场景中,可通过 SandboxClaim 的 security.agents.kruise.io/<key> 注解传入(去掉前缀后即为 metadata 的 Key):

apiVersion: agents.kruise.io/v1alpha1
kind: SandboxClaim
metadata:
  name: my-claim
  namespace: <YOUR_NAMESPACE>
spec:
  templateName: my-sandbox-set
  replicas: 1
  annotations:
    # 以下 annotation 去掉前缀后可在模板中通过 ${ack:agent-identity/metadata/tenant-id} 引用
    security.agents.kruise.io/tenant-id: acme
    security.agents.kruise.io/agent-name: my-agent

对应的 CredentialProvider:

apiVersion: agentidentity.alibabacloud.com/v1alpha1
kind: CredentialProvider
metadata:
  name: llm-api-key
  namespace: <YOUR_NAMESPACE>
spec:
  type: APIKey
  apiKey:
    source:
      provider: Kubernetes
      kubernetes:
        secretRef:
          name: 'llm-api-key-${ack:agent-identity/metadata/tenant-id}'   # 解析为 llm-api-key-acme
        keyName: apiKey

上述配置下,当请求身份的 metadata tenant-idacme 时,secretRef.name 解析为 llm-api-key-acme,即读取该租户专属的 Secret。

字段说明

type: APIKey + Kubernetes 来源的 spec 结构如下:

spec:
  type: APIKey                     # 必填,凭据类型;创建后不可变更
  apiKey:
    source:
      provider: Kubernetes         # 必填,凭据来源
      kubernetes:
        secretRef:
          name: <secret-name>      # 必填,同命名空间 Secret 名称,支持模板变量
        keyName: <key>             # 必填,Secret data 中的字段名

关键字段说明如下:

字段

是否必填

说明

spec.type

凭据类型,本文取 APIKey。创建后不可变更(修改会被校验拒绝)。

spec.apiKey.source.provider

凭据来源。本文取 Kubernetes;若集群已启用 KMS,也可取 KMS(参见 KMS 来源文档)。

spec.apiKey.source.kubernetes.secretRef.name

引用的 Kubernetes Secret 名称,须与 CredentialProvider 同命名空间。支持模板变量

spec.apiKey.source.kubernetes.keyName

读取 Secret data 中的哪个字段作为 API Key。APIKey 类型下必填。

状态与排查

CredentialProvider 的运行状态记录在 status.conditions 中,可通过 kubectl describe credentialprovider <name> -n <YOUR_NAMESPACE> 查看。常见 Condition 与 Reason 如下:

Condition / Reason

说明

Available = True

配置校验通过,凭据来源可正常解析。

Degraded,Reason=SecretNotFound

secretRef.name 指向的 Secret 不存在(检查 Secret 名称与命名空间是否一致)。

Degraded,Reason=SecretKeyMissing

Secret 存在,但其中不包含 keyName 指定的字段。

secretRef.name 使用了模板变量时,实际 Secret 名称需在获取凭据时才能确定,因此协调阶段会跳过 Secret 存在性检查并直接标记 Available;此类配置的错误(如渲染出的 Secret 不存在)会在实际获取凭据时暴露,而非体现在 CredentialProvider 状态上。

使用限制

限制项

说明

Secret 与 CredentialProvider 同命名空间

secretRef 仅支持引用同命名空间下的 Secret,不支持跨命名空间。

单字段读取

APIKey 类型只从 Secret 中读取 keyName 指定的单个字段,keyName 必填。

常见问题

CredentialProvider 的 Available 一直不为 True?

按以下步骤排查:

  1. 执行 kubectl describe credentialprovider <name> -n <YOUR_NAMESPACE>,查看 Conditions 中的 Reason。

  2. Reason 为 SecretNotFound:确认 secretRef.name 与实际 Secret 名称一致,且 Secret 与 CredentialProvider 位于同一命名空间。

  3. Reason 为 SecretKeyMissing:确认 keyName 与 Secret data 中的字段名一致(可执行 kubectl get secret <name> -n <YOUR_NAMESPACE> -o jsonpath='{.data}' 查看字段)。

Agent 获取凭据时被拒绝(无权限)?

确认已创建 AgentRole(action: GetResourceCredentialresource: CredentialProvider/<name>)及 AgentRoleBinding,且 AgentRoleBinding 的 agentName 与目标 AgentIdentity 名称一致,三者位于同一命名空间。

相关文档