3.2 KiB
3.2 KiB
Secrets
SOPS-in-repo with one age key per host. Encrypted files live next to docker-compose.yml in each Charlie/<stack> repo. The reusable deploy-stack.yml workflow SSHes into the target host as sys-gitea-runner and runs sops --decrypt there using /etc/charlie/age.key.
Operational recipe: onboarding.md → Host age key (SOPS).
Caller-facing reference: automation.md.
Goal
Every host can:
- Obtain only the secrets it's allowed to decrypt.
- Materialize them where compose can consume them (typically
.env). - Do this non-interactively from a Gitea Actions runner.
Constraints
- Source of truth lives with the stack. A secret for
mediacomposebelongs inCharlie/mediacompose, not in a separate vault. Blast radius is bounded by repo access. - Per-machine decryption. A compromised host leaks only the secrets in repos whose
.sops.yamllists that host's key. - Self-identifying host.
hostname -s | tr '[:upper:]' '[:lower:]'everywhere. - Versioning is git. Rotation = commit + (when needed)
sops updatekeys+ push.
Layout in a stack repo
Charlie/<stack>/
├── docker-compose.yml
├── .env.example # tracked, redacted
├── secrets/
│ └── env.sops.yaml # encrypted; decrypts to .env
├── .sops.yaml # creation rules: which recipients can decrypt
└── .gitea/workflows/deploy.yaml # calls deploy-stack.yml
For shared stacks, .sops.yaml lists every host that runs the stack.
Keys
- One age key per host, at
/etc/charlie/age.key(0640 root:charlie-deploy). Public keys inhosts.md. - One admin key held by alex on the admin laptop, listed in every repo's
.sops.yamlso secrets stay decryptable from off-host. Backup as a vaultwarden Secure Note (Charlie SOPS admin age key). - Per-host, not per-stack. Container isolation already provides the per-stack property —
/etc/charlie/age.keyisn't bind-mounted into containers.
Rotation
# In the stack repo on the admin laptop.
tmp=$(mktemp) && chmod 600 "$tmp"
printf 'KEY=new-value\n' > "$tmp"
sops --encrypt --input-type dotenv --output-type yaml \
--filename-override secrets/env.sops.yaml "$tmp" > secrets/env.sops.yaml
shred -u "$tmp"
git commit -am 'Rotate <KEY>'
git push # triggers redeploy
For per-host secret files, repeat per host.
Adding/removing recipients (e.g. onboarding mohg): edit .sops.yaml, then sops updatekeys secrets/env.sops.yaml.
What this design intentionally doesn't do
- Manage secrets for stacks that aren't runner-deployed.
- Replace vaultwarden's human-facing UX. Vaultwarden stays for browser autofill + secure notes (including the admin age key backup).
- Solve secrets for
Charlie/giteaauto-deploy — gitea-on-melina is deployed manually (it bootstraps the runners that would deploy it). SOPS is offline crypto though, so its secrets can live encrypted in the repo and a manualsops -d | docker compose up -dworks.