Skip to content

Create and re-create k8s secrets and sealed secrets with the help of vals

License

Notifications You must be signed in to change notification settings

kroonprins/kube-create-secret

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

kube-create-secret

kubectl create secret but with extras. Built on top of vals, so all back-ends handled by vals can be used (AWS secret manager, Hashicorp Vault, Azure Key Vault, ...)

Create a secret from a SecretTemplate containing vals expression to resolve the secrets. Extras on top of what vals already gives out of the box:

  • create a Secret of type kubernetes.io/tls directly from a pkcs12 keystore
  • optionally output directly as a SealedSecret
  • adds an annotation kube-create-secret/source on the generated Secret (or SealedSecret template) containing the original template, to keep track of where the secret values originate from. Easily recreate the secret based on this annotation.
$ kube-create-secret
Usage:
  kube-create-secret [command]

Examples:
  kube-create-secret create -f template.yaml
  kube-create-secret re-create -f secret.yaml
  kube-create-secret show -f secret.yaml
  kube-create-secret new


Available Commands:
  create      Create a secret from a SecretTemplate definition.
  re-create   Re-create a secret from a Secret that was previously created with kube-create-secret.
  show        Show the template for a Secret that was previously created with kube-create-secret.
  new         Print starter template.

Examples

With a template:

$ cat <<EOF | VAR1=foo kube-create-secret create -f -
---
apiVersion: kube-create-secret/v1
kind: SecretTemplate
metadata:
  name: my-secret-template-1
spec:
  metadata:
    name: created-secret-1
    namespace: kube-system
  data:
    POSTGRES_PASSWORD: ref+azurekeyvault://my-vault/postgres-password
    POSTGRES_USERNAME: postgres
    VAR1: ref+envsubst:// \$VAR1
  stringData:
    API_TOKEN: ref+azurekeyvault://my-vault/api-token
  type: Opaque
EOF

Results in:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  annotations:
    kube-create-secret/source: '{"apiVersion":"kube-create-secret/v1","kind":"SecretTemplate","metadata":{"name":"my-secret-template-1"},"spec":{"metadata":{"name":"created-secret-1","namespace":"kube-system"},"data":{"POSTGRES_PASSWORD":"ref+azurekeyvault://my-vault/postgres-password","POSTGRES_USERNAME":"postgres","VAR1":"ref+envsubst://"},"stringData":{"API_TOKEN":"ref+azurekeyvault://my-vault/api-token"},"type":"Opaque"}}'
  name: created-secret-1
  namespace: kube-system
data:
  POSTGRES_PASSWORD: my-postgres-password
  POSTGRES_USERNAME: postgres
  VAR1: foo
stringData:
  API_TOKEN: my-secet-token
type: Opaque

Output format is by default the format of the input but can be set via the -o option.

Multiple yaml documents in one yaml file is supported. E.g.:

$ cat <<EOF | VAR1=foo VAR2=bar kube-create-secret create -f -
---
apiVersion: kube-create-secret/v1
kind: SecretTemplate
metadata:
  name: my-secret-template-1
spec:
  metadata:
    name: created-secret-1
  stringData:
    VAR: ref+envsubst://\$VAR1
  type: Opaque
---
apiVersion: kube-create-secret/v1
kind: SecretTemplate
metadata:
  name: my-secret-template-2
spec:
  metadata:
    name: created-secret-2
  stringData:
    VAR: ref+envsubst://\$VAR2
  type: Opaque
EOF

Gives:

---
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  annotations:
    kube-create-secret/source: '{"kind":"SecretTemplate","apiVersion":"kube-create-secret/v1","metadata":{"name":"my-secret-template-1","creationTimestamp":null},"spec":{"kind":"Secret","apiVersion":"v1","metadata":{"name":"created-secret-1","creationTimestamp":null},"type":"Opaque","stringData":{"VAR":"ref+envsubst://$VAR1"}}}'
  name: created-secret-1
stringData:
  VAR: foo
type: Opaque
---
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  annotations:
    kube-create-secret/source: '{"kind":"SecretTemplate","apiVersion":"kube-create-secret/v1","metadata":{"name":"my-secret-template-2","creationTimestamp":null},"spec":{"kind":"Secret","apiVersion":"v1","metadata":{"name":"created-secret-2","creationTimestamp":null},"type":"Opaque","stringData":{"VAR":"ref+envsubst://$VAR2"}}}'
  name: created-secret-2
stringData:
  VAR: bar
type: Opaque

For multiple documents in json the kubernetes format for lists can be used:

