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>
This commit was merged in pull request #1.
This commit is contained in:
+91
@@ -0,0 +1,91 @@
|
||||
# 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
|
||||
|
||||
```sh
|
||||
# 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:
|
||||
|
||||
```sh
|
||||
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 password** — `EMAIL_PASSWORD` in `.env` (reused by vaultwarden, mealie, baserow SMTP). Regenerate at Google Account → App passwords.
|
||||
- [ ] **ProtonMail SMTP token** — `secrets/authentik_email_password`. Regenerate in Proton → SMTP submission.
|
||||
- [ ] **mealie OIDC client secret** — `secrets/mealie.env`. Rotate the `mealie` provider in Authentik.
|
||||
- [ ] **Last.fm API key + secret** — `secrets/navidrome.env`. Reissue at last.fm/api/accounts.
|
||||
- [ ] **Meilisearch master key** — `secrets/meilisearch.env` (used by karakeep + meilisearch). Generate a new random key.
|
||||
- [ ] **karakeep `NEXTAUTH_SECRET`** — `secrets/karakeep.env`. `openssl rand -base64 36`.
|
||||
- [ ] **vaultwarden `ADMIN_TOKEN`** — `secrets/vaultwarden.env`. Regenerate with `vaultwarden hash` (Argon2).
|
||||
- [ ] **jelu `GOOGLE_API_KEY`** — `secrets/jelu.env`. Rotate in Google Cloud console.
|
||||
|
||||
### Internal (LAN-only, still rotate — `kralovna` is reused widely)
|
||||
- [ ] **Postgres password** — `POSTGRES_PASSWORD` in `.env` (`kralovna`).
|
||||
- [ ] **MySQL/MariaDB passwords** — `MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD` in `.env` (`kralovna`).
|
||||
- [ ] **baserow DB password** — `secrets/baserow.env`.
|
||||
- [ ] **photoprism admin + DB passwords** — `secrets/photoprism.env`.
|
||||
- [ ] **jelu DB password** — `secrets/jelu.env`.
|
||||
- [ ] **komf komga password + Kavita API key** — `secrets/komf.env`.
|
||||
- [ ] **openldap admin + readonly passwords** — `secrets/openldap.env`.
|
||||
- [ ] **maloja force password** — `secrets/maloja.env`.
|
||||
- [ ] **valheim server password** — `secrets/valheim.env`.
|
||||
- [ ] **penpot postgres password** — `secrets/penpot.env`.
|
||||
|
||||
### Orphaned services (moved to `secrets/`; rotate if still running anywhere)
|
||||
- [ ] **mediawiki MySQL password** — `secrets/mediawiki.env`.
|
||||
- [ ] **rtorrent RPC2 password** — `secrets/rtorrent.env`.
|
||||
- [ ] **snibox `SECRET_KEY_BASE`** — `secrets/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:
|
||||
|
||||
```sh
|
||||
# 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.
|
||||
Reference in New Issue
Block a user