secrets
AGE key validation, SOPS-driven template rendering, and KubernetesSecret manifest generation. Other blueprints modules depend on this one
rather than implementing SOPS workflows directly. Created in #143 to
consolidate three previous implementations across configuration, vm,
and kubernetes-deployment.
Installation
dagger install github.com/stuttgart-things/blueprints/secrets@v3.6.0Entrypoint
Return Type
Secrets Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
func (m *MyModule) Example() *dagger.Secrets {
return dag.
Secrets()
}@function
def example() -> dagger.Secrets:
return (
dag.secrets()
)@func()
example(): Secrets {
return dag
.secrets()
}Types
Secrets 🔗
clusterAgeKey() 🔗
ClusterAgeKey returns the cluster’s private key from a GenerateClusterSecrets output, e.g. to hand to flux bootstrap as –sops-age-key. Fails if the key does not match the recorded age.pub.
Return Type
Secret !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| existing | Directory ! | - | Output of GenerateClusterSecrets |
| masterAgeKey | Secret ! | - | AGE key the cluster key is encrypted for |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
cluster-age-key --existing DIR_PATH --master-age-key env:MYSECRETfunc (m *MyModule) Example(existing *dagger.Directory, masterAgeKey *dagger.Secret) *dagger.Secret {
return dag.
Secrets().
Clusteragekey(existing, masterAgeKey)
}@function
def example(existing: dagger.Directory, masteragekey: dagger.Secret) -> dagger.Secret:
return (
dag.secrets()
.clusteragekey(existing, masteragekey)
)@func()
example(existing: Directory, masterAgeKey: Secret): Secret {
return dag
.secrets()
.clusterAgeKey(existing, masterAgeKey)
}createKubernetesSecret() 🔗
CreateKubernetesSecret builds a Kubernetes Secret manifest from name, namespace, and comma-separated key=value pairs, then encrypts it with SOPS using the given AGE public key. Returns the encrypted manifest as a *dagger.File.
Values are base64-encoded and placed under data: to match the standard
Kubernetes Secret layout.
Usage:
dagger call -m secrets create-kubernetes-secret \
--name my-secret --namespace default \
--key-values "user=admin,password=s3cret" \ # pragma: allowlist secret
--age-public-key env:AGE_PUB \
export --path ./secret.enc.yaml
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| name | String ! | - | No description provided |
| namespace | String ! | - | No description provided |
| keyValues | String ! | - | Comma-separated key=value pairs (e.g. “user=admin,password=s3cret”) # pragma: allowlist secret |
| agePublicKey | Secret ! | - | AGE public key for SOPS encryption |
| sopsConfig | File | - | SOPS config file (.sops.yaml) |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
create-kubernetes-secret --name string --namespace string --key-values string --age-public-key env:MYSECRETfunc (m *MyModule) Example(name string, namespace string, keyValues string, agePublicKey *dagger.Secret) *dagger.File {
return dag.
Secrets().
Createkubernetessecret(name, namespace, keyValues, agePublicKey)
}@function
def example(name: str, namespace: str, keyvalues: str, agepublickey: dagger.Secret) -> dagger.File:
return (
dag.secrets()
.createkubernetessecret(name, namespace, keyvalues, agepublickey)
)@func()
example(name: string, namespace: string, keyValues: string, agePublicKey: Secret): File {
return dag
.secrets()
.createKubernetesSecret(name, namespace, keyValues, agePublicKey)
}createKubernetesSecretString() 🔗
CreateKubernetesSecretString is the string-returning variant of CreateKubernetesSecret.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| name | String ! | - | No description provided |
| namespace | String ! | - | No description provided |
| keyValues | String ! | - | No description provided |
| agePublicKey | Secret ! | - | No description provided |
| sopsConfig | File | - | No description provided |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
create-kubernetes-secret-string --name string --namespace string --key-values string --age-public-key env:MYSECRETfunc (m *MyModule) Example(ctx context.Context, name string, namespace string, keyValues string, agePublicKey *dagger.Secret) string {
return dag.
Secrets().
Createkubernetessecretstring(ctx, name, namespace, keyValues, agePublicKey)
}@function
async def example(name: str, namespace: str, keyvalues: str, agepublickey: dagger.Secret) -> str:
return await (
dag.secrets()
.createkubernetessecretstring(name, namespace, keyvalues, agepublickey)
)@func()
async example(name: string, namespace: string, keyValues: string, agePublicKey: Secret): Promise<string> {
return dag
.secrets()
.createKubernetesSecretString(name, namespace, keyValues, agePublicKey)
}decrypt() 🔗
Decrypt decrypts a SOPS-encrypted file with the given AGE private key and returns the plaintext contents.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| sopsKey | Secret ! | - | AGE private key (AGE-SECRET-KEY-…) |
| encryptedFile | File ! | - | SOPS-encrypted file (YAML/JSON) |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
decrypt --sops-key env:MYSECRET --encrypted-file file:pathfunc (m *MyModule) Example(ctx context.Context, sopsKey *dagger.Secret, encryptedFile *dagger.File) string {
return dag.
Secrets().
Decrypt(ctx, sopsKey, encryptedFile)
}@function
async def example(sopskey: dagger.Secret, encryptedfile: dagger.File) -> str:
return await (
dag.secrets()
.decrypt(sopskey, encryptedfile)
)@func()
async example(sopsKey: Secret, encryptedFile: File): Promise<string> {
return dag
.secrets()
.decrypt(sopsKey, encryptedFile)
}encryptFile() 🔗
EncryptFile encrypts a plaintext file with SOPS using an AGE public key and returns the encrypted contents.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| agePublicKey | Secret ! | - | AGE public key for encryption (age1…) |
| plaintextFile | File ! | - | Plaintext file to encrypt |
| fileExtension | String | "yaml" | File extension for SOPS encryption (e.g. “yaml”, “json”) |
| sopsConfig | File | - | SOPS config file (.sops.yaml) |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
encrypt-file --age-public-key env:MYSECRET --plaintext-file file:pathfunc (m *MyModule) Example(ctx context.Context, agePublicKey *dagger.Secret, plaintextFile *dagger.File) string {
return dag.
Secrets().
Encryptfile(ctx, agePublicKey, plaintextFile)
}@function
async def example(agepublickey: dagger.Secret, plaintextfile: dagger.File) -> str:
return await (
dag.secrets()
.encryptfile(agepublickey, plaintextfile)
)@func()
async example(agePublicKey: Secret, plaintextFile: File): Promise<string> {
return dag
.secrets()
.encryptFile(agePublicKey, plaintextFile)
}encryptString() 🔗
EncryptString encrypts an in-memory string with SOPS using an AGE public key. Convenience wrapper around EncryptFile that materializes the input as a file first.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| agePublicKey | Secret ! | - | AGE public key for encryption (age1…) |
| plaintext | String ! | - | Plaintext content to encrypt |
| fileExtension | String | "yaml" | File extension for SOPS encryption (e.g. “yaml”, “json”) |
| sopsConfig | File | - | SOPS config file (.sops.yaml) |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
encrypt-string --age-public-key env:MYSECRET --plaintext stringfunc (m *MyModule) Example(ctx context.Context, agePublicKey *dagger.Secret, plaintext string) string {
return dag.
Secrets().
Encryptstring(ctx, agePublicKey, plaintext)
}@function
async def example(agepublickey: dagger.Secret, plaintext: str) -> str:
return await (
dag.secrets()
.encryptstring(agepublickey, plaintext)
)@func()
async example(agePublicKey: Secret, plaintext: string): Promise<string> {
return dag
.secrets()
.encryptString(agePublicKey, plaintext)
}generateClusterSecrets() 🔗
GenerateClusterSecrets renders every Secret a cluster needs from a ClusterSecrets profile and returns them SOPS-encrypted with a key of that cluster’s own:
.sops.yaml rules for editing the output with plain sops
age.pub the cluster's public key
sops-age.enc.yaml flux-system/sops-age with the cluster's private
key, encrypted for the master key + escrow only
secrets/kustomization.yaml lists every secret below
secrets/<namespace>/<name>.enc.yaml encrypted for the cluster key
Pass the previous output as existing on every later run: the cluster
key and every generated value are then kept (unless named in rotate),
and files whose content and recipients did not change are returned
byte-for-byte, so a re-run yields an empty diff. Export with –wipe so
secrets removed from the profile disappear.
Values come from generate: (crypto/rand), value: literals,
ref+sops://<path>#/<pointer> (files below sopsRefDir, decrypted with
the master key) or ref+vault://<mount>/<path>#/<pointer>. Vault is only
contacted when the profile references it, so a cluster without Vault
needs nothing but the master key.
Usage:
dagger call -m secrets generate-cluster-secrets \
--cluster-profile clusters/edge-01/cluster-secrets.yaml \
--profile-dir profiles/secrets \
--master-age-key file:~/.config/sops/age/master.txt \
--escrow-recipients age1... \
--existing clusters/edge-01/cluster-secrets \
--sops-ref-dir . \
export --path clusters/edge-01/cluster-secrets --wipe
Cached per session only: within one dagger call every use of the result
must see the same run (one key, one set of values), while the next call
must read Vault again.
Return Type
Directory !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| clusterProfile | File ! | - | ClusterSecrets document (kind: ClusterSecrets) |
| masterAgeKey | Secret ! | - | AGE key of the CI / key custodian (key-file format, one identity). Encrypts and decrypts the cluster key; decrypts ref+sops:// files. |
| profileDir | Directory | - | Directory searched (recursively) for SecretProfile documents |
| escrowRecipients | String | - | Comma-separated break-glass AGE public keys every file is also encrypted for |
| existing | Directory | - | Output of a previous run for this cluster |
| sopsRefDir | Directory | - | Base directory for ref+sops:// paths |
| vaultAddr | String | - | Vault address; defaults to http://vault:8200 when vaultService is set |
| vaultService | Service | - | Vault as a Dagger service, bound under the hostname “vault” (tests, local runs) |
| vaultToken | Secret | - | Vault token |
| vaultRoleId | Secret | - | Vault AppRole role ID (used when no token is given) |
| vaultSecretId | Secret | - | Vault AppRole secret ID |
| vaultCacert | File | - | CA certificate for a Vault with a private PKI |
| rotate | String | - | Generated values to regenerate: comma-separated or : |
| defaultNamespace | String | "flux-system" | Namespace for secrets that do not set one |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
generate-cluster-secrets --cluster-profile file:path --master-age-key env:MYSECRETfunc (m *MyModule) Example(clusterProfile *dagger.File, masterAgeKey *dagger.Secret) *dagger.Directory {
return dag.
Secrets().
Generateclustersecrets(clusterProfile, masterAgeKey)
}@function
def example(clusterprofile: dagger.File, masteragekey: dagger.Secret) -> dagger.Directory:
return (
dag.secrets()
.generateclustersecrets(clusterprofile, masteragekey)
)@func()
example(clusterProfile: File, masterAgeKey: Secret): Directory {
return dag
.secrets()
.generateClusterSecrets(clusterProfile, masterAgeKey)
}renderTemplate() 🔗
RenderTemplate decrypts a SOPS-encrypted data file, renders a Go-template against the decrypted values, and (optionally) re-encrypts the result with a different AGE recipient. Returns the rendered file (encrypted by default).
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| ageKey | Secret ! | - | AGE private key for SOPS decrypt (AGE-SECRET-KEY-…) |
| encryptedDataFile | File ! | - | SOPS-encrypted data file (YAML/JSON) whose values feed the template |
| templateFile | File ! | - | Go template file (e.g. secret.json.tmpl) rendered against the decrypted data |
| ageRecipient | Secret | - | AGE public recipient for SOPS re-encrypt (age1…); required when encrypt=true |
| fileExtension | String | "json" | File extension for the SOPS-encrypted output |
| sopsConfig | File | - | Optional .sops.yaml used for both decrypt and encrypt |
| encrypt | Boolean | "true" | When true, SOPS-encrypt the rendered file; when false, return the plaintext render |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
render-template --age-key env:MYSECRET --encrypted-data-file file:path --template-file file:pathfunc (m *MyModule) Example(ageKey *dagger.Secret, encryptedDataFile *dagger.File, templateFile *dagger.File) *dagger.File {
return dag.
Secrets().
Rendertemplate(ageKey, encryptedDataFile, templateFile)
}@function
def example(agekey: dagger.Secret, encrypteddatafile: dagger.File, templatefile: dagger.File) -> dagger.File:
return (
dag.secrets()
.rendertemplate(agekey, encrypteddatafile, templatefile)
)@func()
example(ageKey: Secret, encryptedDataFile: File, templateFile: File): File {
return dag
.secrets()
.renderTemplate(ageKey, encryptedDataFile, templateFile)
}validateAgeKeyPair() 🔗
ValidateAgeKeyPair derives the public key from the given AGE private key and verifies it matches the provided public key. Fails fast on mismatch.
Usage:
dagger call -m secrets validate-age-key-pair --sops-age-key env:SOPS_AGE_KEY --age-public-key env:AGE_PUB
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| sopsAgeKey | Secret ! | - | AGE private key |
| agePublicKey | Secret ! | - | AGE public key to validate against |
Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
validate-age-key-pair --sops-age-key env:MYSECRET --age-public-key env:MYSECRETfunc (m *MyModule) Example(ctx context.Context, sopsAgeKey *dagger.Secret, agePublicKey *dagger.Secret) string {
return dag.
Secrets().
Validateagekeypair(ctx, sopsAgeKey, agePublicKey)
}@function
async def example(sopsagekey: dagger.Secret, agepublickey: dagger.Secret) -> str:
return await (
dag.secrets()
.validateagekeypair(sopsagekey, agepublickey)
)@func()
async example(sopsAgeKey: Secret, agePublicKey: Secret): Promise<string> {
return dag
.secrets()
.validateAgeKeyPair(sopsAgeKey, agePublicKey)
}