Sealed Secrets is a Kubernetes tool that enhances security by enabling the encryption of Kubernetes Secrets at rest in a Git repository. It allows users to commit encrypted versions of their Secrets to source control while still maintaining the ability to decrypt and use them within the Kubernetes cluster. Sealed Secrets employs public-key cryptography, where the cluster holds the public key, enabling it to encrypt Secrets, while a client tool called “kubeseal” uses a private key to decrypt the Secrets for use within the cluster. This approach ensures that sensitive information, such as passwords or API keys, remains secure even when stored in version control.
Let’s create a HelmRepository for sealed-secrets Directory structure
clusters
--production
----flux-system
----kustomization.yaml
------helmrepositories
--------sealed-secrets.yaml
The content of sealed-secrets.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: sealed-secrets
namespace: flux-system
spec:
interval: 1h0m0s
url: https://bitnami-labs.github.io/sealed-secrets
The content of kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
- helmrepositories/sealed-secrets.yaml
Lets create a namespace for sealed-secrets
Directory structure
clusters
---production
-----sealed-secrets
-------namespace.yaml
Content of namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: sealed-secrets
Note, we do not want to inject istio sidecar for this namespace.
Let’s deploy sealed-secrets using a helmrelease CRD Directory structure
clusters
--production
----sealed-secrets
------namespace.yaml
------sealed-secrets.yaml
Content of sealed-secrets.yaml
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: sealed-secrets
namespace: flux-system
spec:
chart:
spec:
chart: sealed-secrets
sourceRef:
kind: HelmRepository
name: sealed-secrets
version: ">=1.15.0-0"
interval: 1h0m0s
releaseName: sealed-secrets-controller
targetNamespace: sealed-secrets
install:
crds: Create
upgrade:
crds: CreateReplace
Push the above changes to the main branch of cluster-config repository and FluxCD will automatically do the sealed-secrets deployment
Let’s validate what we have done by creating an encrypted secret.
Folder structure
clusters
--production
----test-namespace
------namespace.yaml
Content of namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test-namespace
Create a secret in the test-namespace as follows
clusters
--production
----test-namespace
------namespace.yaml
------secret.yaml
Generate the content of secret.yaml as follows
kubectl create secret generic test-secret -n test-namespace --from-literal=password=password123 --dry-run=client -o yaml > secret.yaml
The content of secret.yaml
apiVersion: v1
data:
password: cGFzc3dvcmQxMjM=
kind: Secret
metadata:
creationTimestamp: null
name: test-secret
namespace: test-namespace
As you can see, the password value is base64 encoded, not encrypted. If we commit this file into the repository, anyone who has the access to the repository would be able to decode the secret as follows
echo -n "cGFzc3dvcmQxMjM=" |base64 -d
In order to encrypt the secrets, we need to install kubeseal utility. You can see the latest releases here
Now, retrieve the public certificate with kubeseal utility. Please note that the controller name depends on the helmrelease name.
kubeseal --controller-name=sealed-secrets-controller --controller-namespace=sealed-secrets --fetch-cert > pub.crt
Now that we have the public key, we can encrypt our secret.yaml
kubeseal -o yaml --scope cluster-wide --cert pub.crt < secret.yaml > secret-enc.yaml
This will write the encrypted secret into secret-enc.yaml.
Let’s inspect the content
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
annotations:
sealedsecrets.bitnami.com/cluster-wide: "true"
creationTimestamp: null
name: test-secret
namespace: test-namespace
spec:
encryptedData:
password: AgCrjD2fIvmCiOecKrCgdJxXoev2F4P5AbR+weqb7CdrCo+b0SfBHA/aCDnVbjF4GoJqcQk3xGUlHfNsbw7i0zVB5RxhCNziHwRyGI22rghjt6Wj7T04ko3U+YMJalYpM8MFk2Uy34abE08ygFzE491/XAy4Yym1hQEQAANu8iXH59r+3xOG+UhqycXYatJlstQnq51/7JQZ3+DbC54C6z6JereLv8EZmIXjiJ682jY/cA3clMoYWdksng/rAVQlJwgG7oRtJUFWAGhTiBMO4PBh5CDdQrcSpMUbEOYJDxewcFUswDZlWWA8h895wjnGdy0oRWc/+kOCt1Tm2KKKIPH2/y/HBcFTu5yw+nStQ+HKam0lZNF4i+uC18FSLP0RJp/VEsWXtBZlttluRCJQgy3l7wdc3EOtw0S5XAIq4KZaSFyTKRL56iiXZYxIrsBqhDCmxDxnUxoM2E7RjWBPziOPCogoWO+FsEX7HL0AC2+kj3K7nBJ6vWkfxXoitrItIfnDfkEoU9PlQVeqH8rv0JaKYcNqFlMQJBQdSJUsolEP7lU1IMGUcwDZwnSUwAMIH+fRqv+HD4AqgWMkb2v/PWKy1Oi728LterGrs9xtp1X9QuKF/yeZT7lxhdbNp3kIq9CLqDUGG/ifWKb/ovEUpXEItIbXKg+VYJHQ9lk6/gr7kOf5qWYz6xfy4jAae5ZjSFuel4k3qsbDA==
template:
metadata:
annotations:
sealedsecrets.bitnami.com/cluster-wide: "true"
creationTimestamp: null
name: test-secret
namespace: test-namespace
As you can see, the password now has been encrypted.
Now we can safely commit secret-enc.yaml file to the repository. Kubernetes would be able to decrypt this secret as it already has sealed-secrets private key.
Once the changes are merged, FluxCD will create the secret in the kubernetes cluster. Now we can inspect the secret using kubectl utility
kubectl get secret test-secret -n test-namespace -o yaml
Output will be similar to the following.
apiVersion: v1
data:
password: cGFzc3dvcmQxMjM=
kind: Secret
metadata:
creationTimestamp: null
name: test-secret
namespace: test-namespace
We can observe that Kubernetes was able to decrypt the secret.