External Secrets Operator
This guide shows how to integrate the Akeyless Platform with the External Secrets Operator (ESO) to synchronize secrets between Akeyless and Kubernetes.
It covers:
- Installing ESO with Helm
- Configuring authentication from Kubernetes to Akeyless (API Key, Kubernetes Auth, Azure AD, and other cloud identities)
- Using SecretStore, ClusterSecretStore, and ExternalSecret to fetch secrets
- Using PushSecret to push Kubernetes secrets back to Akeyless
- Example usage
How ESO Works with Akeyless
The External Secrets Operator is a Kubernetes operator that reads secrets from external systems (such as Akeyless) and creates or updates standard Kubernetes Secret objects in a Kubernetes cluster.
For Akeyless, ESO uses the following custom resources:
SecretStore: namespaced definition of how to connect and authenticate to Akeyless.ClusterSecretStore: cluster-wide variant ofSecretStore.ExternalSecret: defines which Akeyless items to sync into which Kubernetes Secret object.PushSecret: pushes a Kubernetes Secret from the cluster into Akeyless.
At a high level:
- ESO authenticates to Akeyless using the method specified in
SecretStoreorClusterSecretStore. - ESO fetches secrets from Akeyless.
- ESO writes those values into Kubernetes Secrets (or pushes them back to Akeyless in the case of
PushSecret). - ESO periodically refreshes secrets according to the
refreshIntervalon eachExternalSecretorPushSecret.
Architecture & Resources
In-cluster components:
- The ESO controller runs as a Kubernetes Deployment.
- It watches custom resources:
ExternalSecret,SecretStore,ClusterSecretStore, andPushSecret. - It reconciles the desired state by calling Akeyless APIs to create and update Kubernetes Secret objects.
Akeyless side:
- Akeyless Authentication Methods define how Kubernetes workloads authenticate (e.g., API Key, Kubernetes Auth, Azure AD, AWS IAM, GCP).
- Access Roles control which Akeyless items (paths) a given authentication identity may access.
- ESO uses an Access ID (and additional auth parameters) to obtain a token and read or write secrets.
Prerequisites
- A running Kubernetes cluster, v1.16+ (ESO requirement).
- Helm installed locally.
- An Akeyless tenant with:
- At least one Authentication Method (API Key, Kubernetes Auth, Azure AD, AWS IAM, or GCP).
- An Access Role that grants read or write permissions to the relevant secrets.
- For Kubernetes Auth, private deployments, or hybrid deployments:
- An Akeyless Gateway with network access to the Kubernetes API server.
Installing External Secrets Operator with Helm
- Add the official ESO Helm repository:
helm repo add external-secrets https://charts.external-secrets.io
helm repo update- Install ESO (default configuration):
helm install external-secrets external-secrets/external-secrets --namespace external-secrets --create-namespaceThe ESO controller pods running in the external-secrets namespace should now be running.
Authentication with Akeyless
ESO’s Akeyless provider supports the following access types:
-
api_key -
k8s -
azure_adFor AKS Workload Identity, use the Azure AD Authentication Method (
accessType: azure_ad).When using
accessType: azure_ad, ESO relies on the identity of the KubernetesServiceAccountthat runs theExternalSecret. Identity is not selected through theSecretStore. For full guidance, see ESO and AKS Workload Identity. -
aws_iam -
gcp
OIDC is not supported as an access type at this time.
Each Authentication Method in Akeyless exposes an Access ID, and for some methods an additional parameter (accessTypeParam) such as:
- API Key →
accessTypeParam: Akeyless Access Key - Kubernetes Auth →
accessTypeParam: Kubernetes Auth config name - Azure AD →
accessTypeParam: Azure Object ID (optional)- See ESO and AKS Workload Identity for more information.
- GCP →
accessTypeParam: GCP audience - AWS IAM →
accessTypeParam: not required
ESO can authenticate in one of two ways:
- Using a credentials Secret (generic pattern, works for all supported access types).
- Using Kubernetes Auth-specific fields (directly referencing a Kubernetes ServiceAccount and/or JWT).
Creating a Credentials Secret
Store the Akeyless credentials that ESO should use within a Kubernetes Secret:
apiVersion: v1
kind: Secret
metadata:
name: akeyless-secret-creds
namespace: akeyless-demo
type: Opaque
stringData:
accessId: "p-XXXX" # Access ID of the Akeyless Auth Method
accessType: "api_key" # api_key / k8s / azure_ad / aws_iam / gcp
accessTypeParam: "<access-key>"API Key Example (NOT Recommended for Production)
apiVersion: v1
kind: Secret
metadata:
name: akeyless-api-creds
namespace: akeyless-demo
type: Opaque
stringData:
accessId: "<p-xxxxxxxxxxxxxxxx>"
accessType: "api_key"
accessTypeParam: "<YOUR-ACCESS-KEY-HERE>"Use api_key for quick demos or POCs. For production, prefer workload identities (Kubernetes Auth, Azure AD, AWS IAM, GCP).
Kubernetes Auth Example
apiVersion: v1
kind: Secret
metadata:
name: akeyless-k8s-creds
namespace: akeyless-demo
type: Opaque
stringData:
accessId: "<p-k8saccessid>"
accessType: "k8s"
accessTypeParam: "<my-k8s-auth-config-name>"accessId: Access ID for the Kubernetes Auth method.accessTypeParam: Name of the Kubernetes Auth config for the cluster.
Azure AD Example (Managed Identity or Service Principal)
apiVersion: v1
kind: Secret
metadata:
name: akeyless-azure-creds
namespace: akeyless-demo
type: Opaque
stringData:
accessId: "<p-xxxxx>"
accessType: "azure_ad"
accessTypeParam: "" # Optional: Azure Object ID; can be left empty if using sub-claims such as xms_miridThis Secret is suitable when using Azure AD Managed Identity with sub-claim enforcement.
ESO does not implement Azure Workload Identity flows internally. When
accessType: azure_adis used, ESO relies entirely on the Azure AD token available in the pod’s environment. This is typically projected via AKS Workload Identity. Therefore, the identity ESO uses depends on theServiceAccounttheExternalSecretruns under, not on theSecretStore.
SecretStore: Namespaced Secret Provider
SecretStore: Namespaced Secret ProviderSecretStore (Using a Credentials Secret)
SecretStore (Using a Credentials Secret)Create a SecretStore resource which defines how ESO connects to Akeyless within a single namespace.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: akeyless-secret-store
namespace: akeyless-demo
spec:
provider:
akeyless:
# Public SaaS API
akeylessGWApiURL: "https://api.akeyless.io"
authSecretRef:
secretRef:
accessID:
name: akeyless-secret-creds
key: accessId
accessType:
name: akeyless-secret-creds
key: accessType
accessTypeParam:
name: akeyless-secret-creds
key: accessTypeParamIf using a private Akeyless Gateway (for example in a zero-knowledge or hybrid deployment), set:
akeylessGWApiURL: "https://<the.akeyless.gw:8080>/v2"Custom CAs can be configured via caBundle or caProvider if the Akeyless gateway uses a private CA.
SecretStore (Direct Kubernetes Auth)
SecretStore (Direct Kubernetes Auth)Alternatively, Kubernetes Auth can be configured directly in the SecretStore without a generic credentials Secret.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: akeyless-k8s-secret-store
namespace: akeyless-demo
spec:
provider:
akeyless:
akeylessGWApiURL: "https://api.akeyless.io"
authSecretRef:
kubernetesAuth:
accessID: "p-k8saccessid"
k8sConfName: "my-k8s-auth-config-name"
serviceAccountRef:
name: "akeyless-demo-sa"
# Optional: Use a specific Secret containing a ServiceAccount JWT
secretRef:
name: "akeyless-demo-sa-token"
key: "token"Key fields:
accessID: Access ID of the Kubernetes Auth method.k8sConfName: Kubernetes Auth config name attached to the cluster.serviceAccountRef:ServiceAccountthat ESO uses to request and project tokens.secretRef: Optional; explicit Secret containing a SA token ESO should use.
ClusterSecretStore: Cluster-Wide Secret Provider
ClusterSecretStore: Cluster-Wide Secret ProviderA ClusterSecretStore is a cluster-scoped provider configuration that can be used by ExternalSecret resources in any namespace.
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: akeyless-cluster-secret-store
spec:
provider:
akeyless:
akeylessGWApiURL: "https://api.akeyless.io"
authSecretRef:
secretRef:
accessID:
name: akeyless-secret-creds
key: accessId
namespace: akeyless-demo
accessType:
name: akeyless-secret-creds
key: accessType
namespace: akeyless-demo
accessTypeParam:
name: akeyless-secret-creds
key: accessTypeParam
namespace: akeyless-demoFor a ClusterSecretStore object, the namespace fields are required for secretRef.accessID, secretRef.accessType, and secretRef.accessTypeParam (and for any serviceAccountRef or secretRef when using the Kubernetes authentication method).
When using ClusterSecretStore, the referencing ExternalSecret must set secretStoreRef to use the ClusterSecretStore as opposed to a SecretStore:
secretStoreRef:
kind: ClusterSecretStore
name: akeyless-cluster-secret-storeRemember that any namespace using this ClusterSecretStore must be authorized in the Akeyless platform with appropriate roles and claims.
ExternalSecret: Syncing Akeyless Secrets into Kubernetes
ExternalSecret: Syncing Akeyless Secrets into KubernetesTo fetch an Akeyless secret and store it as a Kubernetes Secret, define an ExternalSecret resource.
Basic ExternalSecret (Single Values)
ExternalSecret (Single Values)apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: akeyless-external-secret
namespace: akeyless-demo
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: akeyless-secret-store
target:
name: app-config-secret
creationPolicy: Owner
data:
- secretKey: api-key
remoteRef:
key: /path/to/the/secret/api-key
- secretKey: db-password
remoteRef:
key: /path/to/the/secret/db-passwordrefreshInterval: How often ESO refreshes values from Akeyless.secretStoreRef: WhichSecretStoreorClusterSecretStoreto use.target.name: Name of the Kubernetes Secret created.data[*].secretKey: Key name inside the Kubernetes Secret.data[*].remoteRef.key: Full path of the item in Akeyless.
Retrieve values:
kubectl get secret app-config-secret -n akeyless-demo -o jsonpath='{.data.api-key}' | base64 -dUsing dataFrom to Extract JSON
dataFrom to Extract JSONIf an Akeyless secret contains JSON,dataFrom.extract can be used to split that JSON into multiple keys in the Kubernetes Secret.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: akeyless-external-secret-json
namespace: akeyless-demo
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: akeyless-secret-store
target:
name: app-config-json
creationPolicy: Owner
dataFrom:
- extract:
key: /path/to/the/json-secretIf the JSON value in Akeyless is:
{
"username": "demo",
"password": "s3cr3t"
}Then the resulting Kubernetes Secret app-config-json will contain two keys: username and password.
To inspect all keys:
kubectl get secret app-config-json -o jsonpath='{.data}'Certificates: Splitting Certificate and Private Key
Akeyless certificate items typically contain separate PEM blocks for the certificate and private key. They can be mapped to tls.crt and tls.key in a Kubernetes TLS Secret.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: akeyless-tls-secret
namespace: akeyless-demo
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: akeyless-secret-store
target:
name: my-tls-secret
creationPolicy: Owner
template:
type: kubernetes.io/tls
data:
- secretKey: tls.crt
remoteRef:
key: /path/to/the/certificate-item
property: certificate_pem
- secretKey: tls.key
remoteRef:
key: /path/to/the/certificate-item
property: private_key_pemNow my-tls-secret can be used with a Kubernetes Ingress or other resource expecting a TLS Secret.
PushSecret: Push Kubernetes Secrets into Akeyless
PushSecret: Push Kubernetes Secrets into AkeylessPushSecret is used to push local Kubernetes Secrets into Akeyless, enabling a GitOps-friendly workflow where Kubernetes becomes the source of truth for some secrets.
Create a Local Kubernetes Secret
kubectl create secret generic --from-literal=cache-pass=mypassword k8s-created-secret -n akeyless-demoDefine the PushSecret Resource
PushSecret ResourceapiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: push-secret
namespace: akeyless-demo
spec:
refreshInterval: 5s
updatePolicy: Replace
deletionPolicy: Delete
secretStoreRefs:
- name: akeyless-secret-store
kind: SecretStore
selector:
secret:
name: k8s-created-secret
data:
- match:
remoteRef:
remoteKey: eso-created/my-secretKey fields:
refreshInterval: How often ESO checks for changes in the Kubernetes Secret.updatePolicy: Whether to replace or merge when updating the provider secret.deletionPolicy: Whether to delete the provider secret when thePushSecretresource is deleted.remoteKey: Path where the secret will be stored in Akeyless.
Applying this manifest will create an Akeyless secret named eso-created/my-secret whose value is derived from k8s-created-secret (for example {"cache-pass":"mypassword"}).
Azure AD Managed Identity: Sub-Claim Example
This section illustrates how to use Azure AD Managed Identity on AKS in combination with an Akeyless Azure AD Authentication Method that enforces sub-claims, such as xms_mirid (Managed Identity resource ID) and oid (user/object ID).
Example: Akeyless Azure AD Auth Method with Sub-Claims
Below is a truncated example of an Azure AD Auth Method with role associations and sub-claims that bind the role to specific identities:
{
"name": "devops/azure/akeyless-azure-ad-auth",
"auth_method_access_id": "p-xxxxx",
"access_info": {
"rules_type": "azure_ad",
"force_sub_claims": true,
"azure_ad_access_rules": {
"issuer": "https://sts.windows.net/<tenant-id>/",
"jwks_uri": "https://login.microsoftonline.com/common/discovery/keys",
"bound_tenant_id": "<tenant-id>"
}
},
"auth_method_roles_assoc": [
{
"role_name": "devops/devops-api-role",
"auth_method_sub_claims": {
"xms_mirid": [
"/subscriptions/.../resourcegroups/.../providers/Microsoft.ManagedIdentity/userAssignedIdentities/identities"
]
}
},
{
"role_name": "devops/devops-api-role",
"auth_method_sub_claims": {
"oid": [
"11108008-9999-abcd-1234-ab123456abc1"
]
}
},
{
"role_name": "devops/devops-api-role",
"auth_method_sub_claims": {
"xms_mirid": [
"/subscriptions/.../userAssignedIdentities/UserAkeylessGWManagedID",
"/subscriptions/.../userAssignedIdentities/identities"
]
}
}
]
}In this configuration:
force_sub_claims: truerequires that at least one sub-claim match for access to be granted.auth_method_sub_claims.xms_miridbinds access to specific user-assigned Managed Identities.auth_method_sub_claims.oidcan bind access to specific Azure AD identities (for example, human users or service principals).
The associated role devops/devops-api-role typically grants read, list, create, update, and delete capabilities on paths such as /devops/ and /SPIRE/.
Kubernetes Manifests for ESO Using Azure AD Auth
The following manifests show how to use the above Auth Method from an AKS cluster with ESO.
Credentials Secret (Azure AD):
apiVersion: v1
kind: Secret
metadata:
name: akeyless-azure-creds
namespace: app-test
type: Opaque
stringData:
accessId: "p-xxxxx"
accessType: "azure_ad" # Use Azure AD Auth Method
accessTypeParam: "" # Required field; can be empty when binding via sub-claimsSecretStore referencing the Azure AD credentials:
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: akeyless-store
namespace: app-test
spec:
provider:
akeyless:
akeylessGWApiURL: "https://api.akeyless.io"
# Optional: use a specific ServiceAccount for ESO controller in this namespace
serviceAccountRef:
name: app-test
authSecretRef:
secretRef:
accessID:
name: akeyless-azure-creds
key: accessId
accessType:
name: akeyless-azure-creds
key: accessType
accessTypeParam:
name: akeyless-azure-creds
key: accessTypeParamExternalSecret consuming a Secret via the SecretStore:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-api-secret
namespace: app-test
spec:
refreshInterval: 1h
secretStoreRef:
name: akeyless-store
kind: SecretStore
target:
name: app-api-secret
creationPolicy: Owner
data:
- secretKey: api-key
remoteRef:
key: /devops/static_secret_1Retrieving the synced secret:
kubectl -n app-test get secret app-api-secret -o jsonpath="{.data.api-key}" | base64 -dThis pattern ties together:
- AKS nodes or workloads using Managed Identity,
- An Akeyless Azure AD Auth Method with sub-claim constraints, and
- ESO as the consumer that syncs secrets into Kubernetes.
Tutorial
For a hands-on walkthrough, check out our tutorial video on Sync Secrets to Kubernetes with External Secrets Operator (ESO).
Updated about 1 hour ago
