Encrypt Kubernetes Secrets at Rest in etcd
Scenario
By default, Kubernetes stores all object data — including Secrets — as plaintext inside etcd. Anyone with access to etcd's data directory or an etcd backup file can read every Secret value directly, without ever going through the Kubernetes API or RBAC.
A Secret named db-credentials already exists in the secure namespace,
and right now its username and password values are sitting in etcd
completely unencrypted. Confirm this yourself:
sudo ETCDCTL_API=3 etcdctl get /registry/secrets/secure/db-credentials \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
You will see the raw admin / SuperSecret123 values readable in the
output. Your job is to fix this by enabling encryption at rest for
Secrets.
Task
- Create an
EncryptionConfigurationfile at/etc/kubernetes/enc/encryption-config.yamlthat encrypts thesecretsresource using a real encryption provider (aescbc,aesgcm, orsecretbox) — not justidentity - Update the
kube-apiserverstatic pod manifest to use this configuration via--encryption-provider-config, and mount the config directory into the pod - Recreate the existing
db-credentialsSecret so it picks up the new encryption — encryption only applies to writes that happen after it's enabled, so the Secret must be rewritten to actually become encrypted in etcd - Confirm
db-credentialsis now stored encrypted in etcd, and that it's still readable correctly throughkubectl get secret— proving the API server can both encrypt and decrypt transparently
Hint 1 — Create the EncryptionConfiguration
An EncryptionConfiguration is a YAML file that tells the API server which
resources to encrypt and which provider to use. Create the directory and
file:
sudo mkdir -p /etc/kubernetes/enc
sudo vi /etc/kubernetes/enc/encryption-config.yaml
A minimal working example:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
Generate a valid key:
head -c 32 /dev/urandom | base64
The identity: {} provider must come last — it is the fallback that
reads unencrypted data already in etcd. List your real provider first so
new writes use it.
Documentation
Hint 2 — Update the kube-apiserver Manifest
Edit the kube-apiserver static pod manifest to point to the new config and mount the directory containing it:
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
Add the flag to the command args:
- --encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml
Add a volume mount and a matching hostPath volume so the container can actually read the file:
volumeMounts:
- mountPath: /etc/kubernetes/enc
name: enc
readOnly: true
volumes:
- hostPath:
path: /etc/kubernetes/enc
type: DirectoryOrCreate
name: enc
The kubelet detects the manifest change automatically and restarts the apiserver pod. Wait for it to come back before continuing:
kubectl get pod -n kube-system -l component=kube-apiserver -w
Documentation
Hint 3 — Recreate the Existing Secret and Verify
Enabling encryption only affects future writes — Secrets already in etcd remain unencrypted until they are rewritten. Force a rewrite without changing the data:
kubectl get secret db-credentials -n secure -o json | kubectl replace -f -
Now check etcd directly to confirm encryption is working — look for the
k8s:enc: prefix, which marks encrypted data:
sudo ETCDCTL_API=3 etcdctl get /registry/secrets/secure/db-credentials \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
Finally confirm the API server can still decrypt it correctly when read
normally — the actual username and password values should come back
unchanged:
kubectl get secret db-credentials -n secure -o jsonpath='{.data.username}' | base64 -d
kubectl get secret db-credentials -n secure -o jsonpath='{.data.password}' | base64 -d
Documentation