Core Concepts
With the v1alpha2 types, the dockhand-secrets-operator supports the multi-tenant use case by requiring a Profile to be defined in each namespace that utilizes a Dockhand Secret by default. If you do not require multi-tenant security then you can enable cross-namespace access through the helm chart or by passing --allow-cross-namespace to the controller. This will allow Dockhand Secrets to reference a Profile in any namespace where the operator has read access.
A Profile can contain one or more secrets backends and provides the dockhand-secrets-operator with the information it needs to connect to a Secrets Manager.
For simplicity the examples below assume a single tenant use case where the Profile exists in the dockhand-secrets-operator namespace.
---
apiVersion: dhs.dockhand.dev/v1alpha2
kind: Profile
metadata:
name: dockhand-profile
namespace: dockhand-secrets-operator
awsSecretsManager:
cacheTTL: 60s
region: us-east-1
accessKeyId: <accessKeyId>
secretAccessKeyRef:
name: dockhand-profile-secrets
key: aws-secret-access-key
azureKeyVault:
cacheTTL: 60s
keyVault: dockcmd
tenant: <tenantId>
clientId: <clientId>
clientSecretRef:
name: dockhand-profile-secrets
key: azure-client-secret
gcpSecretsManager:
cacheTTL: 60s
project: myproject
credentialsFileSecretRef:
name: dockhand-profile-secrets
key: gcp-credentials.json
vault:
cacheTTL: 60s
addr: http://vault:8200
tokenRef:
name: dockhand-profile-secrets
key: vault-token
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: dockhand-profile-secrets
namespace: dockhand-secrets-operator
data:
aws-secret-access-key: <Base64 AWS SECRET KEY>
vault-token: <Base64 encoded vault token>
azure-client-secret: <Base64 encoded azure client secret>
gcp-credentials.json: <Base64 encoded GCP JSON file>
Dockhand Secret is essentially a Go template with alternate delimiters << >> so that you can use it in a Helm chart. The operator is built off dockcmd. Sprig functions are supported and specific versions of secrets are supported through the use of ?version= on the secret name. For simplicity ?version=latest will work with all of the backends but specific versions require the value expected by the backend.
Note that GCP and Azure have 2 forms of supported secrets text or json. The text version will return the entire secret stored in the key/value whereas the json version will interpret the stored value as json and allow you retrieve a single key in the secret.
The Dockhand Secret will generate a secret of type secretSpec.type in the same namespace specified by secretSpec.name. Changes to a Dockhand Secret will trigger a refresh of the Secret managed by that Dockhand Secret. You can optionally have labels or annotations injected on the Secret created by the Dockhand Secret.
The profile field allows you to specify different Profiles, which gives you flexibility to connect to numerous Secrets Managers from the same cluster.
The syncInterval field instructs the operator to poll for changes to that particular Dockhand Secret - something greater than 5s. The default is 0s, which means do not poll.
- Cloud Providers charge for secrets retrieval requests
cacheTTLis specified in theProfileso be aware of your TTL when picking asyncInterval.- See Auto Updates section below
Dockhand Secret supports retrieval of an AWS Secrets Manager json secret using << (aws <secret-name> <json-key>) >>. The <secret-name> supports optional ?version=<version-id> query string.
Note <secret-name> can also be the ARN of the secret.
Suppose you have an AWS Secrets Manager Secret named dockhand-test, which has json data { "alpha": "s3cr3t", "bravo": "another-s3cr3t" }. The following Dockhand Secret would generate create an Opaque Secret in the aws namespace.
---
apiVersion: dhs.dockhand.dev/v1alpha2
kind: Secret
metadata:
name: example-aws-dockhand
namespace: aws
profile:
name: dockhand-profile
namespace: dockhand-secrets-operator
# default 0s - set to an interval greater than 5s to enable polling for changes
syncInterval: 0s
secretSpec:
name: example-aws-secret
type: Opaque
# optional
labels:
dockhand: awesome
annotations:
alpha: bravo
data:
alpha: << (aws "dockhand-test" "alpha") >>
bravo.yaml: |
bravo: << (aws "dockhand-test?version=latest" "bravo") >>
charlie: delta
Result:
---
apiVersion: v1
kind: Secret
metadata:
name: example-aws-secret
namespace: aws
labels:
dockhand: awesome
annotations:
alpha: bravo
data:
alpha: czNjcjN0Cg==
bravo.yaml: YnJhdm86IGFub3RoZXItczNjcjN0CmNoYXJsaWU6IGRlbHRhCg==
Dockhand Secret supports retrieval of Azure Key Vault json secret using << (azureJson <secret-name> <json-key>) >> or text secret using << (azureText <secret-name>) >>. The <secret-name> supports optional ?version=<version-id> query string.
Suppose you have an Azure Key Vault Secret named dockhand-test, which has json data { "alpha": "s3cr3t", "bravo": "another-s3cr3t" } and an Azure Key Vault Secret named dockhand-text-test which has data "text-s3cr3t". The following Dockhand Secret would generate create an Opaque Secret in the azure namespace.
---
apiVersion: dhs.dockhand.dev/v1alpha2
kind: Secret
metadata:
name: example-azure-dockhand
namespace: azure
profile:
name: dockhand-profile
namespace: dockhand-secrets-operator
secretSpec:
name: example-azure-secret
type: Opaque
data:
alpha: << (azureJson "dockhand-test" "alpha") >>
bravo.yaml: |
bravo: << (azureJson "dockhand-test" "bravo") >>
charlie: << (azureText "dockhand-text-test") >>
delta: echo
Result:
---
apiVersion: v1
kind: Secret
metadata:
name: example-azure-dockhand
namespace: azure
data:
alpha: czNjcjN0Cg==
bravo.yaml: YnJhdm86IGFub3RoZXItczNjcjN0CmNoYXJsaWU6IHRleHQtczNjcjN0CmRlbHRhOiBlY2hvCg==
Dockhand Secret supports retrieval of GCP Secrets Manager json secret using << (gcpJson <secret-name> <json-key>) >> or text secret using << (gcpText <secret-name>) >>. The <secret-name> supports optional ?version=<version-id> query string.
Suppose you have an GCP Secrets Manager Secret named dockhand-test, which has json data { "alpha": "s3cr3t", "bravo": "another-s3cr3t" } and an GCP Secrets Manager Secret named dockhand-text-test which has data "text-s3cr3t". The following Dockhand Secret would generate create an Opaque Secret in the gcp namespace.
---
apiVersion: dhs.dockhand.dev/v1alpha2
kind: Secret
metadata:
name: example-gcp-dockhand
namespace: gcp
profile:
name: dockhand-profile
namespace: dockhand-secrets-operator
secretSpec:
name: example-gcp-secret
type: Opaque
data:
alpha: << (gcpJson "dockhand-test" "alpha") >>
bravo.yaml: |
bravo: << (gcpJson "dockhand-test" "bravo") >>
charlie: << (gcpText "dockhand-text-test") >>
delta: echo
Result:
---
apiVersion: v1
kind: Secret
metadata:
name: example-gcp-dockhand
namespace: gcp
data:
alpha: czNjcjN0Cg==
bravo.yaml: YnJhdm86IGFub3RoZXItczNjcjN0CmNoYXJsaWU6IHRleHQtczNjcjN0CmRlbHRhOiBlY2hvCg==
Dockhand Secret supports retrieval of an AWS Secrets Manager json secret using << (aws <secret-name> <json-key>) >>. Note that Vault v2 keystores supports optional ?version= but v1 does not.
Suppose you have a Vault Secret named dockhand-test, which has json data { "alpha": "s3cr3t", "bravo": "another-s3cr3t" }. The following Dockhand Secret would generate create an Opaque Secret in the vault namespace.
---
apiVersion: dhs.dockhand.dev/v1alpha2
kind: Secret
metadata:
name: example-vault-dockhand
namespace: vault
profile:
name: dockhand-profile
namespace: dockhand-secrets-operator
# default 0s - set to an interval greater than 5s to enable polling for changes
syncInterval: 0s
secretSpec:
name: example-aws-secret
type: Opaque
# optional
labels:
dockhand: awesome
annotations:
alpha: bravo
data:
alpha: << (vault "dockhand-test" "alpha") >>
bravo.yaml: |
bravo: << (vault "dockhand-test?version=latest" "bravo") >>
charlie: delta
Result:
---
apiVersion: v1
kind: Secret
metadata:
name: example-vault-secret
namespace: vault
labels:
dockhand: awesome
annotations:
alpha: bravo
data:
alpha: czNjcjN0Cg==
bravo.yaml: YnJhdm86IGFub3RoZXItczNjcjN0CmNoYXJsaWU6IGRlbHRhCg==
Info
If you are using DockhandSecretsin a Helm chart, and you simply want to retrieve the latest version of the secret everytime helm is executed, place an annotation on the DockhandSecretthat generates a timestamp - this will trigger the operator to handle a DockhandSecretchange.
annotations:
updateTimestamp: {{ now | date "20060102150405" | quote }}
A helm chart example might look like:
apiVersion: dhs.dockhand.dev/v1alpha2
kind: Secret
metadata:
name: {{ include "dockhand-demo.fullname" . }}
labels:
{{- include "dockhand-demo.labels" . | nindent 4 }}
annotations:
# use an annotation like this to force a sync everytime a helm deployment is made
updateTimestamp: {{ now | date "20060102150405" | quote }}
profile:
name: dockhand-profile
namespace: dockhand-secrets-operator
# default 0s - set to an interval greater than 5s to enable polling for changes
syncInterval: 0s
secretSpec:
name: {{ include "dockhand-demo.fullname" . }}
type: Opaque
labels:
{{- include "dockhand-demo.labels" . | nindent 4 }}
data:
alphaDB: 'postgresql://user:<< (aws {{ (printf "dockhand-demo-%s-db-password" .Values.environment) | quote }} "alpha") >>@{{ .Values.postgres.host }}:{{ .Values.postgres.port }}/db'
For DaemonSets, Deployments and StatefulSets you can insert the following label, which make the dockhand-secrets-operator auto roll those types when a Dockhand Secret updates the Secret it owns. If this option is combined with a syncInterval greater than 5s, then the operator will roll these types over automatically when it updates the k8s Secret with changes from your Secrets Backend.
metadata:
labels:
dhs.dockhand.dev/autoUpdate: "true"