Skip to main content

Kubernetes External Secrets

External Secrets Operator (ESO) is a Kubernetes operator that synchronizes secrets from external secret management systems into Kubernetes Secrets. It supports multiple backends including AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, GCP Secret Manager, and many others.

This approach centralizes secret management in enterprise-grade secret stores while making secrets available to Kubernetes workloads through the native Secret API.


Supported Providers

ProviderDescription
AWS Secrets ManagerAWS managed secrets service
AWS Parameter StoreAWS Systems Manager Parameter Store
HashiCorp VaultSelf-hosted or HCP Vault
Azure Key VaultAzure managed secrets service
GCP Secret ManagerGoogle Cloud secrets service
AkeylessAkeyless Vaultless Platform
1Password1Password secrets automation
DopplerDoppler SecretOps platform
GitLabGitLab CI/CD variables

Installation

Using Helm

helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace

Using kubectl

kubectl apply -f https://raw.githubusercontent.com/external-secrets/external-secrets/main/deploy/crds/bundle.yaml
kubectl apply -f https://raw.githubusercontent.com/external-secrets/external-secrets/main/deploy/install.yaml

Core Concepts

SecretStore

Defines how to connect to a secret provider (namespace-scoped):

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: us-west-2
auth:
secretRef:
accessKeyIDSecretRef:
name: aws-credentials
key: access-key
secretAccessKeySecretRef:
name: aws-credentials
key: secret-key

ClusterSecretStore

Cluster-wide secret store available to all namespaces:

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"

ExternalSecret

Defines which secrets to sync:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: database-secret
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: production/database
property: username
- secretKey: password
remoteRef:
key: production/database
property: password

Provider Configuration Examples

AWS Secrets Manager

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secretsmanager
spec:
provider:
aws:
service: SecretsManager
region: us-west-2
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: api-keys
spec:
secretStoreRef:
name: aws-secretsmanager
kind: SecretStore
target:
name: api-keys
data:
- secretKey: api-key
remoteRef:
key: production/api-keys
property: api-key

HashiCorp Vault

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"
serviceAccountRef:
name: external-secrets
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: vault-secret
spec:
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: my-secret
data:
- secretKey: password
remoteRef:
key: secret/data/myapp
property: password

Azure Key Vault

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: azure-keyvault
spec:
provider:
azurekv:
vaultUrl: "https://my-keyvault.vault.azure.net"
authType: ManagedIdentity
identityId: "client-id-of-managed-identity"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: azure-secret
spec:
secretStoreRef:
name: azure-keyvault
kind: SecretStore
target:
name: my-azure-secret
data:
- secretKey: connection-string
remoteRef:
key: database-connection-string

GCP Secret Manager

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: gcp-secretmanager
spec:
provider:
gcpsm:
projectID: my-gcp-project
auth:
workloadIdentity:
clusterLocation: us-central1
clusterName: my-cluster
serviceAccountRef:
name: external-secrets-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: gcp-secret
spec:
secretStoreRef:
name: gcp-secretmanager
kind: SecretStore
target:
name: my-gcp-secret
data:
- secretKey: api-key
remoteRef:
key: api-key
version: latest

Advanced Features

Secret Templates

Transform secrets during synchronization:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: templated-secret
spec:
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: database-url
template:
type: Opaque
data:
DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@db.example.com:5432/mydb"
data:
- secretKey: username
remoteRef:
key: secret/data/database
property: username
- secretKey: password
remoteRef:
key: secret/data/database
property: password

Refresh Intervals

spec:
refreshInterval: 15m # Sync every 15 minutes

Data From Multiple Keys

spec:
dataFrom:
- extract:
key: production/all-secrets

Security Best Practices

  • Use workload identity: Prefer Kubernetes service account authentication over static credentials.
  • Scope SecretStores: Use namespace-scoped SecretStore instead of ClusterSecretStore when possible.
  • Enable audit logging: Track secret access in both Kubernetes and the external provider.
  • Set appropriate refresh intervals: Balance security (frequent rotation) with provider API limits.
  • Use RBAC: Restrict which namespaces can reference ClusterSecretStores.

Troubleshooting

Check ExternalSecret Status

kubectl get externalsecret database-credentials -n production
kubectl describe externalsecret database-credentials -n production

View Operator Logs

kubectl logs -n external-secrets -l app.kubernetes.io/name=external-secrets

Verify Synced Secret

kubectl get secret database-secret -n production -o yaml

References

This article is based on information from the following official sources:

  1. External Secrets Operator Documentation - External Secrets
  2. External Secrets GitHub Repository - GitHub
  3. AWS Secrets Manager - AWS Documentation