Dagger
Search

secrets

AGE key validation, SOPS-driven template rendering, and Kubernetes
Secret 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.0

Entrypoint

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
NameTypeDefault ValueDescription
existingDirectory !-

Output of GenerateClusterSecrets

masterAgeKeySecret !-

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:MYSECRET
func (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
NameTypeDefault ValueDescription
nameString !-No description provided
namespaceString !-No description provided
keyValuesString !-

Comma-separated key=value pairs (e.g. “user=admin,password=s3cret”) # pragma: allowlist secret

agePublicKeySecret !-

AGE public key for SOPS encryption

sopsConfigFile -

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:MYSECRET
func (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
NameTypeDefault ValueDescription
nameString !-No description provided
namespaceString !-No description provided
keyValuesString !-No description provided
agePublicKeySecret !-No description provided
sopsConfigFile -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:MYSECRET
func (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
NameTypeDefault ValueDescription
sopsKeySecret !-

AGE private key (AGE-SECRET-KEY-…)

encryptedFileFile !-

SOPS-encrypted file (YAML/JSON)

Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
 decrypt --sops-key env:MYSECRET --encrypted-file file:path
func (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
NameTypeDefault ValueDescription
agePublicKeySecret !-

AGE public key for encryption (age1…)

plaintextFileFile !-

Plaintext file to encrypt

fileExtensionString "yaml"

File extension for SOPS encryption (e.g. “yaml”, “json”)

sopsConfigFile -

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:path
func (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
NameTypeDefault ValueDescription
agePublicKeySecret !-

AGE public key for encryption (age1…)

plaintextString !-

Plaintext content to encrypt

fileExtensionString "yaml"

File extension for SOPS encryption (e.g. “yaml”, “json”)

sopsConfigFile -

SOPS config file (.sops.yaml)

Example
dagger -m github.com/stuttgart-things/blueprints/secrets@4ac1ff2a32de3eb8a4da737e419d077f5e53316c call \
 encrypt-string --age-public-key env:MYSECRET --plaintext string
func (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
NameTypeDefault ValueDescription
clusterProfileFile !-

ClusterSecrets document (kind: ClusterSecrets)

masterAgeKeySecret !-

AGE key of the CI / key custodian (key-file format, one identity). Encrypts and decrypts the cluster key; decrypts ref+sops:// files.

profileDirDirectory -

Directory searched (recursively) for SecretProfile documents

escrowRecipientsString -

Comma-separated break-glass AGE public keys every file is also encrypted for

existingDirectory -

Output of a previous run for this cluster

sopsRefDirDirectory -

Base directory for ref+sops:// paths

vaultAddrString -

Vault address; defaults to http://vault:8200 when vaultService is set

vaultServiceService -

Vault as a Dagger service, bound under the hostname “vault” (tests, local runs)

vaultTokenSecret -

Vault token

vaultRoleIdSecret -

Vault AppRole role ID (used when no token is given)

vaultSecretIdSecret -

Vault AppRole secret ID

vaultCacertFile -

CA certificate for a Vault with a private PKI

rotateString -

Generated values to regenerate: comma-separated or :

defaultNamespaceString "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:MYSECRET
func (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
NameTypeDefault ValueDescription
ageKeySecret !-

AGE private key for SOPS decrypt (AGE-SECRET-KEY-…)

encryptedDataFileFile !-

SOPS-encrypted data file (YAML/JSON) whose values feed the template

templateFileFile !-

Go template file (e.g. secret.json.tmpl) rendered against the decrypted data

ageRecipientSecret -

AGE public recipient for SOPS re-encrypt (age1…); required when encrypt=true

fileExtensionString "json"

File extension for the SOPS-encrypted output

sopsConfigFile -

Optional .sops.yaml used for both decrypt and encrypt

encryptBoolean "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:path
func (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
NameTypeDefault ValueDescription
sopsAgeKeySecret !-

AGE private key

agePublicKeySecret !-

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:MYSECRET
func (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)
}