Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save brunocarvalhodearaujo/992ec1f93da21fc55418d81453254a50 to your computer and use it in GitHub Desktop.
Save brunocarvalhodearaujo/992ec1f93da21fc55418d81453254a50 to your computer and use it in GitHub Desktop.
Helm + Sealed secrets

I had a bit of trouble figuring out how to use bitnami's sealed secrets with helm

Here's a definition of done to help you see what I was trying to achieve.

Definition of done

  • Single secret available for a release in a namespace, listing all secret variables
  • Regular helm workflow, with no extra kubeseal commands for developers
  • Encrypted secrets clearly visible in git
  • Sealedsecret managed by helm

After much suffering, here's what I came up with. A pre-commit hook that creates the single sealedsecret in my chart. It took me a while, so I thought I should share in case someone can improve it.

app/templates/sealedsecret.yml ->

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: {{ include "app.fullname" . }}
  labels:
    ...
spec:
  encryptedData:
    {{- range $key, $val := .Values.secret }}
      {{ $key }}: {{ $val | quote }}
    {{- end }}

./env/ci.values.yml ->

...
env:
  ...
  NODE_ENV: production
  AWS_BUCKET: xyz

./env/ci.secrets.yml ->

...
AWS_ACCESS_KEY_ID: abc123
AWS_SECRET_ACCESS_KEY: xyz

Pre-commit hook ->

#!/bin/bash
# use bash, as echo -n does not work in #!/bin/sh
# dependencies
# - yq : read secrets.yml
# - kubeseal : encrypt secrets with kubernetes cluster sealed secrets public key

set -e

encrypt() {
    # the full name of the app - {release}-{chart} or just {release}
    fullname="YOUR APP FULL NAME"
    # the top level yaml key for secrets in the values.yml file
    secret_prefix="secret"
    # the name and namespace of the sealed secrets controller after installation to the cluster
    controller_name="sealed-secrets"
    controller_namespace="default"

    # find namespace specific values and secrets files, and a temporary file for storing intermediate values
    namespace=${1}
    secrets_file=${GIT_DIR:-$PWD}/env/${namespace}.secrets.yml
    values_file=${GIT_DIR:-$PWD}/env/${namespace}.values.yml
    tmp_secret_file=${GIT_DIR:-$PWD}/env/tmpsecretfile

    if [[ ! -f "$secrets_file" ]]; then
        echo "No secrets to encrypt in $secrets. Skipping..."
        return
    fi

    # get all secret key names
    keys=$(yq r -j $secrets_file | jq -r 'keys[]')
    # count keys, and strip whitespace
    nkeys=$(wc -w <<<$keys | sed 's/ //g')
    echo "Encrypting $nkeys secrets from $secrets..."

    # we will write each key to the values file
    for key in $keys; do
        # echo with -n to file first, otherwise you might have a trailing new line in your decrypted value
        echo -n $(yq r $secrets_file $key) >${tmp_secret_file}
        # --name should match the name of the sealedsecret (data-platform.fullname from _helpers.tpl)
        encrypted=$(kubeseal --raw --name=${fullname} --namespace=${namespace} --from-file=${tmp_secret_file} --controller-name=${controller_name} --controller-namespace=${controller_namespace})
        rm ${tmp_secret_file}

        # write the secret to the values file
        yq w -i $values_file "$secret_prefix.$key" $encrypted
    done

    echo "Successfully wrote ciphertext to $values."
}

encrypt ci
encrypt qa
encrypt prod

git add env

The folder structure is as follows: app/templates/sealedsecret.yml env/ci.values.yml env/ci.secrets.yml

After running git commit, the env/ci.values.yml file is now as follows

env:
  ...
  NODE_ENV: production
  AWS_BUCKET: xyz
secret:
  AWS_ACCESS_KEY_ID: ...encrypted data
  AWS_SECRET_ACCESS_KEY: ...encrypted data

Make sure to add env/*.secrets.yml to your .gitignore!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment