Secrets Management on EKS

A Kubernetes Secret is a delivery mechanism, not a vault. Its values are base64-encoded, stored in etcd, and readable by anyone with get on Secrets in the namespace — and by anyone who can create a pod there. Secrets management on EKS is about deciding where the source of truth lives and how values reach pods with the least exposure.

The fundamentals are in Secrets (concepts) and secret encryption.

Notes in this section

  • AWS Secrets Manager — the Secrets Store CSI Driver with the AWS provider, External Secrets Operator, rotation, and what syncing to a Kubernetes Secret gives up.
  • Sealed Secrets — encrypted secrets in Git, decrypted by a controller in the cluster; scopes, key rotation and backup.

What EKS does by default

  • Encryption at rest. EKS encrypts etcd volumes, and newer clusters apply envelope encryption to all Kubernetes API data by default. You can supply your own KMS key for Secrets, which adds key-policy control and an audit trail of decryptions in CloudTrail.
  • Encryption in transit. All API traffic is TLS.

Neither protects against someone who is authorised to read the Secret through the API. That is what RBAC and the choices below are for.

The approaches

ApproachSource of truthReaches the pod asStored in etcdRotation
Plain Kubernetes SecretsThe clusterEnv vars or filesYesManual
Secrets Store CSI Driver + ASCPSecrets Manager / SSMFiles on a tmpfs volumeNo (optional)Built in
External Secrets OperatorSecrets Manager / SSMA synced Kubernetes SecretYesBuilt in
Sealed SecretsGit (encrypted)A Kubernetes SecretYesManual
SOPS with KMSGit (encrypted)A Kubernetes SecretYesManual
No secret at allIAMTemporary credentialsNoAutomatic

The last row is the most important. Most “secrets” on EKS are AWS credentials, and those should not exist: give the pod an IAM role through Pod Identity or IRSA and let the SDK fetch short-lived credentials. The same goes for databases that support IAM authentication. The best-managed secret is the one you eliminated.

Choosing

SituationRecommendation
The pod needs to call AWS APIsPod Identity — no secret
Compliance requires that secrets never rest in etcdCSI driver with mounted files, no sync
Applications expect environment variables or ordinary SecretsExternal Secrets Operator
Automatic rotation and a central audit trail matterSecrets Manager with either tool
Everything must live in Git; no dependency on a cloud APISealed Secrets
Secrets in Git, with AWS managing the keysSOPS with KMS (native in Flux)
Fargate-only workloadsExternal Secrets Operator (the CSI driver needs a DaemonSet)

Mixing is normal: Pod Identity for AWS access, Secrets Manager for third-party API keys and database passwords, and nothing sensitive in Git in plain text.

Practices that apply whichever tool you choose

  1. Least privilege on both sides. Narrow IAM permissions to specific secret paths, and narrow Kubernetes RBAC on secrets — list and watch reveal values just as get does.
  2. Prefer mounted files to environment variables. Environment variables leak into crash dumps, debug output and child processes, and never update in a running process.
  3. Rotate, and make applications tolerate it: re-read the file, or restart on change.
  4. Separate by namespace and by environment. One service account, one role, one set of secrets.
  5. Keep secrets out of images, manifests, logs and CI output — scan for them: secrets detection.
  6. Audit access. CloudTrail for Secrets Manager and KMS; Kubernetes audit logs for Secret reads.
  7. Plan recovery. Know how secrets are restored after a cluster loss — especially the sealing keys for Sealed Secrets.