sealed-secrets icon indicating copy to clipboard operation
sealed-secrets copied to clipboard

When sealing multiple secrets in singe yaml file only first is sealed

Open devopsmariocom opened this issue 6 years ago • 21 comments

Steps to reproduce

10:27:56 in ~/Downloads/kubeseal-test
➜ kubectl create secret generic mysecret --dry-run --from-literal=foo=bar -o yaml >mysecret.yaml

10:28:13 in ~/Downloads/kubeseal-test
➜ cat mysecret.yaml                                                                             
apiVersion: v1
data:
  foo: YmFy
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret

10:28:14 in ~/Downloads/kubeseal-test
➜ echo "---" >> mysecret.yaml

10:28:34 in ~/Downloads/kubeseal-test
➜ kubectl create secret generic mysecret2 --dry-run --from-literal=foo2=bar2 -o yaml >>mysecret.yaml

10:28:56 in ~/Downloads/kubeseal-test
➜ cat mysecret.yaml                                                                       
apiVersion: v1
data:
  foo: YmFy
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret
---
apiVersion: v1
data:
  foo2: YmFyMg==
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret2

10:28:59 in ~/Downloads/kubeseal-test
➜ kubeseal --format yaml <mysecret.yaml >mysealedsecret.yaml

10:29:36 in ~/Downloads/kubeseal-test
➜ cat mysealedsecret.yaml 
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: mysecret
  namespace: default
spec:
  encryptedData:
    foo: AgBcOSwLF/cCWmAR7J3mZvFBJIYq7QIjBUQbymqHAhhUDwqpukiCRnA3Rr9iTOXUDOQLthjoujU9NLIM/5xpSFy2AjFRexA02B4ZoxqDFD2ZNxh1kFzedKEDRYgt855jaCwZZbqCnJc4Q4I9m0fWioj9+Ij3rFouVlu+QATpqyAZ7baiVa3wYp6s9YnDSsGwS0OI2URfB5wxZyEIF/vmw/TuZe6dN/qgPVQOK3wWN1+/j9dMYFPgUbTi9gcYr7SerYFCwi5dergNLagEb/zovMhQXymvvhFTeqk4AOZXXSpVbUNIIpdatLWntIXa1k7xNWIl+O1y0KHmLEmW8Ynv2GHbl0c04suJTAapMmWcVorlN1eRjmPaXogtPDTrbDMzs4SqFM6KAxhairwpjZEX8A7dIfWohGVb01aD4wjdzWd0Jgi5WL6LyCkhvV7YUGDfoJsV8Av1WF1snRJRh2ChoTGbHre2i6eZXAUUDDsLLnVRvEioCFMsKVe+pkXHwZhDhVrCmdswUB4mvNDolpEDZ/pj0ykz9M6WMTErs9VlNB1OHvWBr/B6AIDU/HTomJ4zd9siMhwN+zksLz88pfF8kcKlC/nHd+3xtsPsL8jZbppp8Bw1DwXu19FGOwpwDyGoFxkeB3OH5ECK9yuhHGdecoPoDLDco4An+NOgLI52Qp9fscTuezA4aw9AH7L71XQzqd6BB9A=

you can see only mysecret and mysecret2 is truncated

devopsmariocom avatar Sep 11 '18 08:09 devopsmariocom