$ cat <<EOF | VAR1=foo VAR2=bar kube-create-secret create -f -
{
  "apiVersion": "v1",
  "kind": "List",
  "items": [
    {
      "apiVersion": "kube-create-secret/v1",
      "kind": "SecretTemplate",
      "metadata": {
        "name": "my-secret-template-1"
      },
      "spec": {
        "metadata": {
          "name": "created-secret-1"
        },
        "stringData": {
          "VAR": "ref+envsubst://\$VAR1"
        },
        "type": "Opaque"
      }
    },
    {
      "apiVersion": "kube-create-secret/v1",
      "kind": "SecretTemplate",
      "metadata": {
        "name": "my-secret-template-2"
      },
      "spec": {
        "metadata": {
          "name": "created-secret-2"
        },
        "stringData": {
          "VAR": "ref+envsubst://\$VAR2"
        },
        "type": "Opaque"
      }
    }
  ]
}
EOF

Which gives:

{
  "kind": "List",
  "apiVersion": "v1",
  "metadata": {},
  "items": [
    {
      "kind": "Secret",
      "apiVersion": "v1",
      "metadata": {
        "name": "created-secret-1",
        "creationTimestamp": null,
        "annotations": {
          "kube-create-secret/source": "{\"kind\":\"SecretTemplate\",\"apiVersion\":\"kube-create-secret/v1\",\"metadata\":{\"name\":\"my-secret-template-1\",\"creationTimestamp\":null},\"spec\":{\"kind\":\"Secret\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"created-secret-1\",\"creationTimestamp\":null},\"type\":\"Opaque\",\"stringData\":{\"VAR\":\"ref+envsubst://$VAR1\"}}}"
        }
      },
      "stringData": {
        "VAR": "foo"
      },
      "type": "Opaque"
    },
    {
      "kind": "Secret",
      "apiVersion": "v1",
      "metadata": {
        "name": "created-secret-2",
        "creationTimestamp": null,
        "annotations": {
          "kube-create-secret/source": "{\"kind\":\"SecretTemplate\",\"apiVersion\":\"kube-create-secret/v1\",\"metadata\":{\"name\":\"my-secret-template-2\",\"creationTimestamp\":null},\"spec\":{\"kind\":\"Secret\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"created-secret-2\",\"creationTimestamp\":null},\"type\":\"Opaque\",\"stringData\":{\"VAR\":\"ref+envsubst://$VAR2\"}}}"
        }
      },
      "stringData": {
        "VAR": "bar"
      },
      "type": "Opaque"
    }
  ]
}

The data or stringData of the secret can be populated with an entire object. E.g.:

$ cat <<EOF | kube-create-secret create -f -
---
apiVersion: kube-create-secret/v1
kind: SecretTemplate
metadata:
  name: my-expanded-secret-template-1
spec:
  metadata:
    name: created-expanded-secret-1
    namespace: kube-system
  data: ref+azurekeyvault://my-vault#/*
  stringData: ref+azurekeyvault://my-vault#/*
  type: Opaque
EOF

Results in:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  annotations:
    kube-create-secret/source: '{"kind":"SecretTemplate","apiVersion":"kube-create-secret/v1","metadata":{"name":"my-expanded-secret-template-1","creationTimestamp":null},"spec":{"kind":"Secret","apiVersion":"v1","metadata":{"name":"created-expanded-secret-1","namespace":"kube-system","creationTimestamp":null},"type":"Opaque","data":"ref+azurekeyvault://my-vault#/*","stringData":"ref+azurekeyvault://my-vault#/*"}}'
  name: created-expanded-secret-1
  namespace: kube-system
data:
  api-token: bXktc2VjcmV0LXRva2Vu
  postgres-password: bXktcG9zdGdyZXNzLXBhc3N3b3Jk
stringData:
  api-token: my-secret-token
  postgres-password: my-postgress-password
type: Opaque

Tls secrets

kubectl create secret tls NAME --cert=path/to/cert/file --key=path/to/key/file allows creating a Secret of type kubernetes.io/tls, but you need the key and certificate already in PEM format. Often the key and cert are in a keystore. For example in Azure Key Vault certificates can be stored and retrieved in pkcs12 format. To create a tls secret from such a file:

$ cat <<EOF | kube-create-secret create -f -
---
apiVersion: kube-create-secret/v1
kind: SecretTemplate
metadata:
  name: my-pkcs12-secret-template
spec:
  metadata:
    name: my-tls-secret
    namespace: default
  tls:
    pkcs12: ref+azurekeyvault://my-vault/my-vault-certificate
  type: kubernetes.io/tls
EOF

