Skip to the content.

Secret synchronization

Every Kubernetes cluster runs a keyhub-secrets-controller, which is responsible for syncing secrets from KeyHub to Kubernetes. Secrets will be automatically synchronized with a 10 minute interval. In case of an error the retry interval is 2 minutes.

To define a mapping between KeyHub and Kubernetes a KeyHubSecret CR can be created. The name of the generated Kubernetes Secret is the same as the name of the KeyHubSecret. The mapping between a secret key and a vault record is based on the uuid of the vault record, e.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  data:
    - name: "<secret key1>"
      record: "<KeyHub vault record uuid>"
      property: "username"
    - name: "<secret key2>"
      record: "<KeyHub vault record uuid>"
      property: "password"

The example above will create the following Secret:

apiVersion: v1
kind: Secret
metadata:
  name: "<name of the secret>"
type: Opaque
data:
  <secret key1>: "<username from KeyHub vault record with uuid>"
  <secret key2>: "<password from KeyHub vault record with uuid>"

Supported property values are username, password, link, file and lastModifiedAt. The default property is password. Timestamps are returned in utc according to RFC3339 format. The keys in the following example will both expose the password field from the KeyHub vault record:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  data:
    - name: "<secret key1>"
      record: "<KeyHub vault record uuid>"
      property: "password"
    - name: "<secret key2>"
      record: "<KeyHub vault record uuid>"

The bcrypt password-hashing function can be used to hash the value of a password field before it is stored in the resulting Secret:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  data:
    - name: "<secret key1>"
      record: "<KeyHub vault record uuid>"
      property: "password"
      format: "bcrypt"

Sometimes secrets are embedded in a configuration file, which is mounted into the pod. In this case the entire configuration file can be uploaded to KeyHub and exposed using file as property value.

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  data:
    - name: "config.yaml"
      record: "<KeyHub vault record uuid>"
      property: "file"

The previous examples all create a secret with type Opaque. To create different types of secrets the Kubernetes secret type can be defined, e.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  template:
    type: kubernetes.io/tls
  ...

Some Kubernetes secret type values have dedicated support to make working with these types easier, see below for details. If no dedicated support for a type has been implemented, the type is handled the same as if the type had been Opaque. This means it is possible to construct a wide range of Kubernetes secrets, e.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: docker-registry-auth
spec:
  template:
    type: kubernetes.io/dockerconfigjson
  data:
    - name: ".dockerconfigjson"
      record: "<KeyHub vault record uuid>"
      property: "file"

Basic authentication

A kubernetes.io/basic-auth secret uses the username and password fields from the vault record. E.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  template:
    type: kubernetes.io/basic-auth
  data:
    - name: "auth"
      record: "<KeyHub vault record uuid>"

SSH authentication

A kubernetes.io/ssh-auth secret requires a PEM formatted file containing just the private-key. E.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  template:
    type: kubernetes.io/ssh-auth
  data:
    - name: "key"
      record: "<KeyHub vault record uuid>"

TLS Secrets

Using multiple KeyHub vault records

If the certificate and key are stored in different vault records, they have to be added using the names tls.crt and tls.key, e.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  template:
    type: kubernetes.io/tls
  data:
    - name: "tls.crt"
      record: "<KeyHub crt vault record uuid>"
    - name: "tls.key"
      record: "<KeyHub key vault record uuid>"

Optionally a CA certificate chain can be defined with the ca.crt field. The CA certificate chain will be included in the tls.crt field, after the leaf certificate.

  data:
    - name: "ca.crt"
      record: "<KeyHub key vault record uuid>"

Using a single KeyHub vault record

To store the certificate and key in a single vault record, they have to be included in a certificate container. Currently the PEM and PKCS#12 container formats are supported. The name field identifies the container format and has to be pem or pkcs12, e.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  template:
    type: kubernetes.io/tls
  data:
    - name: "pem"
      record: "<KeyHub pem vault record uuid>"

The KeyHub password field is used to decrypt a password protected PKCS#12 file.

Both containers can contain a CA certificate chain. The CA certificate chain will be included in the tls.crt field, after the leaf certificate. The first certificate in the container is assumed to be the leaf certificate, and subsequent certificates, if any, are assumed to comprise the CA certificate chain.

To have a seperate field in the generated Secret resource containing the CA certificate chain, define the desired Secret field name in the format field. E.g. Traefik (2.2+) uses the tls.ca field:

  data:
    - name: "ca.crt"
      record: "<KeyHub key vault record uuid>"
      format: tls.ca

Labels and annotations

Helm standard labels set on the KeyHubSecret CR are automatically set on the generated secret.

To overwrite standard labels or to set custom labels and annotations on the generated secret use the metadata field of the spec template, e.g.:

apiVersion: keyhub.topicus.nl/v1alpha1
kind: KeyHubSecret
metadata:
  name: "<name of the secret>"
spec:
  template:
    metadata:
      labels:
        app: foo
      annotations:
        key1: value1
  data:
    - name: "my_secret"
      record: "<KeyHub vault record uuid>"

The generated secret is:

apiVersion: v1
kind: Secret
metadata:
  name: "<name of the secret>"
  labels:
    app: foo
  annotations:
    key1: value1
type: Opaque
data:
  my_secret: ...

Synchronization status

The sync status of a KeyHubSecret CR can be inspected with kubectl:

$ kubectl get keyhubsecrets.keyhub.topicus.nl
NAMESPACE              NAME                                SYNC STATUS
default                example                             Synced

Creation, updates and errors during synchronization are written to the Kubernetes event log, e.g.:

$ kubectl get events
LAST SEEN   TYPE      REASON            OBJECT                            MESSAGE
10s         Normal    SecretUpdated     keyhubsecret/example              Secret has been updated
14s         Normal    SecretCreated     keyhubsecret/ssh-example          Secret (type 'kubernetes.io/ssh-auth') has been created
1m20s       Warning   ProcessingError   keyhubsecret/tls-example          Unsupported secret type: kubernetes.io/tsl
30m         Warning   ProcessingError   keyhubsecret/auth-example         Missing KeyHub vault record(s)

More status details can be found on each KeyHubSecret CR, e.g.:

$ kubectl describe keyhubsecrets.keyhub.topicus.nl example
...
Status:
  Secret Key Statuses:
    Hash:  <bcrypt hash of the value to detect drift>
    Key:   <referenced key>
  Sync:
    Status:  Synced
  Vault Record Statuses:
    Last Modified At:  <KeyHub record modification timestamp>
    Name:              <KeyHub record name>
    Record ID:         <KeyHub record UUID>