Kubernetes Secrets Management: What Nobody Tells You About External KMS, Vaults, and Encryption

Home >> TECHNOLOGY >> Kubernetes Secrets Management: What Nobody Tells You About External KMS, Vaults, and Encryption
Share

Last updated on September 18th, 2026 at 04:03 pm

Most Kubernetes introductions show how to create a Secret item in just three lines of YAML. They never reveal that their secrets, by default, are base64-encoded strings in etcd, that aren’t encrypted, that aren’t rotated or audited.

It’s no small mistake. That’s a security hole that has been exploited in real life.

Base64 is encoding and NOT encryption. It only takes a moment, and anyone with etcd access can crack your database password. But if RBAC isn’t managed closely in clusters, that’s a much greater number of individuals than you might think.

Real secrets management starts at the architecture level—not at the kubectl create secret step.

What “Secrets Management in Kubernetes” Actually Means

It is good to be specific about the problem space before starting work on the tools.
Kubernetes secrets management addresses 3 different concerns:

  • Storage Security: Do secrets get encrypted at rest in etcd?
  • Access control: Who/what may read a secret, and when?
  • Lifecycle management: How are secrets rotated, versioned, and revoked?

Most teams solve 1 of these. Mature setups can address all three. The difference between the two states is most often where incidents occur.

Secrets Management in Kubernetes: External KMS, Vaults, and Encryption – The Real Architecture Breakdown

Encryption at Rest with KMS Providers

Kubernetes supports envelope encryption for storing secrets in etcd. The DEK (Secret) encrypts the secret; the KEK encrypts the DEK. The KEK exists outside the cluster and is stored in a Key Management Service, such as AWS KMS, Google Cloud KMS, or Azure Key Vault.

Even if someone drops your etcd, you only get encrypted blobs. Otherwise, they are worthless blobs.

Creating this will involve creating an EncryptionConfiguration file on the API server itself:

apiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources:  - resources:      - secrets    providers:      - kms:          name: myKMSPlugin          endpoint: unix:///tmp/socketfile.sock          apiVersion: v2      - identity: {}

The final identity provider is a “last resort” and a footgun. Without KMS, Kubernetes stores secrets unencrypted. I see teams miss this step, set it up, and forget about it, which makes it a silent Time Capsule winner.

This printer was observed in a staging environment, where the KMS plugin’s connectivity failed, and secrets were being delivered in a plain old fashion. The team saw no warnings and no errors. Worth auditing explicitly.

HashiCorp Vault – More Than Just a Secret Store

For reasons that shall remain a mystery, Vault is the most widely used external secrets backend in the Kubernetes world, and for good reason. Supports out-of-the-box dynamic secret generation, short-lived credentials, fine-grained policies, and full audit logging.

The team typically uses the Vault Agent Sidecar Injector pattern. Vault deploys an init container and sidecar to your pods, which authenticate via Kubernetes service accounts and extract secrets to a shared in-memory volume.

You shouldn’t have any secrets in the environment variables no secrets in Secret objects in Kubernetes. No data is stored; the pod gets what it needs at runtime.

In this, it excels:

  • Database credentials – rotating hourly.
  • Dynamic certificates for each of the services provided. Vault generates dynamic certificates for each required service.
  • Assigning cloud provider credentials with narrowly-drafted IAM policies

When things get tricky:

  • Operational investment in the HA setup at Vault is vital.
  • When Vault is sealed and the agent can’t authenticate, the cold start will take a long time.
  • Don’t mess around at 2 AM if you have a problem with sidecar injection; it’s not fun, okay?

While sidecar has been the recommended approach, I have tried this in a few different cluster configurations and consistently get good results using Vault Kubernetes auth in tandem with the External Secrets Operator (ESO). It provides more granular control over secret syncing and supports drift detection.

External Secrets Operator – The Bridge That Makes Sense

ESO is an open-source operator that securely accesses external secret stores (like Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, and so on) and transforms the contents into native Kubernetes Secret objects.

The process is as follows:

  • You create a SecretStore or ClusterSecretStore that references your external SecretStore.
  • You define an ExternalSecret resource which correlates specific keys in the external store to a Kubernetes Secret.
  • ESO fetches, syncs, and optionally rotates the secret every N minutes.
apiVersion: external-secrets.io/v1beta1kind: ExternalSecretmetadata:  name: db-credentialsspec:  refreshInterval: 1h  secretStoreRef:    name: vault-backend    kind: SecretStore  target:    name: db-secret  data:    - secretKey: DB_PASSWORD      remoteRef:        key: secret/data/prod/db        property: password

What you get is a standard Kubernetes Secret that ESO owns, and automatically refreshed at ESO’s mercy. ESO detects changes to the underlying value in Vault and updates the Secret without involving users.

It means as much as it does as a sound bite. When teams only rotate the hands out in panic mode, it is their own fault. The possibility of human error is eliminated by automating it.
For teams committed to securing Kubernetes, ESO is fast becoming the bare minimum—not an add-on—the Environment Variable Trap.

