Use API Key credentials stored in a Kubernetes Secret by configuring a CredentialProvider
CredentialProvider centrally manages API Keys stored in Kubernetes Secrets. The platform issues credentials on demand to AI Agent workloads based on their Agent identities, so applications do not need to embed plaintext keys in code or images.
Feature introduction
In AI Agent scenarios, Agents often need to call APIs of third-party services that require API Keys, such as LLM services like OpenAI and Tongyi Qianwen. Embedding a real API Key directly in application code, images, or environment variables poses risks of credential leakage and difficulty in rotation.
CredentialProvider is a credential source custom resource (CRD) provided by ack-agent-identity. When the type is APIKey and the source is Kubernetes, it reads a single field from a Kubernetes Secret in the cluster as the API Key and provides the following capabilities:
The real API Key is stored only in a Kubernetes Secret on the cluster side, and is centrally managed and rotated by the platform.
The AgentRole / AgentRoleBinding authorization mechanism precisely controls which CredentialProviders each Agent identity can obtain credentials from.
secretRef.namesupports template variables, allowing different Secrets to be referenced dynamically based on context such as the Agent identity. This achieves "one configuration, different credentials per identity".
In addition toAPIKey,CredentialProvideralso supports types such asRAM, and supports sources such as KMS and RRSA. This topic only coverstype: APIKeywith a Kubernetes Secret source. For other types and sources, see the corresponding documentation.
Resource objects involved
Setting up API Key credential delivery typically involves the following CRs, which are created in sequence in the steps below:
Resource object | Description |
| Defines the Agent identity. It is the subject of authorization and credential delivery. |
| Defines the credential source. In this topic, |
| Defines permission rules that specify which CredentialProvider credentials an Agent is allowed to obtain. |
| Binds an AgentRole to a specified AgentIdentity to complete authorization. |
Applicability
The cluster Kubernetes version is >= 1.28.
On the Component Management page of the cluster, make sure that the version of the
ack-agent-identityadd-on is >= 0.2.0.
Configure an API Key credential
The following operations are performed with kubectl. Before you proceed, make sure that you have configured a kubeconfig that can access the target ACS cluster. For more information, see Use ACS with kubectl.
The following resources must be created in the same namespace: the Secret referenced by the CredentialProvider must reside in the same namespace as the CredentialProvider. Cross-namespace references are currently not supported.
Save the following content as
llm-api-key-secret.yamland run thekubectl apply -f llm-api-key-secret.yamlcommand to create the Kubernetes Secret that stores the API Key.apiVersion: v1 kind: Secret metadata: name: llm-api-key namespace: <YOUR_NAMESPACE> type: Opaque stringData: apiKey: "sk-xxxxxxxxxxxxxxxx" # Replace with the real API KeySave the following content as
agent-identity.yamland run thekubectl apply -f agent-identity.yamlcommand to create an AgentIdentity that defines the Agent identity.apiVersion: agentidentity.alibabacloud.com/v1alpha1 kind: AgentIdentity metadata: name: my-agent # The Agent identity name, referenced in later authorization namespace: <YOUR_NAMESPACE> spec: description: "Sample AI Agent identity"Save the following content as
credential-provider-apikey.yamland run thekubectl apply -f credential-provider-apikey.yamlcommand to create a CredentialProvider that references the preceding 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 # The name of the Secret created in Step 1 keyName: apiKey # The field name in the Secret that contains the API KeysecretRef.namesupports template variables that dynamically reference different Secrets based on context. For more information, see Template variables in secretRef.name.Save the following content as
agent-role-apikey.yamland run thekubectl apply -f agent-role-apikey.yamlcommand to create an AgentRole and an AgentRoleBinding that authorize the Agent identity to obtain credentials from this 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" # The name of the CredentialProvider created in the previous step --- 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 # Must match the AgentIdentity nameConfirm that the CredentialProvider is ready (the
Availablecolumn showsTrue).kubectl get credentialprovider llm-api-key -n <YOUR_NAMESPACE>Expected output:
NAME AVAILABLE AGE llm-api-key True 30sIf
Availableis notTrue, usekubectl describeto check the cause inConditions. For troubleshooting methods, see Status and troubleshooting.
Consume credentials
A created and authorized API Key credential is typically consumed through credential injection for Agent Sandbox egress traffic: reference this CredentialProvider in a tokenTransformation rule of a SecurityProfile. The Sandbox application initiates requests with a placeholder token, and the egress gateway automatically replaces it with the real API Key when forwarding. For the complete end-to-end configuration (SecurityProfile, SandboxSet, SandboxClaim, and verification), see Configure credential injection for Agent Sandbox.
Template variables in secretRef.name
In addition to a fixed Secret name, secretRef.name can also embed ${ack:agent-identity/...} template variables. When obtaining credentials, the system renders the actual Secret name based on the identity context of the current request, thereby delivering different credentials per identity. For example, llm-api-key-${ack:agent-identity/agent-name} resolves to llm-api-key-my-agent based on the Agent name.
The following template variables are supported:
Template variable | Description |
| The name of the AgentIdentity associated with the current Agent identity. |
| The value of the specified key in the custom metadata written when the identity token was issued. Replace |
If a template variable cannot be resolved in the current request context (for example, the referenced metadata key does not exist), the credential retrieval request fails with an explicit error message instead of silently returning an empty value.
The following example demonstrates how to dynamically reference different Secrets per tenant: the API Keys of different tenants are stored in Secrets named llm-api-key-<tenant identifier>, and the CredentialProvider resolves the corresponding Secret name through ${ack:agent-identity/metadata/tenant-id} when obtaining credentials.
The metadata values come from the custom metadata written when the identity token was issued. In Agent Sandbox scenarios, they can be passed in through the security.agents.kruise.io/<key> annotation of a SandboxClaim (the part after removing the prefix is the metadata key):
apiVersion: agents.kruise.io/v1alpha1
kind: SandboxClaim
metadata:
name: my-claim
namespace: <YOUR_NAMESPACE>
spec:
templateName: my-sandbox-set
replicas: 1
annotations:
# After removing the prefix, the following annotation can be referenced in templates via ${ack:agent-identity/metadata/tenant-id}
security.agents.kruise.io/tenant-id: acme
labels:
security.agents.kruise.io/agent-name: my-agentThe corresponding 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}' # Resolves to llm-api-key-acme
keyName: apiKeyWith the preceding configuration, when the tenant-id in the identity metadata of the request is acme, secretRef.name resolves to llm-api-key-acme, reading the Secret dedicated to that tenant.
Field descriptions
The spec structure for type: APIKey with a Kubernetes source is as follows:
spec:
type: APIKey # Required. Credential type. Cannot be changed after creation
apiKey:
source:
provider: Kubernetes # Required. Credential source
kubernetes:
secretRef:
name: <secret-name> # Required. Name of the Secret in the same namespace. Supports template variables
keyName: <key> # Required. Field name in the Secret dataKey field descriptions:
Field | Required | Description |
| Yes | Credential type. In this topic, set it to |
| Yes | Credential source. In this topic, set it to |
| Yes | The name of the referenced Kubernetes Secret. It must be in the same namespace as the CredentialProvider. Supports template variables. |
| Yes | Which field in the Secret |
Status and troubleshooting
The running status of a CredentialProvider is recorded in status.conditions. To view it, run kubectl describe credentialprovider <name> -n <YOUR_NAMESPACE>. Common Conditions and Reasons are as follows:
Condition / Reason | Description |
| The configuration passes validation and the credential source can be resolved properly. |
| The Secret pointed to by |
| The Secret exists, but it does not contain the field specified by |
WhensecretRef.nameuses a template variable, the actual Secret name can only be determined when credentials are obtained. Therefore, the reconciliation phase skips the Secret existence check and marks the CredentialProviderAvailabledirectly. Errors in such configurations (for example, the rendered Secret does not exist) surface when credentials are actually obtained, and are not reflected in the CredentialProvider status.
Limits
Limit | Description |
The Secret must be in the same namespace as the CredentialProvider |
|
Single-field reading | The APIKey type reads only the single field specified by |
FAQ
Why is the Available status of a CredentialProvider not True?
Troubleshoot with the following steps:
Run
kubectl describe credentialprovider <name> -n <YOUR_NAMESPACE>and check the Reason inConditions.If the Reason is
SecretNotFound: make sure thatsecretRef.namematches the actual Secret name, and that the Secret and the CredentialProvider are in the same namespace.If the Reason is
SecretKeyMissing: make sure thatkeyNamematches the field name in the Secretdata(you can runkubectl get secret <name> -n <YOUR_NAMESPACE> -o jsonpath='{.data}'to view the fields).
Why is the Agent denied (no permission) when obtaining credentials?
Make sure that the AgentRole (action: GetResourceCredential, resource: CredentialProvider/<name>) and the AgentRoleBinding have been created, and that the agentName of the AgentRoleBinding matches the name of the target AgentIdentity, and that all three are in the same namespace.