通过 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,实现“一份配置、按身份取不同凭据”。
CredentialProvider除APIKey外还支持RAM等类型,来源也支持 KMS、RRSA 等。本文仅介绍type: APIKey+ Kubernetes Secret 来源,其余类型与来源请参见对应文档。
涉及的资源对象
配置一个可用的 API Key 凭据下发,通常涉及以下 CR,下文操作步骤将依次创建:
资源对象 | 说明 |
| 定义 Agent 身份标识,是授权与凭据下发的主体。 |
| 定义凭据来源。本文中 |
| 定义权限规则,声明允许获取哪个 CredentialProvider 的凭据。 |
| 将 AgentRole 绑定到指定 AgentIdentity,完成授权。 |
适用范围
集群版本 >= 1.28。
在集群组件管理页面,确认
ack-agent-identity组件版本 >= 0.2.0。
配置 API Key 凭据
以下操作通过 kubectl 完成,执行前请确保已配置可访问目标 ACS 集群的 kubeconfig,具体操作请参见通过kubectl快速使用ACS。
以下资源需创建在同一命名空间下:CredentialProvider 引用的 Secret 必须与其位于同一命名空间,暂不支持跨命名空间引用。
将以下内容保存为
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将以下内容保存为
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 身份"将以下内容保存为
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 模板变量。将以下内容保存为
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 一致确认 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。
支持的模板变量如下:
模板变量 | 说明 |
| 当前 Agent 身份关联的 AgentIdentity 名称。 |
| 身份 Token 签发时写入的自定义 metadata 中指定 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-id 为 acme 时,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 中的字段名关键字段说明如下:
字段 | 是否必填 | 说明 |
| 是 | 凭据类型,本文取 |
| 是 | 凭据来源。本文取 |
| 是 | 引用的 Kubernetes Secret 名称,须与 CredentialProvider 同命名空间。支持模板变量。 |
| 是 | 读取 Secret |
状态与排查
CredentialProvider 的运行状态记录在 status.conditions 中,可通过 kubectl describe credentialprovider <name> -n <YOUR_NAMESPACE> 查看。常见 Condition 与 Reason 如下:
Condition / Reason | 说明 |
| 配置校验通过,凭据来源可正常解析。 |
|
|
| Secret 存在,但其中不包含 |
当secretRef.name使用了模板变量时,实际 Secret 名称需在获取凭据时才能确定,因此协调阶段会跳过 Secret 存在性检查并直接标记Available;此类配置的错误(如渲染出的 Secret 不存在)会在实际获取凭据时暴露,而非体现在 CredentialProvider 状态上。
使用限制
限制项 | 说明 |
Secret 与 CredentialProvider 同命名空间 |
|
单字段读取 | APIKey 类型只从 Secret 中读取 |
常见问题
CredentialProvider 的 Available 一直不为 True?
按以下步骤排查:
执行
kubectl describe credentialprovider <name> -n <YOUR_NAMESPACE>,查看Conditions中的 Reason。Reason 为
SecretNotFound:确认secretRef.name与实际 Secret 名称一致,且 Secret 与 CredentialProvider 位于同一命名空间。Reason 为
SecretKeyMissing:确认keyName与 Secretdata中的字段名一致(可执行kubectl get secret <name> -n <YOUR_NAMESPACE> -o jsonpath='{.data}'查看字段)。
Agent 获取凭据时被拒绝(无权限)?
确认已创建 AgentRole(action: GetResourceCredential、resource: CredentialProvider/<name>)及 AgentRoleBinding,且 AgentRoleBinding 的 agentName 与目标 AgentIdentity 名称一致,三者位于同一命名空间。