This is a practical reference for how sops and age fit together in this repo, and which assets/commands matter.
When GitTarget.spec.encryption.provider: sops is enabled:
- Kubernetes
Secretresources go through the same write pipeline as other resources. - Secret manifests are written to Git as
*.sops.yaml. - GitOps Reverser bootstraps a path-local
.sops.yamlin the target repo path when needed. - If Secret encryption fails, the Secret write is rejected. There is no plaintext fallback.
- The container image ships with
sopsat/usr/local/bin/sops.
The controller can classify additional Secret-shaped resource types at startup with
--additional-sensitive-resources. The Helm value for a CozyStack-style custom resource is:
controllerManager:
additionalSensitiveResources:
- core.cozystack.io/tenantsecretsEntries use resource for the core API group or group/resource for grouped APIs. The bootstrapped
SOPS rule still encrypts Secret-shaped data and stringData fields.
Operator/runtime notes:
--sops-binary-pathcan override thesopsbinary path.--sops-config-pathis optional if you want to pointsopsat a specific config file.- The usual repo flow is still to commit
.sops.yamland encrypted*.sops.yamlfiles, never age private keys.
ageis the crypto system.sopsis the tool that encrypts/decrypts YAML/JSON/etc. and stores metadata.- You encrypt to recipients (public keys).
- You decrypt with matching private keys.
For this repo:
.sops.yamlcontains recipient rules (public info).- Private key material for Flux-compatible secrets is stored under
*.agekeykeys (for exampleidentity.agekey). - Secret manifests written to Git are stored as
*.sops.yaml.
ageprivate key: secret, never commit.agerecipient (public key): safe to commit/share..sops.yaml: encryption policy/rules and recipients (usually committed).- encrypted
*.sops.yamlfiles: committed artifacts.
# macOS
brew install sops age
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y age
# sops may come from distro package or release binaryumask 077
age-keygen -o age-key.txtThis creates:
- private key in
age-key.txt(line starts withAGE-SECRET-KEY-...) - recipient in a comment line like
# public key: age1...
Get recipient from private key file:
age-keygen -y age-key.txtMinimal example:
creation_rules:
- path_regex: '.*\.sops\.ya?ml$'
encrypted_regex: '^(data|stringData)$'
key_groups:
- age:
- "age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Notes:
path_regexcontrols which files are encrypted with this rule.encrypted_regexkeeps Kubernetes Secret payload fields encrypted.- You can add multiple recipients under
age:. - SOPS encrypts to all listed recipients; it does not select only one.
Encrypt file:
sops --encrypt --input-type yaml --output-type yaml \
secret.yaml > secret.sops.yamlDecrypt file:
SOPS_AGE_KEY="$(grep '^AGE-SECRET-KEY-' age-key.txt)" \
sops --decrypt secret.sops.yamlOr use key file:
export SOPS_AGE_KEY_FILE="$PWD/age-key.txt"
sops --decrypt secret.sops.yamlIn this project, age identities are sourced from GitTarget.spec.encryption.secretRef using *.agekey entries.
Create the runtime secret like this (namespace must match the GitTarget namespace):
kubectl -n sut create secret generic sops-age-key \
--from-literal=identity.agekey="$(cat age-key.txt)"Then reference it from GitTarget.spec.encryption.secretRef.name.
Optional bootstrap mode:
spec:
encryption:
provider: sops
age:
enabled: true
recipients:
extractFromSecret: true
generateWhenMissing: true
secretRef:
name: sops-age-keyWhen enabled and the secret is missing, gitops-reverser generates one age key and creates the secret. Generated secrets include:
configbutler.ai/age-recipient: age1...configbutler.ai/backup-warning: REMOVE_AFTER_BACKUP
If the Secret already exists but has no *.agekey entry, GitOps Reverser adds one.
If the Secret already contains one or more *.agekey entries, it does not overwrite them.
Important: back up the generated private key immediately. Remove the warning annotation after backup:
kubectl annotate secret sops-age-key -n <namespace> configbutler.ai/backup-warning-- Generate new keypair.
- Add new recipient to
.sops.yaml. - Re-wrap existing encrypted files:
sops updatekeys -y path/to/file.sops.yaml
# or for many files
find . -name '*.sops.yaml' -print0 | xargs -0 -n1 sops updatekeys -y- Deploy new private key to runtime secret.
- Remove old recipient from
.sops.yamland runupdatekeysagain when ready.
The bootstrap template currently contains a static recipient in
internal/git/bootstrapped-repo-template/.sops.yaml.
- This is a public recipient, not a private key.
- It is not auto-replaced by the controller.
- If you need your own keys, update committed
.sops.yamlin the repo path and re-wrap files withsops updatekeys.
- For install and starter resource setup:
../README.md - For chart quickstart values:
configuration.md - For the deeper bootstrap/encryption architecture:
design/sops-repo-bootstrap-and-key-management-architecture.md - For explicit non-goals and deferred work:
design/sops-repo-bootstrap-out-of-scope.md