Learn how to configure encryption at rest for Kubernetes secrets stored in etcd. By default, secrets are stored in plain text in etcd, which poses a security risk if the etcd database is compromised.
Prerequisites
Understanding the Problem
First, let’s understand why encryption at rest is important:
# Create a test secretkubectl create secret generic test-secret --from-literal=password=mysecretpassword
# Access etcd directly to see how secrets are stored# (This requires access to the control plane node)
Q1: Why is encryption at rest important?
Answer
Default behavior: Kubernetes stores secrets in etcd as base64-encoded text (not encrypted)
Security risk: If someone gains access to etcd backups or the etcd database, they can easily decode all secrets
Compliance: Many security frameworks (SOC2, PCI-DSS) require encryption at rest
Defense in depth: Even if other security controls fail, encrypted data remains protected
Generate Encryption Key
Generate a strong 32-byte encryption key:
# Generate a random 32-byte key and encode it in base64head -c 32 /dev/urandom | base64
Save this key securely - you’ll need it for the encryption configuration.
Configure Encryption
Step 1: Create Encryption Configuration
Create the encryption configuration file /etc/kubernetes/enc.yaml:
After modifying the manifest, the kubelet will automatically restart the API Server. Monitor the restart:
# Watch for the API server to restartkubectl get pods -n kube-system -w -l component=kube-apiserver
# Verify the API server is running with encryptionkubectl get nodes
Step 4: Encrypt Existing Secrets
When encryption is first enabled, existing secrets remain unencrypted. You must actively encrypt them:
# Encrypt all existing secrets in all namespaceskubectl get secrets --all-namespaces -o json | kubectl replace -f -
# Or encrypt secrets in a specific namespacekubectl get secrets -n default -o json | kubectl replace -f -
Verification
Test New Secrets
Create a new secret and verify it’s encrypted:
# Create a new secret (this should be encrypted)kubectl create secret generic encrypted-secret --from-literal=token=verysecrettoken
# Verify the secret works normallykubectl get secret encrypted-secret -o yaml
echo $(kubectl get secret encrypted-secret -o jsonpath='{.data.token}') | base64 -d
Verify Encryption in etcd
etcd Verification Commands
# Access the control plane node# For Kind clusters:docker exec -it <cluster-name>-control-plane bash
# Check etcd directly (this should show encrypted data)ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
get /registry/secrets/default/encrypted-secret
# The output should show encrypted content, not plain text
Key Rotation
For production environments, regular key rotation is essential:
Step 1: Add New Key
Update /etc/kubernetes/enc.yaml to add a new key as the first provider: