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_configrequired, 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_nosecretsencryptioncheck 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, andkms:DescribeKeypermissions 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, andwatchon thesecretsresource. - 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.

