Back to blog
AWSBest PracticesCloud SecurityCompute & ContainersKubernetes

EKS Secrets Not Encrypted: Enable Envelope Encryption on Your Clusters

Learn why EKS secrets need KMS envelope encryption, how to enable it with CLI and Terraform, and how to enforce it in CI to keep clusters compliant.

TL;DR

This check flags EKS clusters that store Kubernetes secrets in etcd without envelope encryption. Without it, anyone with etcd or backup access can read your secrets in plaintext. Fix it by enabling KMS envelope encryption on the cluster.

Kubernetes secrets hold the keys to your kingdom: database passwords, API tokens, TLS private keys, and service account credentials. On EKS, those secrets live inside etcd, the control plane datastore that AWS manages for you. By default, EKS encrypts etcd at rest using AWS-managed keys at the disk level, but the secret objects themselves are stored base64-encoded, not encrypted at the application layer.

The EKS Secrets Not Encrypted check (eks_nosecretsencryption) detects clusters that have not enabled envelope encryption with a KMS key. This is the extra layer that encrypts each secret object before it is written to etcd, so a leak of the underlying datastore does not hand an attacker your plaintext secrets.


What this check detects

Lensix inspects each EKS cluster's encryptionConfig and reports a finding when no KMS provider is configured for the secrets resource. In practical terms, the check passes only when your cluster has an entry like this in its configuration:

{
  "encryptionConfig": [
    {
      "resources": ["secrets"],
      "provider": {
        "keyArn": "arn:aws:kms:us-east-1:111122223333:key/abcd1234-..."
      }
    }
  ]
}

If encryptionConfig is empty or missing, the cluster fails the check.

Note: Envelope encryption means EKS uses your KMS key (the key encryption key) to encrypt a locally generated data encryption key, which in turn encrypts the secret. AWS never stores your KMS key material in etcd, so compromising the datastore alone is not enough to decrypt anything.


Why it matters

Base64 is encoding, not encryption. Anyone who pulls a secret object out of etcd, a backup snapshot, or an exposed API response can decode it instantly. Consider how that plays out in a real incident:

  • etcd or backup exposure. If a control plane backup or snapshot leaks, every secret in the cluster is readable. With envelope encryption, the attacker also needs access to your KMS key, which is governed by separate IAM and key policies.
  • Compliance failures. Frameworks like PCI DSS, HIPAA, and SOC 2 expect sensitive data to be encrypted with customer-managed keys and auditable access. A cluster without envelope encryption is a common audit finding.
  • Blast radius containment. KMS gives you a single point to revoke access. Disable or restrict the key and the secrets become unreadable, even if an attacker still holds a stale backup.
  • Defense in depth. Disk-level encryption protects against physical theft of storage. Envelope encryption protects against logical access to the data, which is the far more likely attack path in a managed service.

Warning: Envelope encryption protects secrets at rest in etcd. It does not encrypt secrets in transit to pods, in environment variables, or in your IaC repos. Treat it as one layer, not the whole strategy.


How to fix it

You enable envelope encryption by associating a KMS key with the cluster. This is a one-way operation on EKS: once secrets encryption is enabled, you cannot remove it, only rotate to a new key.

Step 1: Create or pick a KMS key

aws kms create-key \
  --description "EKS secrets envelope encryption - prod" \
  --tags TagKey=Purpose,TagValue=eks-secrets \
  --query 'KeyMetadata.Arn' \
  --output text

Save the returned ARN. You can also add an alias to make it easier to reference:

aws kms create-alias \
  --alias-name alias/eks-prod-secrets \
  --target-key-id arn:aws:kms:us-east-1:111122223333:key/abcd1234-...

Step 2: Associate the key with the cluster

Danger: Enabling secrets encryption is irreversible. You cannot disable it once applied, and choosing the wrong KMS key (for example, one with a restrictive key policy that the EKS service cannot use) can break secret access. Verify the key policy and test in a non-production cluster first.

