Sealed Secrets in Kubernetes

GitOps advocates managing all your configuration through Git. In Kubernetes, we can manage resources inside the cluster using manifest files. The Secret is used to help us store sensitive information, such as SSH Key, docker registry credentials. Developers can then easily reference the Secrets inside the manifest files without having to hard-code it directly. However, it is not good to manage Secret inside a git repository. It is not secure as Kubernetes only encode the sensitive information with base64. We can quickly obtain the Secret by running

kubectl get secret secretname --output="jsonpath={.data.valuename}" | base64 --decode

Therefore, Bitnami Labs create a project called Sealed Secrets to solve this problem.

What is Sealed Secrets does?

Encrypt your Secret into a SealedSecret, which is safe to store - even to a public repository. The SealedSecret can be decrypted only by the controller running in the target cluster and nobody else (not even the original author) is able to obtain the original Secret from the SealedSecret.

How it works?

Sealed Secrets have two main components to make it works.

  1. A controller deployed in the cluster
  2. A client-side command-line tools: kubeseal

kubeseal uses RSA 4096 to encrypt Kubernetes Secret, and SealedSecret is a CRD (Custom Resource Definition) containing an encrypted Secret that only the controller itself can decrypt. Therefore, it is safe to store the SealedSecret in a public repository. I made a simple illustration to show how kubeseal works:

kubeseal
Fig 1. How kubeseal work.

Installation

You can checkout that latest release in their GitHub repository

Install SealedSecret Controller (Cluster Side)

kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.16.0/controller.yaml

Install kubeseal (Client Side)

# MacOS
brew install kubeseal

# Linux
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.16.0/kubeseal-linux-amd64 -O kubeseal
sudo install -m 755 kubeseal /usr/local/bin/kubeseal

Creating a Secret

To create a SealedSecret, we need to first create a Secret.

echo -n "Hello, World!" | kubectl create secret generic mysecret --dry-run --from-file=secret=/dev/stdin -o yaml > secret.yaml

It will create a secret.yaml like this:

apiVersion: v1
data:
  secret: SGVsbG8sIFdvcmxkIQ==
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret

We know that the secret is base64 encoded. So let’s try to decode the secret.

echo "SGVsbG8sIFdvcmxkIQ==" | base64 --decode

# Output: Hello, World!

Then create a SealedSecret.

kubeseal --format yaml <secret.yaml >sealedsecret.yaml

It will create a sealedsecret.yaml like this:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: mysecret
  namespace: default
spec:
  encryptedData:
    secret: AgBiK....oSKfg
  template:
    data: null
    metadata:
      creationTimestamp: null
      name: mysecret
      namespace: default

Now apply the sealedsecret.yaml.

kubectl apply -f sealedsecret.yaml

Now let’s create a busybox pod to test the SealedSecret.

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  containers:
    - name: testbox
      image: busybox
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - name: mysecretvol
          mountPath: "/tmp/mysecret"
          readOnly: true
  volumes:
    - name: mysecretvol
      secret:
        secretName: mysecret

Apply the busybox.yaml and see if the secret is correct.

kubectl apply -f busybox.yaml
kubectl exec -it busybox -- cat /tmp/mysecret/secret

# Output: Hello, World!

You can see Hello, World! printed. That means the SealedSecret is working correctly. Now, remove the test secret and pods by running:

kubectl delete -f busybox.yaml -f sealedsecret.yaml

Resources

Sealed Secrets GitHub repository
AWS Open Source Blog: Managing secrets deployment in Kubernetes using Sealed Secrets


About the author
cyrusho

A Full-Stack Engineer based in :hong_kong:Hong Kong