Results in:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  annotations:
    kube-create-secret/source: '{"kind":"SecretTemplate","apiVersion":"kube-create-secret/v1","metadata":{"name":"my-pkcs12-secret-template","creationTimestamp":null},"spec":{"kind":"Secret","apiVersion":"v1","metadata":{"name":"my-tls-secret","namespace":"default","creationTimestamp":null},"type":"kubernetes.io/tls","tls":{"pkcs12":"ref+azurekeyvault://my-vault/my-vault-certificate"}}}'
  name: my-tls-secret
  namespace: default
data:
  tls.crt: LS0tL...........SUNBVEUtLS0tLQo=
  tls.key: LS0tL...SBLRVktLS0tLQo=
type: kubernetes.io/tls

If the key store has a password, the password can be defined in the template, e.g.:

tls:
  pkcs12: ref+azurekeyvault://my-vault/my-vault-certificate
  password: ref+envsubst://$PASSWORD

To specify the name of the key and crt in the secret:

tls:
  pkcs12: ref+azurekeyvault://my-vault/my-vault-certificate
  name: foo

Which will result in:

spec:
  ...
  data:
    foo.key: ...
    foo.crt: ...

Alternatively, a different name for the key and certificate can be given:

tls:
  pkcs12: ref+azurekeyvault://my-vault/my-vault-certificate
  key:
    name: foo.key
  crt:
    name: bar.crt

Resulting in:

spec:
  ...
  data:
    foo.key: ...
    bar.crt: ...

If the certficate contains a chain, it can in certain circumstances be useful to add a delimiter between the certificates in the chain:

tls:
  pkcs12: ref+azurekeyvault://my-vault/my-vault-certificate
  crt:
    delimiter: ","

If the key and cert are known in PEM format then nothing special to do, e.g.:

apiVersion: kube-create-secret/v1
kind: SecretTemplate
metadata:
  name: my-pkcs12-pems-secret-template
spec:
  metadata:
    name: my-tls-secret
    namespace: default
  data:
    tls.key: ref+file://my-key.key
    tls.crt: ref+file://my-certificate.crt
  type: kubernetes.io/tls

Sealed secrets

If you're using sealed secrets the output of kube-create-secret can be piped to kubeseal, but for convenience kube-create-secret can directly output sealed secrets. E.g.:

$ cat <<EOF | VAR1=foo kube-create-secret create -f - -o sealed-secret
---
apiVersion: kube-create-secret/v1
kind: SecretTemplate
metadata:
  name: my-sealed-secret-template-1
spec:
  apiVersion: v1
  kind: Secret
  metadata:
    name: created-sealed-secret-1
    namespace: default
  data:
    VAR: ref+envsubst://\$VAR1
  type: Opaque
EOF

Gives:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: created-sealed-secret-1
  namespace: default
spec:
  encryptedData:
    VAR: AgBZuBgjEr...koGVkfwhlQ=
  template:
    metadata:
      creationTimestamp: null
      annotations:
        kube-create-secret/source: '{"kind":"SecretTemplate","apiVersion":"kube-create-secret/v1","metadata":{"name":"my-sealed-secret-template-1","creationTimestamp":null},"spec":{"kind":"Secret","apiVersion":"v1","metadata":{"name":"created-sealed-secret-1","creationTimestamp":null},"type":"Opaque","data":{"VAR":"ref+envsubst://$VAR1"}}}'
      name: created-sealed-secret-1
      namespace: default
    type: Opaque

The input parameters for kubeseal (e.g. to give the sealed secret controller name, the kubernetes context, ...) can be given via the input flags starting with --kubeseal-.

Re-creating secrets

The template from which a secret is generated is saved as an annotation on the the secret. This annotation can be used to re-create the secret if the values in the secret have changed. For example:

$ kubectl get secret my-secret -o yaml | kube-create-secret re-create -f - | kubectl apply -f -

It retrieves the secret from the cluster, re-evaluates the template of it with the latest values for the vals references, and then re-applies it on the cluster.

If an extra secret value needs to be added, the template can first be retrieved back via kube-create-secret show after which the template can be updated and the secret re-generated via kube-create-secret create.

$ kubectl get secret my-secret -o yaml | kube-create-secret show -f -

The re-create command is the combination of the show command and the create command, this is the same as the re-create example from above:

$ kubectl get secret my-secret -o yaml | kube-create-secret show -f - | kube-create-secret create -f | kubectl apply -f -

When using GitOps with sealed secrets you can either store the secret template next to the generated sealed secret in git, or just the generated sealed secret and then use re-create when the sealed secret needs to be updated. For example, for a sealed secret generated by kube-create-secret stored in git, you can do kube-create-secret re-create -f my-sealed-secret.yml to see what is in the sealed secret (assuming the vals reference values didn't change).