aws eks associate-encryption-config \
  --cluster-name prod-cluster \
  --encryption-config '[{
    "resources": ["secrets"],
    "provider": {
      "keyArn": "arn:aws:kms:us-east-1:111122223333:key/abcd1234-..."
    }
  }]'

Monitor the update until it completes:

aws eks describe-update \
  --name prod-cluster \
  --update-id <update-id-from-previous-output>

Step 3: Re-encrypt existing secrets

Enabling encryption only applies to secrets written after the change. Existing secrets stay in their previous form until they are rewritten. Force a rewrite of every secret so they all flow through the new KMS provider:

kubectl get secrets --all-namespaces -o json \
  | kubectl replace -f -

Tip: Run the re-encrypt step during a maintenance window for large clusters. Rewriting thousands of secrets at once generates a burst of KMS API calls and etcd writes. Batch by namespace if you hit KMS request rate limits.

Terraform example

If you manage EKS with the terraform-aws-modules/eks module, add the encryption config to the cluster resource:

resource "aws_kms_key" "eks_secrets" {
  description             = "EKS secrets envelope encryption"
  deletion_window_in_days = 30
  enable_key_rotation     = true
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = "prod-cluster"
  cluster_version = "1.30"

  cluster_encryption_config = {
    resources        = ["secrets"]
    provider_key_arn = aws_kms_key.eks_secrets.arn
  }

  # ... vpc, subnets, node groups ...
}

With the raw aws_eks_cluster resource, the equivalent block is:

resource "aws_eks_cluster" "this" {
  name = "prod-cluster"

  encryption_config {
    resources = ["secrets"]
    provider {
      key_arn = aws_kms_key.eks_secrets.arn
    }
  }

  # ... role_arn, vpc_config ...
}

How to prevent it from happening again

The cleanest fix is to bake encryption into cluster creation so no cluster ever ships without it. Layer a few guardrails on top:

  • Module defaults. If your teams provision clusters through a shared Terraform module, make cluster_encryption_config required, not optional. A cluster created without a KMS key should fail the plan.
  • Policy as code in CI. Add an OPA/Conftest or Checkov rule to your pipeline that rejects EKS definitions missing an encryption config.
  • Continuous detection. Keep the Lensix eks_nosecretsencryption check running on a schedule so a manually created or imported cluster surfaces fast.

A Conftest policy to block unencrypted clusters in a Terraform plan:

package eks.secrets

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_eks_cluster"
  not resource.change.after.encryption_config
  msg := sprintf("EKS cluster %s has no secrets encryption config", [resource.address])
}

Wire it into your pipeline:

terraform plan -out tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test --policy ./policy tfplan.json

Tip: Checkov ships a built-in rule for this (CKV_AWS_58). Add checkov -d . --check CKV_AWS_58 to a CI step and you get coverage without writing a custom policy.


Best practices

  • Use a customer-managed KMS key, not an AWS-managed one. A CMK lets you control the key policy, audit usage through CloudTrail, and revoke access independently.
  • Enable automatic key rotation on the CMK so the underlying key material rotates yearly without manual work.
  • Scope the key policy tightly. Grant the EKS service and the cluster role only the kms:Encrypt, kms:Decrypt, and kms:DescribeKey permissions they need.
  • Consider an external secrets manager. For high-value credentials, pull from AWS Secrets Manager or Parameter Store at runtime via the Secrets Store CSI Driver instead of storing them as native Kubernetes secrets at all.
  • Restrict RBAC on secrets. Encryption at rest does nothing if every service account can read every secret. Lock down get, list, and watch on the secrets resource.
  • Audit KMS access. Send KMS CloudTrail events to your logging pipeline and alert on unexpected decrypt calls against the cluster key.

Envelope encryption is cheap insurance. The KMS cost is negligible, the setup is a single API call at cluster creation, and it removes one of the most common findings in any EKS security review.