Skip to content

Advanced Templating v2

With External Secrets Operator you can transform the data from the external secret provider before it is stored as Kind=Secret. You can do this with the Spec.Target.Template. Each data value is interpreted as a golang template.

Examples

You can use templates to inject your secrets into a configuration file that you mount into your pod:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: template
spec:
  # ...
  target:
    name: secret-to-be-created
    # this is how the Kind=Secret will look like
    template:
      engineVersion: v2
      data:
        # multiline string
        config: |
          datasources:
          - name: Graphite
            type: graphite
            access: proxy
            url: http://localhost:8080
            password: "{{ .password }}"
            user: "{{ .user }}"
        # using replace function to rewrite secret
        connection: '{{ .dburl | replace "postgres://" "postgresql://" }}'

  data:
  - secretKey: user
    remoteRef:
      key: /grafana/user
  - secretKey: password
    remoteRef:
      key: /grafana/password
  - secretKey: dburl
    remoteRef:
      key: /database/url

Another example with two keys in the same secret:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: template
spec:
  # ...
  target:
    template:
      engineVersion: v2
      data:
        name: admin
        password: "{{ .mysecret }}"                   # If you are using plain manifests or gitops tools
        # password: '{{ printf "{{ .mysecret }}" }}'  # If you are using templated tools like helm
  data:
  - secretKey: mysecret
    remoteRef:
      key: /credentials

TemplateFrom

You do not have to define your templates inline in an ExternalSecret but you can pull ConfigMaps or other Secrets that contain a template. Consider the following example:

# define your template in a config map
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-config-tpl
data:
  config.yaml: |
    datasources:
      - name: Graphite
        type: graphite
        access: proxy
        url: http://localhost:8080
        password: "{{ .password }}"
        user: "{{ .user }}"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-template-example
spec:
  # ...
  target:
    name: secret-to-be-created
    template:
      engineVersion: v2
      templateFrom:
      - configMap:
          # name of the configmap to pull in
          name: grafana-config-tpl
          # here you define the keys that should be used as template
          items:
          - key: config.yaml
  data:
  - secretKey: user
    remoteRef:
      key: /grafana/user
  - secretKey: password
    remoteRef:
      key: /grafana/password

Extract Keys and Certificates from PKCS#12 Archive

You can use pre-defined functions to extract data from your secrets. Here: extract keys and certificates from a PKCS#12 archive and store it as PEM.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: template
spec:
  # ...
  target:
    template:
      type: kubernetes.io/tls
      engineVersion: v2
      data:
        tls.crt: "{{ .mysecret | pkcs12cert }}"
        tls.key: "{{ .mysecret | pkcs12key }}"

        # if needed unlock the pkcs12 with the password
        tls.crt: "{{ .mysecret | pkcs12certPass "my-password" }}"

Extract from JWK

You can extract the public or private key parts of a JWK and use them as PKCS#8 private key or PEM-encoded PKIX public key.

A JWK looks similar to this:

{
  "kty": "RSA",
  "kid": "cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df",
  "use": "sig",
  "n": "pjdss...",
  "e": "AQAB"
  // ...
}

And what you want may be a PEM-encoded public or private key portion of it. Take a look at this example on how to transform it into the desired format:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: template
spec:
  # ...
  target:
    template:
      engineVersion: v2
      data:
        # .myjwk is a json-encoded JWK string.
        #
        # this template will produce for jwk_pub a PEM encoded public key:
        # -----BEGIN PUBLIC KEY-----
        # MIIBI...
        # ...
        # ...AQAB
        # -----END PUBLIC KEY-----
        jwk_pub: "{{ .myjwk | jwkPublicKeyPem }}"
        # private key is a pem-encoded PKCS#8 private key
        jwk_priv: "{{ .myjwk | jwkPrivateKeyPem }}"

Filter PEM blocks

Consider you have a secret that contains both a certificate and a private key encoded in PEM format and it is your goal to use only the certificate from that secret.

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvxGZOW4IXvGlh
 . . .
