BlogKubernetes

Encrypting Kubernetes Secrets at rest in etcd

Kubernetes Secrets are only base64 by default. Turn on an EncryptionConfiguration and rotate the key without downtime.

Feb 4, 2026·10 min readAdvanced

A Kubernetes Secret is not encrypted — it is base64-encoded, which is not the same thing. Anyone who can read etcd (a backup, a snapshot, a compromised control-plane node) can read every secret in the cluster. An EncryptionConfiguration on the API server fixes that by encrypting Secrets before they hit etcd.

bash — base64 is not encryptionlive
ETCDCTL_API=3 etcdctl get /registry/secrets/shop/db | strings
k8s Secret ... password: c3VwZXItc2VjcmV0
echo c3VwZXItc2VjcmV0 | base64 -d -> super-secret

Turn on encryption at rest

enc.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: [secrets]
providers:
- secretbox:
keys:
- name: key1
secret: <32-byte base64 key>
- identity: {} # read old plaintext during rollout

Point the API server at it with --encryption-provider-config=/etc/kubernetes/enc/enc.yaml and restart. New Secrets are now encrypted; existing ones are still plaintext until you rewrite them.

Encrypt what already exists

bash — rewrite every secret in placelive
kubectl get secrets -A -o json | kubectl replace -f -
each secret re-written through the encrypting API server
verify with etcdctl again — the value is now ciphertext
Use a KMS provider in production
A local key in the config file is better than nothing, but the key sits on the control-plane node. The kms provider (AWS KMS, GCP KMS, Vault) keeps the key material off the host and gives you rotation and audit.

Rotating the key

Add the new key first in the provider list, restart, rewrite all secrets, then remove the old key on a second pass. Keeping identity last during the first rollout lets the API server still read anything not yet encrypted.

Go deeper in a courseKubernetes security & hardeningetcd encryption, RBAC, Pod Security and the rest of CKS.View course

Related posts