Thanks for the report. Yep, the input is assumed to only be a single document, not a full YAML stream atm. We can change that, but obviously that would only work for -o yaml (not json), which unfortunately breaks the symmetry beween these options :(

Related: I think we should completely rethink kubeseal to work on individual key values rather than entire Secrets, now that we've changed the SealedSecrets schema. So we might end up obsoleting this particular issue rather than fixing it directly...

anguslees avatar Sep 14 '18 01:09 anguslees

@anguslees yes! that would solve another issue we have. We want to allow developers to exchange encrypted credentials so other side get sealed credentials and all they have to do is deploy them to k8s with sealed-secrets setup. Unfortunately currently encrypting side has to know into which namespace it will be deployed as namespace is part of encryption algorithm. So definitely 👍

Also it would be great to have simple go api to allow integrate sealed-secrets into other tools like helm without need to use whole kubeseal binary and bash magic.

Is there any public roadmap?

Thanks!

devopsmariocom avatar Sep 14 '18 10:09 devopsmariocom

What if we use the {"apiVersion": "v1", "kind": "List", "items": [...]} to render to and from json?

mkmik avatar Sep 11 '19 17:09 mkmik

FTR, per-item encryption has been implemented in v0.7.0

mkmik avatar Sep 11 '19 17:09 mkmik

Unfortunately currently encrypting side has to know into which namespace it will be deployed as namespace is part of encryption algorithm.

This is by design in order to allow you to enforce strict namespace isolation with RBAC rules. For your use case to work you need to explicitly pass the sealedsecrets.bitnami.com/cluster-wide: "true" annotation, see v0.6.0 release notes

mkmik avatar Sep 11 '19 17:09 mkmik

It looks like just outputting multiple json objects back-to-back also works: https://github.com/kubernetes/kubernetes/issues/8362

So you can have:

{
  "apiVersion": "v1",
  ...
}
{
  "apiVersion": "v1",
  ...
}

I also want it for yaml since it is pretty common to store multiple secrets in one file.

SerialVelocity avatar Apr 09 '20 16:04 SerialVelocity

Outputting multiple json objects doesn't seem to work anymore, getting the following error:

error: couldn't get version/kind; json parse error: invalid character '{' after top-level value

With:

$ kubeseal --version
kubeseal version: v0.12.1+dirty
$ kubectl version --client     
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.2", GitCommit:"52c56ce7a8272c798dbc29846288d7cd9fbae032", GitTreeState:"clean", BuildDate:"2020-04-16T11:56:40Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}

Example:

apiVersion: v1
kind: Secret
metadata:
  name: firstsecret
  namespace: randomns
type: Opaque
stringData:
  SECRET: EXAMPLE
---
apiVersion: v1
kind: Secret
metadata:
  name: secondsecret
  namespace: randomns
type: Opaque
stringData:
  SECRET2: EXAMPLE
kubectl create -f example.yaml --dry-run=client -o json | kubeseal --cert ../workload-secrets.pem -o yaml

voor avatar May 03 '20 13:05 voor

A v1/List will work

mkmik avatar May 03 '20 15:05 mkmik

Different error, but still an error:

error: converting (v1.List) to (v1.Secret): ObjectMeta not present in src
$ <example.yaml kubeseal --cert workload-secrets.pem -o yaml

example.yaml:

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: Secret
  metadata:
    name: firstsecret
    namespace: randomns
  type: Opaque
  stringData:
    SECRET: EXAMPLE
- apiVersion: v1
  kind: Secret
  metadata:
    name: secondsecret
    namespace: randomns
  type: Opaque
  stringData:
    SECRET2: EXAMPLE

Guessing it might have to do with the usage of stringData

voor avatar May 03 '20 16:05 voor

I meant, it "will" work once implemented

mkmik avatar May 03 '20 19:05 mkmik

voor avatar May 03 '20 19:05 voor

It should be easy to implement hence the "good first issue" label. I like small self-contained PRs to review :-)

mkmik avatar May 03 '20 20:05 mkmik

@voor I meant kubectl accepts multiple json without a list being needed, not kubeseal

SerialVelocity avatar May 03 '20 23:05 SerialVelocity

supporting multiple secrets would be very useful, that way kustomize could be used to create the secrets.

kubectl create --dry-run=client -k . | kubeseal ...

mikkosivulainen avatar Sep 17 '20 07:09 mikkosivulainen

once implementing this, let's make sure it works also on --validate (see #474)

mkmik avatar Nov 09 '20 13:11 mkmik

I'm using kustomize to generate secrets to send in to the kubeseal command. Does anyone have any bash script workarounds to iterate over each secret in a multiyaml output to send to kubeseal yet?

I'm very much an amateur at bash scripting so creating a loop that iterates over each secret in a single file (split by ---??) has been quite difficult for me.

For example, here's my yaml:

data:
  firebase-dev-credentials.json: |
    MY_BASE_64_CREDENTIALS
