Skip to content

Latest commit

 

History

History
198 lines (142 loc) · 5.83 KB

File metadata and controls

198 lines (142 loc) · 5.83 KB

SOPS + age Guide

This is a practical reference for how sops and age fit together in this repo, and which assets/commands matter.

What GitOps Reverser does for Secrets

When GitTarget.spec.encryption.provider: sops is enabled:

  • Kubernetes Secret resources 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.yaml in 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 sops at /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/tenantsecrets

Entries 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-path can override the sops binary path.
  • --sops-config-path is optional if you want to point sops at a specific config file.
  • The usual repo flow is still to commit .sops.yaml and encrypted *.sops.yaml files, never age private keys.

Mental model

  • age is the crypto system.
  • sops is 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.yaml contains recipient rules (public info).
  • Private key material for Flux-compatible secrets is stored under *.agekey keys (for example identity.agekey).
  • Secret manifests written to Git are stored as *.sops.yaml.

Important assets

  • age private key: secret, never commit.
  • age recipient (public key): safe to commit/share.
  • .sops.yaml: encryption policy/rules and recipients (usually committed).
  • encrypted *.sops.yaml files: committed artifacts.

Install tools

# 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 binary

Create a new age keypair

umask 077
age-keygen -o age-key.txt

This creates:

  • private key in age-key.txt (line starts with AGE-SECRET-KEY-...)
  • recipient in a comment line like # public key: age1...

Get recipient from private key file:

age-keygen -y age-key.txt

Create/update .sops.yaml

Minimal example:

creation_rules:
  - path_regex: '.*\.sops\.ya?ml$'
    encrypted_regex: '^(data|stringData)$'
    key_groups:
      - age:
          - "age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Notes:

  • path_regex controls which files are encrypted with this rule.
  • encrypted_regex keeps 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/decrypt with sops

Encrypt file:

sops --encrypt --input-type yaml --output-type yaml \
  secret.yaml > secret.sops.yaml

Decrypt file:

SOPS_AGE_KEY="$(grep '^AGE-SECRET-KEY-' age-key.txt)" \
  sops --decrypt secret.sops.yaml

Or use key file:

export SOPS_AGE_KEY_FILE="$PWD/age-key.txt"
sops --decrypt secret.sops.yaml

Key material in Kubernetes

In 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-key

When 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-

Rotation (new recipient/key)

  1. Generate new keypair.
  2. Add new recipient to .sops.yaml.
  3. 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
  1. Deploy new private key to runtime secret.
  2. Remove old recipient from .sops.yaml and run updatekeys again when ready.

Repo-specific security note

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.yaml in the repo path and re-wrap files with sops updatekeys.

What to read next