1
0
Files
docker-compose-templates/SECURITY.md
T
lukas 5aa85b0920 secrets: migrate exposed plaintext secrets to git-crypt
Move all hardcoded credentials out of tracked compose/env files into the
git-crypt-encrypted secrets/ directory, using each app's supported mechanism:

- env_file -> secrets/*.env: mealie, navidrome, karakeep, meilisearch,
  baserow, maloja, valheim, photoprism, komf, openldap, penpot, vaultwarden
- file:///run/secrets: authentik email password
- jelu DB password appended to existing secrets/jelu.env

Untrack root .env (interpolated ${VAR} secrets) and add sanitized
.env.example template; gitignore /.env.

Move unreferenced orphan files (mediawiki/rtorrent/snibox .env) into
secrets/ to preserve values while encrypting them.

Add SECURITY.md documenting the secrets conventions and a rotation
checklist. NOTE: all migrated values remain in prior git history and
must be rotated at their providers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 13:15:25 +02:00

4.5 KiB

Security: secrets handling & rotation checklist

How secrets are stored in this repo

  • secrets/ — git-crypt encrypted (see .gitattributes: secrets/** filter=git-crypt). All real credentials live here. The working-tree copies are plaintext (git-crypt only encrypts the committed blobs), so Docker reads them normally.
  • Root .env — git-ignored (/.env) and not committed. It holds non-secret config (ports, paths, domains) plus a few ${VAR} values interpolated across services. Use .env.example as the template; fill in the blanked secret values locally.
  • Tracked compose / *.env files — must contain no secret values. Pull secrets in via one of:
    • env_file: - secrets/<svc>.env (universal)
    • FILE__VARNAME=/run/secrets/<name> (LinuxServer.io images, e.g. calibre-web-automated)
    • VARNAME_FILE=/run/secrets/<name> (miniflux, gitea-runner, …)
    • file:///run/secrets/<name> (authentik)

Before you commit

# git-crypt must be unlocked so secrets/ files encrypt on commit
git-crypt status | grep -i 'not encrypted' || echo "all secrets/ files encrypted"

Quick scan for accidental plaintext secrets in tracked files:

git ls-files | grep -vE '^secrets/|^\.env\.example$' | xargs grep -nIE \
  '(PASSWORD|SECRET|TOKEN|API_?KEY|CLIENT_SECRET|MASTER_KEY)=[^ ]' 2>/dev/null \
  | grep -vE '_FILE|/run/secrets/|file:///|\$\{|=\s*$'

Rotation checklist

All values below were committed to git history in plaintext before the 2026-06-12 migration. Migrating them to secrets/ only protects them going forward — each must be rotated at its provider, then history scrubbed (see bottom).

Tick each once the credential has been regenerated and the new value written to the corresponding secrets/ file.

External / high priority (reachable beyond the LAN)

  • Gmail app passwordEMAIL_PASSWORD in .env (reused by vaultwarden, mealie, baserow SMTP). Regenerate at Google Account → App passwords.
  • ProtonMail SMTP tokensecrets/authentik_email_password. Regenerate in Proton → SMTP submission.
  • mealie OIDC client secretsecrets/mealie.env. Rotate the mealie provider in Authentik.
  • Last.fm API key + secretsecrets/navidrome.env. Reissue at last.fm/api/accounts.
  • Meilisearch master keysecrets/meilisearch.env (used by karakeep + meilisearch). Generate a new random key.
  • karakeep NEXTAUTH_SECRETsecrets/karakeep.env. openssl rand -base64 36.
  • vaultwarden ADMIN_TOKENsecrets/vaultwarden.env. Regenerate with vaultwarden hash (Argon2).
  • jelu GOOGLE_API_KEYsecrets/jelu.env. Rotate in Google Cloud console.

Internal (LAN-only, still rotate — kralovna is reused widely)

  • Postgres passwordPOSTGRES_PASSWORD in .env (kralovna).
  • MySQL/MariaDB passwordsMYSQL_PASSWORD, MYSQL_ROOT_PASSWORD in .env (kralovna).
  • baserow DB passwordsecrets/baserow.env.
  • photoprism admin + DB passwordssecrets/photoprism.env.
  • jelu DB passwordsecrets/jelu.env.
  • komf komga password + Kavita API keysecrets/komf.env.
  • openldap admin + readonly passwordssecrets/openldap.env.
  • maloja force passwordsecrets/maloja.env.
  • valheim server passwordsecrets/valheim.env.
  • penpot postgres passwordsecrets/penpot.env.

Orphaned services (moved to secrets/; rotate if still running anywhere)

  • mediawiki MySQL passwordsecrets/mediawiki.env.
  • rtorrent RPC2 passwordsecrets/rtorrent.env.
  • snibox SECRET_KEY_BASEsecrets/snibox.env.

Scrubbing git history

After rotating, remove the old plaintext values from history so the leaked secrets become useless even to someone with an old clone:

# Using git-filter-repo (recommended). Removes the old tracked paths entirely.
git filter-repo --invert-paths \
  --path .env \
  --path mediawiki.env --path rtorrent.env --path snibox.env

# Then force-push and have every clone re-clone (rewritten history diverges):
git push --force --all
git push --force --tags

For surgical edits to lines inside files that stay tracked (e.g. a secret that lived in docker-compose.yml), use git filter-repo --replace-text <file> with old==>***REMOVED*** rules, or BFG's --replace-text.

Rotation is what actually neutralizes a leak. History scrubbing is best-effort — assume anything ever pushed is already compromised and rotate regardless.