Encrypting Secret Data at Rest
You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. It is recommended to run this tutorial on a cluster with at least two nodes that are not acting as control plane hosts. If you do not already have a cluster, you can create one by using or you can use one of these Kubernetes playgrounds:
Your Kubernetes server must be at or later than version 1.13. To check the version, enter .
etcd v3.0 or later is required
To encrypt a custom resource, your cluster must be running Kubernetes v1.26 or newer.
Configuration and determining whether encryption at rest is already enabled
The kube-apiserver
process accepts an argument --encryption-provider-config
that controls how API data is encrypted in etcd. The configuration is provided as an API named . --encryption-provider-config-automatic-reload
boolean argument determines if the file set by --encryption-provider-config
should be automatically reloaded if the disk contents change. This enables key rotation without API server restarts. An example configuration is provided below.
Caution: IMPORTANT: For high-availability configurations (with two or more control plane nodes), the encryption configuration file must be the same! Otherwise, the kube-apiserver
component cannot decrypt data stored in the etcd.
Understanding the encryption at rest configuration.
Each resources
array item is a separate config and contains a complete configuration. The resources.resources
field is an array of Kubernetes resource names (resource
or resource.group
that should be encrypted like Secrets, ConfigMaps, or other resources.
If custom resources are added to EncryptionConfiguration
and the cluster version is 1.26 or newer, any newly created custom resources mentioned in the EncryptionConfiguration
will be encrypted. Any custom resources that existed in etcd prior to that version and configuration will be unencrypted until they are next written to storage. This is the same behavior as built-in resources. See the section.
The providers
array is an ordered list of the possible encryption providers to use for the APIs that you listed.
Only one provider type may be specified per entry (identity
or aescbc
may be provided, but not both in the same item). The first provider in the list is used to encrypt resources written into the storage. When reading resources from storage, each provider that matches the stored data attempts in order to decrypt the data. If no provider can read the stored data due to a mismatch in format or secret key, an error is returned which prevents clients from accessing that resource.
For more detailed information about the EncryptionConfiguration
struct, please refer to the encryption configuration API.
Caution: If any resource is not readable via the encryption config (because keys were changed), the only recourse is to delete that key from the underlying etcd directly. Calls that attempt to read that resource will fail until it is deleted or a valid decryption key is provided.
Each provider supports multiple keys - the keys are tried in order for decryption, and if the provider is the first provider, the first key is used for encryption.
By default, the identity
provider is used to protect secret data in etcd, which provides no encryption. EncryptionConfiguration
was introduced to encrypt secret data locally, with a locally managed key.
Encrypting secret data with a locally managed key protects against an etcd compromise, but it fails to protect against a host compromise. Since the encryption keys are stored on the host in the EncryptionConfiguration YAML file, a skilled attacker can access that file and extract the encryption keys.
Envelope encryption creates dependence on a separate key, not stored in Kubernetes. In this case, an attacker would need to compromise etcd, the kubeapi-server
, and the third-party KMS provider to retrieve the plaintext values, providing a higher level of security than locally stored encryption keys.
Create a new encryption config file:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example
providers:
- aescbc:
keys:
- name: key1
secret: <BASE 64 ENCODED SECRET>
- identity: {}
To create a new Secret, perform the following steps:
Generate a 32-byte random key and base64 encode it. If you’re on Linux or macOS, run the following command:
head -c 32 /dev/urandom | base64
Place that value in the
secret
field of theEncryptionConfiguration
struct.Set the
--encryption-provider-config
flag on thekube-apiserver
to point to the location of the config file.You will need to mount the new encryption config file to the
kube-apiserver
static pod. Here is an example on how to do that:- Save the new encryption config file to
/etc/kubernetes/enc/enc.yaml
on the control-plane node. - Edit the manifest for the
kube-apiserver
static pod:/etc/kubernetes/manifests/kube-apiserver.yaml
similarly to this:
- Save the new encryption config file to
Restart your API server.
Caution: Your config file contains keys that can decrypt the contents in etcd, so you must properly restrict permissions on your control-plane nodes so only the user who runs the kube-apiserver
can read it.
Verifying that data is encrypted
Data is encrypted when written to etcd. After restarting your kube-apiserver
, any newly created or updated Secret or other resource types configured in EncryptionConfiguration
should be encrypted when stored. To check this, you can use the etcdctl
command line program to retrieve the contents of your secret data.
-
ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C
where
[...]
must be the additional arguments for connecting to the etcd server.For example:
ETCDCTL_API=3 etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry/secrets/default/secret1 | hexdump -C
The output is similar to this (abbreviated):
Verify the stored Secret is prefixed with
k8s:enc:aescbc:v1:
which indicates theaescbc
provider has encrypted the resulting data. Confirm that the key name shown inetcd
matches the key name specified in theEncryptionConfiguration
mentioned above. In this example, you can see that the encryption key namedkey1
is used inetcd
and inEncryptionConfiguration
.Verify the Secret is correctly decrypted when retrieved via the API:
kubectl get secret secret1 -n default -o yaml
The output should contain
mykey: bXlkYXRh
, with contents ofmydata
encoded, check decoding a Secret to completely decode the Secret.
Ensure all Secrets are encrypted
Since Secrets are encrypted on write, performing an update on a Secret will encrypt that content.
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
The command above reads all Secrets and then updates them to apply server side encryption.
Note: If an error occurs due to a conflicting write, retry the command. For larger clusters, you may wish to subdivide the secrets by namespace or script an update.
Changing a Secret without incurring downtime requires a multi-step operation, especially in the presence of a highly-available deployment where multiple kube-apiserver
processes are running.
- Generate a new key and add it as the second key entry for the current provider on all servers
- Restart all
kube-apiserver
processes to ensure each server can decrypt using the new key - Make the new key the first entry in the
keys
array so that it is used for encryption in the config - Restart all
kube-apiserver
processes to ensure each server now encrypts using the new key - Run
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
to encrypt all existing Secrets with the new key - Remove the old decryption key from the config after you have backed up etcd with the new key in use and updated all Secrets
When running a single kube-apiserver
instance, step 2 may be skipped.
Decrypting all data
To disable encryption at rest, place the identity
provider as the first entry in the config and restart all kube-apiserver
processes.
What’s next
- Learn more about the .