Use API Key credentials stored in a Kubernetes Secret by configuring a CredentialProvider

Updated at:

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.name supports 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 to APIKey, CredentialProvider also supports types such as RAM, and supports sources such as KMS and RRSA. This topic only covers type: APIKey with 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

AgentIdentity

Defines the Agent identity. It is the subject of authorization and credential delivery.

CredentialProvider

Defines the credential source. In this topic, type: APIKey, and the API Key is read from a Kubernetes Secret.

AgentRole

Defines permission rules that specify which CredentialProvider credentials an Agent is allowed to obtain.

AgentRoleBinding

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-identity add-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.

  1. Save the following content as llm-api-key-secret.yaml and run the kubectl apply -f llm-api-key-secret.yaml command 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 Key
  2. Save the following content as agent-identity.yaml and run the kubectl apply -f agent-identity.yaml command 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"
  3. Save the following content as credential-provider-apikey.yaml and run the kubectl apply -f credential-provider-apikey.yaml command 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 Key
    secretRef.name supports template variables that dynamically reference different Secrets based on context. For more information, see Template variables in secretRef.name.
  4. Save the following content as agent-role-apikey.yaml and run the kubectl apply -f agent-role-apikey.yaml command 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 name
  5. Confirm that the CredentialProvider is ready (the Available column shows True).

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

    Expected output:

    NAME          AVAILABLE   AGE
    llm-api-key   True        30s

    If Available is not True, use kubectl describe to check the cause in Conditions. 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

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

The name of the AgentIdentity associated with the current Agent identity.

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

The value of the specified key in the custom metadata written when the identity token was issued. Replace <key> with the actual key name.

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-agent

The 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: apiKey

With 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 data

Key field descriptions:

Field

Required

Description

spec.type

Yes

Credential type. In this topic, set it to APIKey. It cannot be changed after creation (any modification is rejected by validation).

spec.apiKey.source.provider

Yes

Credential source. In this topic, set it to Kubernetes. If KMS is enabled in the cluster, it can also be set to KMS (see the documentation for the KMS source).

spec.apiKey.source.kubernetes.secretRef.name

Yes

The name of the referenced Kubernetes Secret. It must be in the same namespace as the CredentialProvider. Supports template variables.

spec.apiKey.source.kubernetes.keyName

Yes

Which field in the Secret data to read as the API Key. Required for the APIKey type.

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

Available = True

The configuration passes validation and the credential source can be resolved properly.

Degraded, Reason=SecretNotFound

The Secret pointed to by secretRef.name does not exist (check whether the Secret name and namespace are correct).

Degraded, Reason=SecretKeyMissing

The Secret exists, but it does not contain the field specified by keyName.

When secretRef.name uses 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 CredentialProvider Available directly. 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

secretRef supports referencing only Secrets in the same namespace. Cross-namespace references are not supported.

Single-field reading

The APIKey type reads only the single field specified by keyName from the Secret. keyName is required.

FAQ

Why is the Available status of a CredentialProvider not True?

Troubleshoot with the following steps:

  1. Run kubectl describe credentialprovider <name> -n <YOUR_NAMESPACE> and check the Reason in Conditions.

  2. If the Reason is SecretNotFound: make sure that secretRef.name matches the actual Secret name, and that the Secret and the CredentialProvider are in the same namespace.

  3. If the Reason is SecretKeyMissing: make sure that keyName matches the field name in the Secret data (you can run kubectl 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.

Related documentation