bitnami-labs / sealed-secrets

A Kubernetes controller and tool for one-way encrypted Secrets

Home Page:https://sealed-secrets.netlify.app/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Template `data` field treated as `stringData`, `stringData` ignored

javajawa opened this issue · comments

Which component:
Isuse with controller, tested against v0.19.3 and 0.25.0 of docker.io/bitnami/sealed-secrets-controller as deployed by Helm.

Describe the bug

When supplying a template Secret to the SealedSecret, the data field is treated as if it was stringData, and any stringData field is ignored.

To Reproduce

First example -- data is treated as if stringData

cat >secret.yaml <<EOF
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: hello
  namespace: default
spec:
  encryptedData: {}
  template:
    data:
      bar: Y2F0Cg==
      baz: hello
EOF

kubectl apply -f secret.yaml
kubectl get secret hello -o yaml | yq '.data | map_values(@base64d)'
kubectl delete -f secret.yaml

Expected
Either an error from trying to create a Secret where data is not base64
or { "bar": "Y2F0Cg==" }

Actual

bar: Y2F0Cg==
baz: hello

Second point -- stringData is ignored from the template.

cat >secret.yaml <<EOF
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: hello
  namespace: default
spec:
  encryptedData: {}
  template:
    stringData:
      bar: Y2F0Cg==
      baz: hello
EOF

kubectl apply -f secret.yaml
kubectl get secret hello -o yaml | yq '.data | map_values(@base64d)'
kubectl delete -f secret.yaml

Expected

bar: Y2F0Cg==
baz: hello

Actual

[]  # (empty)

Version of Kubernetes:

  • Output of kubectl version:
Client Version: v1.29.1
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.26.11-eks-8cb36c9

Just realised I haven't included any context as to why this is relevant when the secret should be sealed, because that was originally in a slack thread.

The use case this was discovered by was setting up manual sharing in argocd in an incident response -- cluster connections are secrets, which are managed as sealed secrets. We discovered that we could get these in without having to encrypt them by adding them to the template.

This led to the question "is adding unencrypted values to the template a feature". The one answer I got over on the slack channel was "probably?".

It would be good to know what the expected behaviour is to make some secrets a lot less opaque -- going back to the Argo example, three of the four values (cluster name, url, and shard) are public knowledge (and are duplicated by our helm chart to the annotations) and having those not be large encrypted blobs improves readability.

More so than the bug itself, I'd like clarity on whether this is even a valid use, along the lines of:

  • sealed secrets explicitly does not support data in templates, any use is undefined behaviour
  • it's supported, but a special case where only data is unused and is unencrypted, as this reduced validation complexity (current behaviour)
  • it is supported, and will follow the Secret semantics in some future version

Hi @javajawa thanks for the detailed report. Let us check this out and we'll get back to you with an explanation for the issue you're observing

This Issue has been automatically marked as "stale" because it has not had recent activity (for 15 days). It will be closed if no further activity occurs. Thanks for the feedback.

Thank you bot! I am continuing to interact with this thread :3

I have a bit more time this week to actually look at the code myself, and reading through the codebase the behaviour is well defined. .spec.template.data is treated as a source of go template strings that are rendered using the encrypted data as the source

for key, value := range s.Spec.Template.Data {
var plaintext bytes.Buffer
template, err := template.New(key).Parse(value)
if err != nil {
errs = append(errs, multierror.Tag(key, err))
continue
}
err = template.Execute(&plaintext, data)
if err != nil {
errs = append(errs, multierror.Tag(key, err))
}
secret.Data[key] = plaintext.Bytes()
}

There is a concrete example in docs/examples/config-template linked from a not especially obvious section of the readme.

This is mentioned in the schema (and derived helm CRD, but the swagger docs are a lot less clear. There's also a line in the helm helper.tpl which may or may not be setting a default template context?

Overall conclusions:

  • The behaviour is well defined
  • The behaviour is supported
  • The behaviour is documented (but only easy to find with a code search).
  • The behaviour is not the "least surprising", and should probably be documented slightly better

I have put a proposed documentation update in #1456 -- but I'm doing so without a clear understanding of why this feature is "unadvertised". There may be a historical decision I am missing here.

Additionally, I feel that that bug label doesn't apply here any more and should be replaced with the documentation label.