kind: Secret
metadata:
  name: nodejs-firebase-credentials-989t6dg7m2
type: Opaque
---
apiVersion: v1
data:
  AGENDA_DATABASE_URL: |
    MY_BASE_64_URL
kind: Secret
metadata:
  name: nodejs-secrets-c229fkh579
type: Opaque

Running kubectl kustomize . will output the above text to the shell. How can I loop over each yaml using a bash script to pipe to kubeseal in the mean time? Would love any guidance!

uncvrd avatar Jun 06 '21 21:06 uncvrd

Here is a python script looping on multiple yamls and passing them to kubeseal

#!/bin/python3
import subprocess
import sys

kubeseal = "kubeseal -o yaml".split()

secrets = sys.stdin.read()
for secret in secrets.split("\n---\n"):
    process = subprocess.Popen(kubeseal, stdin=subprocess.PIPE)
    process.stdin.write(secret.encode())
    process.communicate()

    if process.returncode == 0:
        print("---")

Save it in seal.py then chmod u+x seal.py, and you should be able to seal multiple secrets generated with kubectl kustomize . | ./seal.py

bchabanne-cochl avatar Jun 09 '21 08:06 bchabanne-cochl

Although love python just like everyone else, let me chime in with some underrated good ol' shell scripting with awk:

awk 'BEGIN {SR=0} /^-+$/{SR++} !/^-+$/{out=SR ".yaml"; print > out}' multi.yaml
for i in [0-9]*.yaml; do
  kubeseal -f $i -w sealed-$i
done

(it correctly deals with a multidoc yaml file that begins with --- by just starting the numbering from 1 instead of 0)

Example:

$ cat multi.yaml
---
apiVersion: v1
data:
  foo: YmFy
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret
---
apiVersion: v1
data:
  foo: YmF6
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret2
---
apiVersion: v1
data:
  foo: cXV4
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret3
$ awk 'BEGIN {SR=1} /^-+$/{SR++} !/^-+$/{out=SR ".yaml"; print > out}' multi.yaml
$ ls
1.yaml	2.yaml	3.yaml
$ for i in [0-9]*.yaml; do kubeseal -f $i -w sealed-$i; done
$ ls
1.yaml	2.yaml	3.yaml
sealed-1.yaml sealed-2.yaml sealed-3.yaml

mkmik avatar Jun 10 '21 07:06 mkmik

It would be really great to get this feature in the product. I think many people use multi-document yamls in order to organize their resources. Going off @bchabanne-cochl, I wrote a python script which loops through the multi-document yaml and outputsindividual .json seals with names SEALED-.json. I am using subprocess.Popen which I think is discouraged. I am going to put this little script in my path variable until support is added for multi-document yamls. You invoke the script like python3 script.py multi_doc_secret.yaml

#!/bin/python3
import sys
import yaml
import subprocess
nargs = len(sys.argv)

filename = sys.argv[1]

with open(filename) as file:
    docs = yaml.load_all(file, Loader=yaml.SafeLoader)
    for doc in docs:
        with open("SEALED-" + doc["metadata"]["name"] + ".json", "w") as outFile:
            process = subprocess.Popen(
                "kubeseal", stdin=subprocess.PIPE, stdout=outFile)
            process.stdin.write(
                yaml.dump(doc, default_flow_style=False).encode())
            process.communicate()

michaelfortunato avatar Jul 20 '21 03:07 michaelfortunato

any update on having multiple sealed secret in single values template like I have to connect to different DB's and I need to create 2 sealed secret one for oracle and other for Postgres. But I can deploy 2 apps using single templates ? is this possible ? if yes can you please provide me some insight?

anudina avatar Jan 19 '22 03:01 anudina

To aid with splitting the secrets files, GNU Coreutils has a csplit command for context-sensitive file splitting:

$ csplit -z -s multistream-secrets.yaml -f secret- -b %02d.yaml '/^---$/' '{*}'

$ ls -1 secret-*.yaml
secret-00.yaml
secret-01.yaml
secret-02.yaml

yrro avatar May 26 '22 14:05 yrro