Skip to content

feat(secrets): compose delivery — env_file for nested docker/compose apps#494

Merged
hsinatfootprintai merged 1 commit into
mainfrom
feat/secrets-envfile
Jun 5, 2026
Merged

feat(secrets): compose delivery — env_file for nested docker/compose apps#494
hsinatfootprintai merged 1 commit into
mainfrom
feat/secrets-envfile

Conversation

@hsinatfootprintai
Copy link
Copy Markdown
Contributor

Closes #491. Stacked on #493 (the shared EnvFile extraction); base is refactor/envfile-delivery and re-targets to main once #493 merges.

Summary

KMS-protected tenant secrets are decrypted (via the KMS-unwrapped DEK, incl. the AWS backend in #490) and delivered to the app — but the existing env (LXC config) and file (/run/secrets) modes both miss a docker-compose stack running inside the LXC: a docker container doesn't inherit the LXC environment and doesn't see /run/secrets. That's the gap OTel closed with a dotenv env_file: (#370).

Adds a third delivery mode, compose, that renders the tenant's compose-mode secrets into a single dotenv file at /run/containarium/secrets.env (mode 0400, tmpfs) for docker-compose env_file:. Built on the shared EnvFile helper from #492/#493 — no copy-paste of the OTel plumbing.

secrets store (KMS-wrapped DEK)
   │ daemon unwraps DEK, decrypts values
   ▼
stampSecretsOnLXC → WriteEnvFile(SecretsEnvFile) → /run/containarium/secrets.env (0400, tmpfs)
   ▼ docker-compose:  env_file: /run/containarium/secrets.env
app container sees $DB_PASSWORD, $API_KEY, …

Changes

  • internal/secrets/store.goDeliveryCompose constant; ValidateDelivery accepts it; ValidateValueForDelivery rejects multi-line values for compose at set-time (a dotenv KEY=value line can't hold a newline). Multi-line blobs → use file delivery.
  • pkg/core/container/secrets_envfile.goSecretsEnvFile EnvFile value (/run/containarium/secrets.env, 0400).
  • internal/server/secrets_server.gostampSecretsOnLXC batches compose rows and Write/RemoveEnvFile once per tenant (empty-map Remove cleans up after the last compose secret is deleted/switched).
  • CLI --delivery help + secrets.proto comments updated (regen). No wire change — delivery is a free string validated server-side.
  • docs/security/SECRETS-ENV-VAR-RISK.mdcompose-mode section + exposure profile vs env/file.

Security notes

  • Mode 0400 root (not OTel's 0644) — the compose runtime (root) reads it at container-create time and injects values as env; the app never reads the file, so 0400 root is safe and sufficient.
  • On /run (tmpfs) — evicted on container stop, never on the writable rootfs.
  • Does not leak via incus config show (unlike env mode).

Test plan

  • go build ./... ✅, go vet clean.
  • go test ./internal/secrets/ ./pkg/core/container/ ✅ — ValidateDelivery accepts compose; ValidateValueForDelivery single-line guard (env/file allow multi-line, compose rejects newline/CR); constant shape.

Refs

🤖 Generated with Claude Code

@hsinatfootprintai hsinatfootprintai changed the base branch from refactor/envfile-delivery to main June 5, 2026 03:15
…apps

KMS-protected tenant secrets are decrypted (via the KMS-unwrapped DEK) and
delivered to the app, but the existing `env` (LXC config) and `file`
(/run/secrets) modes both miss a docker-compose stack running inside the
LXC: a docker container doesn't inherit the LXC environment and doesn't
see /run/secrets. This is the gap OTel closed with a dotenv `env_file:`.

Add a third delivery mode, `compose`, that renders the tenant's
compose-mode secrets into a single dotenv file at
/run/containarium/secrets.env (mode 0400, tmpfs) for docker-compose
`env_file:` consumption. Built on the shared EnvFile helper from #492 —
no copy-paste of the OTel plumbing.

- internal/secrets/store.go: DeliveryCompose constant; ValidateDelivery
  accepts it; ValidateValueForDelivery rejects multi-line values for
  compose at set-time (a dotenv KEY=value line can't represent a newline).
- pkg/core/container/secrets_envfile.go: SecretsEnvFile EnvFile value
  (path /run/containarium/secrets.env, mode 0400).
- internal/server/secrets_server.go: stampSecretsOnLXC batches compose
  rows and Write/RemoveEnvFile once per tenant (the empty-map Remove
  cleans up after the last compose secret is deleted).
- CLI `--delivery` help + secrets.proto comments updated (regen); no wire
  change (delivery is a free string validated server-side).
- docs/security/SECRETS-ENV-VAR-RISK.md: compose-mode section + exposure
  profile vs env/file.

Tests: ValidateDelivery accepts compose; ValidateValueForDelivery
single-line guard (env/file allow multi-line, compose rejects newline/CR).

Closes #491.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hsinatfootprintai hsinatfootprintai merged commit d2e47e7 into main Jun 5, 2026
7 checks passed
@hsinatfootprintai hsinatfootprintai deleted the feat/secrets-envfile branch June 5, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(secrets): env_file delivery for KMS-protected secrets to nested docker/compose apps

2 participants