m8JCpbJXDfSSVxKHgK1Siw4K6pnTsIA2e/Z+Ha2fvtocERjq7VQMAJFaIZSTKo9Q
JwwY+vj0yxWjyzHUzZB33tg=
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDMDCCAhigAwIBAgIQabPaXuZCQaCg+eQAVptGGDANBgkqhkiG9w0BAQsFADAV
 . . .
NtFUGA95RGN9s+pl6XY0YARPHf5O76ErC1OZtDTR5RdyQfcM+94gYZsexsXl0aQO
9YD3Wg==
-----END CERTIFICATE-----

You can achieve that by using the filterPEM function to extract a specific type of PEM block from that secret. If multiple blocks of that type (here: CERTIFICATE) exist then all of them are returned in the order they are specified.

Helper functions

Info

Note: we removed env and expandenv from sprig functions for security reasons.

We provide a couple of convenience functions that help you transform your secrets. This is useful when dealing with PKCS#12 archives or JSON Web Keys (JWK).

In addition to that you can use over 200+ sprig functions. If you feel a function is missing or might be valuable feel free to open an issue and submit a pull request.


Function Description
pkcs12key Extracts all private keys from a PKCS#12 archive and encodes them in PKCS#8 PEM format.
pkcs12keyPass Same as pkcs12key. Uses the provided password to decrypt the PKCS#12 archive.
pkcs12cert Extracts all certificates from a PKCS#12 archive and orders them if possible. If disjunct or multiple leaf certs are provided they are returned as-is.
Sort order: leaf / intermediate(s) / root.
pkcs12certPass Same as pkcs12cert. Uses the provided password to decrypt the PKCS#12 archive.
filterPEM Filters PEM blocks with a specific type from a list of PEM blocks.
jwkPublicKeyPem Takes an json-serialized JWK and returns an PEM block of type PUBLIC KEY that contains the public key. See here for details.
jwkPrivateKeyPem Takes an json-serialized JWK as string and returns an PEM block of type PRIVATE KEY that contains the private key in PKCS #8 format. See here for details.
toYaml Takes an interface, marshals it to yaml. It returns a string, even on marshal error (empty string).
fromYaml Function converts a YAML document into a map[string]interface{}.

Migrating from v1

If you are still using v1alpha1, You have to opt-in to use the new engine version by specifying template.engineVersion=v2:

apiVersion: external-secrets.io/v1alpha1
kind: ExternalSecret
metadata:
  name: secret
spec:
  # ...
  target:
    template:
      engineVersion: v2
  # ...

The biggest change was that basically all function parameter types were changed from accepting/returning []byte to string. This is relevant for you because now you don't need to specify toString all the time at the end of a template pipeline.

apiVersion: external-secrets.io/v1alpha1
kind: ExternalSecret
# ...
spec:
  target:
    template:
      engineVersion: v2
      data:
        # this used to be {{ .foobar | toString }}
        egg: "new: {{ .foobar }}"
Functions removed/replaced
  • base64encode was renamed to b64enc.
  • base64decode was renamed to b64dec. Any errors that occurr during decoding are silenced.
  • fromJSON was renamed to fromJson. Any errors that occurr during unmarshalling are silenced.
  • toJSON was renamed to toJson. Any errors that occurr during marshalling are silenced.
  • pkcs12key and pkcs12keyPass encode the PKCS#8 key directly into PEM format. There is no need to call pemPrivateKey anymore. Also, these functions do extract all private keys from the PKCS#12 archive not just the first one.
  • pkcs12cert and pkcs12certPass encode the certs directly into PEM format. There is no need to call pemCertificate anymore. These functions now extract all certificates from the PKCS#12 archive not just the first one.
  • toString implementation was replaced by the sprig implementation and should be api-compatible.
  • toBytes was removed.
  • pemPrivateKey was removed. It's now implemented within the pkcs12* functions.
  • pemCertificate was removed. It's now implemented within the pkcs12* functions.