External Secrets Operator (ESO) is a Kubernetes operator that synchronizes secrets from external secret management systems (like AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault, etc.) into Kubernetes Secrets. This allows you to manage your sensitive data outside of Kubernetes, leveraging the robust security features of dedicated secret stores, while still making them available to your applications within Kubernetes as native Secrets.
Let’s see this in action with a simple example using AWS Secrets Manager.
Imagine you have a secret in AWS Secrets Manager named my-app/database-credentials. It contains a JSON object like this:
{
"username": "db_user",
"password": "supersecretpassword"
}
You want to make these credentials available as a Kubernetes Secret named database-credentials in your default namespace, which your application can then mount as a volume or read as environment variables.
First, you need to install External Secrets Operator. You can do this using Helm:
helm repo add external-secrets https://external-secrets.io/charts
helm repo update
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets-operator \
--create-namespace
Next, you need to define a SecretStore resource. This tells ESO where to find your external secrets and how to authenticate. For AWS Secrets Manager, it would look something like this:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager-store
namespace: default
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountToken: "" # This will be populated by the SA of the running pod
In this SecretStore, we specify SecretsManager as the service, the AWS region, and the authentication method. For AWS, ESO can often use the IAM role attached to the Kubernetes service account of the ESO pod itself (if configured appropriately with IRSA or similar). If not, you might need to provide explicit credentials, but using the SA’s IAM role is the recommended and more secure approach.
Now, you create an ExternalSecret resource. This is the resource that links your external secret to a Kubernetes Secret.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-credentials-secret
namespace: default
spec:
refreshInterval: "1h" # How often to check for updates in the external secret
secretStoreRef:
name: aws-secrets-manager-store
key: aws-secrets-manager-store # Defaults to the SecretStore name if not specified
target:
name: database-credentials # The name of the Kubernetes Secret to create
creationPolicy: Owner # The owner of the created secret
data:
- secretKey: username # The key in the Kubernetes Secret
remoteRef:
key: my-app/database-credentials # The name of the secret in AWS Secrets Manager
property: username # The specific property within the AWS secret (if it's JSON)
- secretKey: password
remoteRef:
key: my-app/database-credentials
property: password
When you apply this ExternalSecret manifest, ESO will:
- Read the
ExternalSecretresource. - Consult the
secretStoreRefto find theaws-secrets-manager-store. - Use the authentication method defined in the
SecretStoreto connect to AWS Secrets Manager. - Fetch the secret named
my-app/database-credentialsfrom AWS Secrets Manager. - Extract the
usernameandpasswordproperties from the fetched secret. - Create or update a Kubernetes
Secretnameddatabase-credentialsin thedefaultnamespace, containingusernameandpasswordkeys with their respective values.
Your application pods can then consume this database-credentials Secret like any other Kubernetes secret:
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: default
spec:
containers:
- name: app
image: nginx
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: database-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: database-credentials
key: password
volumeMounts:
- name: db-credentials-volume
mountPath: "/etc/secrets"
volumes:
- name: db-credentials-volume
secret:
secretName: database-credentials
The most surprising true thing about External Secrets Operator is that it doesn’t store secrets itself; it acts as a highly efficient, event-driven bridge. When a Kubernetes Secret is updated or created via an ExternalSecret resource, ESO reacts, fetches the latest from the external store, and updates the Kubernetes Secret. It’s a reactive system that ensures your in-cluster secrets are always a faithful, up-to-date reflection of your external secrets.
The refreshInterval on the ExternalSecret is crucial. It dictates how often ESO will poll the external secret store for changes. A shorter interval means faster propagation of updates but increased API calls to your secret manager. A longer interval means slower propagation but fewer API calls. If your secrets change very frequently and your applications need those changes immediately, you might consider a very short interval or explore more advanced reconciliation mechanisms if your secret store supports event notifications.
The creationPolicy field on the ExternalSecret’s target is also significant. Owner is the default and recommended setting, meaning the ExternalSecret resource will be the owner of the generated Kubernetes Secret. If the ExternalSecret is deleted, the generated Secret will also be garbage collected. Other options include Merge (merges data into an existing secret) and Orphan (doesn’t set an owner reference).
One thing that often trips people up is how ESO handles updates to secrets that contain multiple key-value pairs. When you define data entries in your ExternalSecret, ESO expects a direct mapping. If your external secret is a JSON blob, you use the property field to extract specific keys. If it’s a plain string, you’d omit property and the entire string content would be placed under the specified secretKey. If you change the structure of your external secret (e.g., from plain text to JSON or vice-versa), you’ll need to adjust your ExternalSecret definition accordingly.
The next concept you’ll likely encounter is how to manage secrets that require more complex transformations or conditional logic, which might lead you to explore custom ExternalSecret templates or more advanced operators that build upon ESO’s foundation.