Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sealing a Helm Templated Secret #277

Closed
mvadood opened this issue Sep 30, 2019 · 9 comments
Closed

Sealing a Helm Templated Secret #277

mvadood opened this issue Sep 30, 2019 · 9 comments
Labels

Comments

@mvadood
Copy link

mvadood commented Sep 30, 2019

Imagine a secret like this:

apiVersion: v1
kind: Secret
metadata:
  name: {{ include "test-cicd.fullname" . }}
  labels:
    app.kubernetes.io/name: {{ include "test-cicd.name" . }}
    helm.sh/chart: {{ include "test-cicd.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
type: Opaque
data:
  secret.yaml: |
    {{ if eq .Values.env "prod" }}
    foo: bar-prod
    foo2: bar2_prod
    {{ else if eq .Values.evn "dev" }}
    foo: bar-dev
    {{ end }}

Is it possible to seal this using Kubeseal?
Upon doing it now I get invalid map key: map[interface {}]interface {}{"include \"test-cicd.fullname\" .":interface {}(nil)} which is probably because it is not a "valid" yaml file.

@mkmik
Copy link
Collaborator

mkmik commented Oct 1, 2019

I don't understand the use case. Where do the actual secret values come from?

@mvadood
Copy link
Author

mvadood commented Oct 2, 2019

It is hardcoded in the yaml file at the moment.

@mkmik
Copy link
Collaborator

mkmik commented Oct 2, 2019

So, you're composing a secret value with client-side templating.
Parts of your secret.yaml file are secret, yet parts must be templating directives (the if) and hence cannot be encrypted.

You have two options:

  1. you encrypt your secrets somehow using some client-side vault software, possibly with helm integration (e.g. https://github.com/futuresimple/helm-secrets). That requires every user (and CI environment) that applies that helm chart, to be able to decrypt the secrets.

  2. you re-factor your secrets so that secrets are "atomic", and use sealed-secrets to benefit from its "one-way encryption" approach, which allows your devops users (and CI automation) to apply the helm charts without ever seeing the secret values themselves.

The rest of this answer assumes you picked option (2)


Now, since you decided to use Helm, you have to deal with the fact that helm templates are not json/yaml files, but instead they are Go templates, and hence they cannot be manipulated by tools designed to manipulated structured data formats.

Luckily, kubeseal has a --raw command, that allows you to encrypt individual secret values and put them manually in whatever file format you're using to describe your k8s resources.

So, assuming you want to create a Helm template for SealedSecrets resource, which takes the name and label values as paramters, and also chooses which secrets to put also based on boolean prod/dev parameter, this example might work for you:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: {{ include "test-cicd.fullname" . }}
  annotations:
    # this is because the name is a deployment time parameter
    # consider also using "cluster-wide" if the namespace is also a parameter
    # please make sure you understand the implications, see README
    sealedsecrets.bitnami.com/namespace-wide: "true"
  labels:
    app.kubernetes.io/name: {{ include "test-cicd.name" . }}
    helm.sh/chart: {{ include "test-cicd.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
type: Opaque
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "test-cicd.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
        app.kubernetes.io/managed-by: {{ .Release.Service }}
  encryptedData:
    {{ if eq .Values.env "prod" }}
    foo: AgASNmKx2+QYbbhSxBE0KTa91sDBeNSaicvgBPW8Y/q/f806c7lKfF0mnxzEirjBsvF67C/Yp0fwSokIpKyy3gXtatg8rhf8uiQAA3VjJGkl5VYLcad0t6hKQyIfHsD7wrocm36uz9hpH30DRPWtL5qy4Z+zbzHj8AvEV+xTpBHCSyJPF2hyvHXTr6iQ6KJrAKy04MDwjyQzllN5OQJT2w4zhVgTxXSg/c7m50U/znbcJ1x5vWLXLSeiDRrsJEJeNoPQM8OHmosf5afSOTDWQ4IhG3srSBfDExSFGBIC41OT2CUUmCCtrc9o61LJruqshZ3PkiS7PqejytgwLpw/GEnj2oa/uNSStiP9oa9mCY6IUMujwjF9rKLIT456DlrnsS0bYXO2NmYwSfFX+KDbEhCIVFMbMupMSZp9Ol2DTim5SLIgIza/fj0CXaO3jGiltSQ0aM8gLSMK9n3c1V+X5hKmzMI3/Xd01QmhMmwqKp+oy21iidLJjtz67EiWyfIg1l7hiD5IIVlM9Gvg3k67zij5mOcXPkFnMmUQhQWxVKgAf4z8qEgprt03C+q+Wwwt25UDhQicpwoGtVQzU5ChJi09ja5LeW4RrvDf2B5KRp9HXoj1eu93MMl1Kcnx+X7uVT5OqQz28c4wOLT4FDItFzh8zREGZbiG/B3o1vI8MmwvxXj++pQ7SfBxoz9Xe8gmQ7BuXno=
    foo2: AgAkaTBYcESwogPiauZ15YbNldmk4a9esyYuR2GDt7hNcv+ycPLHmnsJcYs0hBtqucmrO3HbgCy/hQ6dMRCY12RA7w7XsFqNjZy3kavnhqwM6YkHntK2INwercRNQpO6B9bH6MxQTXcxfJbPqaPt30iTnTAhtpN47lueoyIoka4WWzwG/3PAikXhIlkTaq0hrclRJHRqg4z8Kmcaf5A/BRL2xX8syHbjA7MK9/OoK+zytv+LGrbLLHUtuhNNNQ2PG9u05rP6+59wRduQojEDtB9FTCa+daS+04/F4H1vi6XUNnjkK+Xna1T2Eavyuq2GieKj/7ig96et/4HoTAz44zwVhh8/pk0IFC8srcH3p+rFtZZmjvbURrFahEjFZbav3BDMBNhrU8SI3MDN0Abiyvz4vJJfSxIYcyLD1EQ507q7ZXrqYN/v1EiYgYUACi0JGxSWHB9TlCkZOAdCl+hroXEhBN2u5utLJ12njBQJ8ACNQDOYf+CmtV0y7foCZ6Aaap0pV7a8twyqK8c17kImzfi102Zel8ALfLAzdAXBV9c1+1pH76turnTCE33aSMQlaVF3VTmFQWqB8uIO/FQhZDPo8u/ki3L8J31nepup4/WE7i59IT0/9qGh2LKql4oAv6v4D7qtKziN6DvG7bsJlj14Dln0roiTfTWEEnBqdDER+GKZJlKayOWsPQdN0Wp+2KVfwLM=
    {{ else if eq .Values.evn "dev" }}
    foo: AgAkaTBYcESwogPi..........
    {{ end }}

An alternative approach would be to have two templates, one for prod and one for dev and use Helm templating logic to pick the right file depending on which environment you're deploying to.

Anyway, each of those base64 blobs can be produced with:

$ kubeseal --raw --scope namespace-wide --from-file=yoursecret.txt

Pro-tip, you can pipe the secret if it's not in a file:

$ echo -n yoursecret | kubeseal --raw --scope namespace-wide --from-file=/dev/stdin

Then you have to paste the output of that command into your Helm Go template.

@mvadood
Copy link
Author

mvadood commented Oct 3, 2019

Thanks for your reply @mkmik. I completely missed out on --raw. That'll fix my problem as you mentioned.

@mkmik mkmik added the question label Oct 7, 2019
@mkmik
Copy link
Collaborator

mkmik commented Oct 7, 2019

closing because I think it has been answered. Feel free to re-open if not.

@mkmik mkmik closed this as completed Oct 7, 2019
@presidenten
Copy link

@mkmik In your example

$ kubeseal --raw --scope namespace-wide --from-file=yoursecret.txt

The scope is namespace-wide, but I dont see a namespace anywhere. How does it work?

@mkmik
Copy link
Collaborator

mkmik commented Feb 12, 2020

it uses the default namespace from your current context (as defined in ~/.kube/config)

@mkmik
Copy link
Collaborator

mkmik commented Feb 12, 2020

you can override that by passing -n somenamespace explicitly

@ozbillwang
Copy link

ozbillwang commented Mar 20, 2023

Thanks @mkmik 's solution.

The original poster's secret file is customized and complex. I provide a simple one.

Here I put the generic way to manage it, the loop makes the key/value simplified, especially when you have many env variables to be managed.

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  # replace app_name to your helm chart name, you can take reference from `template/deployment.yaml`
  name: {{ include "app_name.fullname" . }}
type: Opaque
spec:
  template:
    metadata:
      # replace app_name to your helm chart name, you can take reference from `template/deployment.yaml`
      name: {{ include "app_name.fullname" . }}
  encryptedData:
      {{- range $key, $val := .Values.encryptedData }}
      {{ $key }}: {{ $val }}
      {{- end }}

So after you generate the sealed secret yaml file, copy the whole part of encryptedData to Value.yaml

encryptedData:
    foo: AgDN88FoKgsVieInalCeRNUVql7hXsWwGxU1GD8UxeRYoDbhBhbNCZbItBwicUN8cRmQvvybqClBMhr5X1WYc2utt0MnYtq6dCSZ6Ci9M+lKhNncM2tmDOH6LDHTicLWR1+xy240B57ph3Bfi9wyvp2D+U/UGyI/sbHeFmVvgdDIikBsTe3OqDKpjNdL1Vh974ZE7z2mpa4Syw0yBWHDaX6GE4dLtBZfhjp7wACcSQ9KLuovhGrl1Gs5TSMFW6nLguE0g4T5MO0OC1/7jWqvsUUZu9+HiLqXNdIE5Rf7bHxAD90y8nr1jqNpTJ8IK1g8krbmaPfTCG2QIXR5Lhn/XIXBD1VFgsaChZsSx8Rt4pthmztrCwXk8YO9B9GA+2rfkBVxXQ3lO2BQKFwX+WrgOgUkljcAafl2F4qXXaMVV+00ZQ8I01yjef0V0Baud2hRd0Z11gI7Rw0/+BEiC/eoTfRJh+LRj+a6dijdsEUskSfAM/ahu7ALNq/NXmvdYHWrMSkV1DL+w6Vs0Qt172PkD57VSFhxcemstS5XHYIdymPXbayrxXJ6HbiDQh/ffbJk/mS3xYDFYLSe4PEt45Uoz2a+E1MhbcQJ6dh6lBOgS9brsl26ctiNoY4W1HY9Wb8bYn0yvP46HL59VPq53k3FVoxvXBGhk=
    foo2: AgCK2Gm6yfraFNAaM16CF79nrBzv9Q1QV2rdhBd7AjI01PORQ4T65P8BiXK2MckZ37v3Qd+PU0UEWCFvTQ4fp4ayEIgVM3AxLy1LXwny79/ifhyVyhGQIicQqPnrVmKogvuGvcksS+KquIc6QeL5Z9X3rfPN1SAASW4PbbfEcIyvQJl5Oihofx0DqdXJHZYQDY0SQ5Ke56cy5qo/9recVeagAl38NIQvKTtuTsGY5SqnYSfcmlWXP+MgrA1cbYvFYmCpRoux29y1wQx8fQ/aBH/cccj4cmL28gZtWGK+VJ6a2cYwMJtxdyG2OwTsBj8nzHQc5I7uxMkgeQtmiZViZ1GCt7BYm7GWvg8o77BwYzqDlelkBdgW/YQ9pNbyaDGxjhJZUwt34EvIvWvHwUigMawmSRhY3M2mJixN7hAuDWPL3DM4olSJpPTaqe/m/LbXYaGsnE2TaKRxhLNsGFpGw4a5n76MtPqijKyXpgvsmVwe/GTk8XS28ubO1tx1sIA3SZX5tLb7klfKwvfJ7MBYLEBY9roCNIzRfywq4rK40P+NA75m7vTL749F7+7TczL+2PDzZdpQrbTYde+LGHQ+E2fFf5rihTgiuNj9YOBSvpLriVaWvetM=

If you want to reference these secrets as env in deployment or pod,

          env:
          {{- range $key, $val := .Values.encryptedData }}
          - name: {{ $key }}
            valueFrom:
              secretKeyRef:
                name: {{ include "app_name.fullname" $ }}
                key: {{ $key }}
          {{- end }}

Notes,

In range, the context has been changed, so if you need reference other values, the original "." will be failed with error

can't evaluate field Values in type interface {}

So you need replace "." to "$". The $ symbol is used to reference the root context of the template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants