Managing Secrets
Kubernetes secrets provide a way to distribute sensitive data into Pods. Secrets are just another object the Kubernetes API can manage and are represented in YAML manifest files. The sensitive data part of a secret will be encoded in base64 format, meaning they are not encrypted. This raises an issue, as committing these manifests in Version Control Systems like Git is not secure.
Bitnami Sealed Secrets is a solution to this problem, providing one-way encrypted secrets that are safe to commit in Git repositories.
Sealed secrets are based on asymmetric cryptography, making use of a certificate and private key pair in a similar way as to how GPG keys work. The certificate/public key part can be used locally with kubeseal
to encrypt a secret, which can be applied to a Kubernetes cluster as any other resource.
The sealed-secrets-controller
running in the kube-system
namespace by default, will in turn decrypt the sealed secret, using the private key, to a normal Secret resource that can be mounted to Pods in the cluster.
In the cluster creation step, if you would like to use a specific certificate - key pair, specify it in the main configuration file at setup/config.yaml
and WKP will launch the sealed secrets controller with the provided key.
If left blank a new self signed certificate - key pair is created and stored at setup/sealed-secrets-cert.crt
and setup/sealed-secrets-key
.
Do not store the private key in any VCS repository. It should be stored in a password manager of your choice or retrieved from the Secret in the Kubernetes cluster. Compromising the private key annuls any protection provided by the sealed secrets so it needs to be handled with utmost care.
#
Example: Creating a sealed secretOnce the cluster is ready, you can create a sealed secret as described in the following example from bitnami:
#
ScopesEnforcing RBAC for users of a Kubernetes cluster usually entails separating user access to specific namespaces. To ensure that reading a sealed secret is safe, they can include in the encryption process the name of the secret and the namespace. This is the case of sealing a secret in the strict
scope. An example of why this is essential is the following:
- Let's assume a user of namespace
foo
reads a sealed secret manifest from Git that is meant for namespacebar
. - He could change the namespace field from
bar
tofoo
and create the sealed secret in his namespacefoo
and read out the value once the controller decrypts it.
Same example is valid for RBAC within the same namespace, e.g. if the user could only access a secret with a specific name in namespace foo
. By changing the name of the secret to foo
he could read the decrypted value once the controller decrypts it to a secret he can access.
The 3 scopes of sealed secrets are:
- strict (default): the secret must be sealed with exactly the same name and namespace. These attributes become part of the encrypted data and thus changing name and/or namespace would lead to "decryption error".
- namespace-wide: you can freely rename the sealed secret within a given namespace.
- cluster-wide: the secret can be unsealed in any namespace and can be given any name.
You can select the scope of a secret by passing the --scope
flag to kubeseal:
If the flag is not passed the default scope is strict
.
#
Key RenewalKey rotation
is critical to the security of any cryptosystem. The recommended procedure of securely managing sealed secrets is referred to as key renewal
and it differs from traditional key rotation in ways explained below. For further reading, please refer to the Sealed Secrets README on Github.
Traditionally, in the example of an access key rotation works by:
- Creating a new key
- Updating all applications to use the new key and validating that they are working
- Labelling as expired the old key and optionally deleting it
In dealing with sealed secrets in Kubernetes the process differs in these ways:
- Creating a new key and reencrypting the secrets with the new one is not enough, the periodical rotation of the actual secret value is also advised
- "Expired" keys are not automatically deleted, they are kept in a list in the controller and can still be used to decrypt sealed secrets that have been sealed with the old certificate
Key renewal occurs automatically at the time interval passed to the controller with the --key-renew-period
flag, which defaults to 30 days.
#
Disaster RecoveryIf you suspect that a private key has been compromised you can consider that all sealed secrets that have been encrypted with it are compromised as well. In this case you need to change all sensitive values of the secrets, create a new sealing key and reencrypt all secrets. The steps in this case are:
Change all sensitive data of your secrets
Retrieve the compromised private key from the cluster and delete it
Restart the sealed-secrets-controller pod
When the new pod starts, it will create a new private key
Get the new certificate and store it locally
Reseal your secrets with the new certificate
Commit them to your repo to create them
The compromised secret key retrieved above can be used to decrypt any old sealed secrets if needed:
#
Reusing the Sealing Key in Multiple ClustersFor architectures where multiple clusters are managed by GitOps, reusing the same sealing key can decrease the operational complexity, if this process is within your security constraints. Keys are stored as standard Kubernetes secrets within a cluster, in the same namespace as the controller, usually kube-system
under the name sealed-secrets-key
.
To share keys between two clusters, assuming one cluster is operational and the second is being created:
Extract the key from the first one
Commit it in the repo of your second cluster
Launch the workloads of the cluster as normal. As the sealed-secrets-controller starts it will read the key value from the secret and use it for decryption.
#
Secret RotationTo create a WKP cluster, the required secret values are a deploy key for the git repository, alongside your docker credentials. These two values are sealed, with a similar process as described in the example section above, and stored in the repository in cluster/platform/gitops-secrets.yaml
.
To rotate the sealed secret values when a new key is in place, first change directory to the top level directory of your github repository, then:
Fetch the certificate for the new key. (
kubeseal
is installed in thebin
directory of your installation)Run
wk gitops generate-secrets
(again, from the top level directory of your git repository) passing the new certificate, and push the new changes to the remote repository:After a few minutes the controller should decrypt the new sealed secrets. You can verify this from the logs, as for example:
This process rotates just the encrypted values in the sealed secrets stored in the git repository. It is recommended
to rotate the actual secret values, in this case these are the docker credentials and the git deploy key.
In that case, after step 4 it is required to restart the pods in the cluster that will mount the updated Secrets
.