Many articles skim over this; sometimes, even after making Vault and ESO work, they end up in environment variables, and that is a bad thing.

The environment variables that are available in a container are:

  • Available to any process in the container
  • Found by certain frameworks on crashes or debugging
  • If isolation between the container and the host isn’t perfect, the host can access the/proc/environ file.

The preferred alternative is to have secret files in an in-memory tmpfs. The data is not stored on disk, and the application reads it from the filesystem path.

volumes:  - name: secrets-vol    emptyDir:      medium: MemoryvolumeMounts:  - name: secrets-vol    mountPath: /var/secrets    readOnly: true

Though it is a minor pod spec change, this has a dramatic impact on the security posture – particularly in environments where Kubernetes Network Security policies are not consistently blocking intra-pod traffic.

My Take on Cloud-Native KMS Options

AWS Secrets Manager + ASCP

AWS Secrets and Configuration Provider (ASCP) provides the AWS Secrets Store CSI Driver. It mounts secrets directly as volumes via the CSI interface, with authentication using IRSA (IAM Roles for Service Accounts).

If you’re already on AWS, the integration is tight. The drawback: vendor lock-in – too late to move to GCP or multi-cloud later on and have to re-imagine your secret fetching layer.

GCP Secret Manager

Very much like Google’s offering, but less polluted by its IAM model. The External Secrets Operator supports Workload Identity well. The External Secrets Operator has good support for Workload Identity (WI).

Unlike AWS, my experience shows that GCP’s audit logging for secret access is also more granular out of the box. Every attempt to access a secret appears in the Cloud Audit Logs without setting anything up.

Azure Key Vault

While CSI’s implementation for Azure is working now, it has had a few bumps in the road with identifying pod identity. Things improved significantly with the transition from AAD Pod Identity to Azure AD Workload Identity (via Workload Identity). However, it still wasn’t as seamless as it could be without a migration effort on existing clusters.

What Most People Misunderstand About Secret Rotation

But the world of rotation seems simple – change the number, update the references, woop dee doo, that’s it! In practice, it’s a distributed coordination problem.

Once a secret rotates, if no running pod understands it, that pod begins to fail. The naive solution is to restart all pods after a rotation. What is more correct is to create applications that can deal with secret reload without rebooting – that is, reading it every time it is requested, or every time it’s refreshed after a fixed period.

Some frameworks include built-in support for this. Others require a container. Consider this before building the rotation pipeline.

It is important that you know two patterns:

  • Blue/Green secret rotation – Duplicating the new secret on the old one, gradually moving the consumers in each of those versions over to the new one, and finally removing the old one. When using on-behalf-of, you can map database passwords to a particular secret version in your config with versioned secret references. When rotation occurs, it becomes an explicit, auditable deployment change.

Audit Logging – The Part Everyone Skips Until They Need It

Log access to secrets. This is not “a pod read a secret”; this is which service account, which pod, at what time, with what specific key.

When you enable/configure the right verbosity for Kubernetes API audit logs, you can see secret read events too. Vault has a native audit backend that does just this. Both AWS and GCP expose it in their respective logging systems.

It is the teams that do not do this step that have to scramble around after an incident, asking the other teams, “How long did the attacker have access?

Creating a simple audit policy for Kubernetes:

apiVersion: audit.k8s.io/v1kind: Policyrules:  - level: Metadata    resources:      - group: ""        resources: ["secrets"]

Stores metadata parameters (Who, When, What resource, but not secret value!) It’s a good place to start without polluting your audit log with sensitive data.

Putting It Together – A Practical Stack That Works

What follows is a realistic configuration for production that is secure, complex enough for operations, and maintainable:

Secret StoreHashiCorp Vault (or AWS/GCP equivalent)Dynamic secrets, fine-grained policies, audit logs
Sync LayerExternal Secrets OperatorDecoupled from pod lifecycle, supports multiple backends
Encryption at RestKMS envelope encryption on etcdProtects against etcd compromise
Access ControlKubernetes RBAC + Vault policiesDefense in depth
Secret DeliveryFilesystem mounts (tmpfs)Avoids environment variable exposure
AuditingKubernetes audit policy + Vault audit backendFull access trail

This isn’t the only valid architecture. However, it resolves all three issues related to storage, access, and lifecycle without having to build a custom system to solve these problems.

Honest Recommendation – Who Needs What

If you are a small team on a managed cluster (EKS, GKE, AKS): You can begin with your cloud provider’s native secrets integration and ESO. This is because it incurs minimal operating costs and works for most workloads.

For sensitive data, compliance, or multi-cloud configurations, Vault is worth the operational investment. It’s more than sufficient because of audit logging and dynamic credential generation.

If you’re continuing to use Base64-encoded secrets in plain YAML in a repository: Stop. It’s the top priority to fix, regardless of anything else (what you do or don’t do).

Secrets management is no bed of roses in Kubernetes. It is not visible in the demo. Yet getting it wrong can have repercussions at inconvenient times.

Leave a Reply

Your email address will not be published. Required fields are marked *