- Delete backups/*.dump and all real credentials from README/docs
(prod MinIO creds, Casdoor passwords, server IP, admin password)
- All stack secrets now come from git-ignored .env (.env.example added);
docker-compose.prod.yml refuses to boot without them (${VAR:?})
- seed_casdoor.py: bcrypt password hashing, generated console/client secrets
- Harden infra/deploy-webhook: HMAC-verified Gitea webhook receiver
+ systemd unit + README (replaces unauthenticated deploy endpoint)
- docs/SECRET-MANAGEMENT.md: secret inventory + rotation checklist
68 lines
3.4 KiB
Markdown
68 lines
3.4 KiB
Markdown
# Secret management & rotation
|
|
|
|
**Policy (short):** real credentials live only in git-ignored `.env` files —
|
|
the server-side `/srv/erp/.env` (used by the deploy webhook and compose) and
|
|
local dev `.env` copies. Nothing secret goes into git: not in the README,
|
|
not in dumps, not in docs. The repo is (or was) **public** — assume anything
|
|
ever committed is compromised and rotate.
|
|
|
|
`docker-compose.prod.yml` declares every production secret as
|
|
`${VAR:?set in .env}` so the stack **refuses to boot** on defaults.
|
|
|
|
## Where each secret lives
|
|
|
|
| Secret | Used by | Defined in |
|
|
|---|---|---|
|
|
| `POSTGRES_PASSWORD` | postgres, api | server `.env` |
|
|
| `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` | minio, minio-init, api | server `.env` |
|
|
| `CASDOOR_CLIENT_SECRET` | Casdoor application record, api | server `.env` (must match the app in the Casdoor console) |
|
|
| `CASDOOR_ADMIN_PASSWORD` | Casdoor console admin, api (`/users/sync`) | server `.env` (rotate in the Casdoor console too) |
|
|
| `CORS_ORIGINS`, `*_HOST`, `TRAEFIK_ACME_EMAIL` | compose / Traefik | server `.env` |
|
|
| `GITEA_WEBHOOK_SECRET` | deploy-webhook receiver (port 9000) | server `.env` + Gitea webhook config |
|
|
| Monitoring DSNs/keys | OpenPanel, GlitchTip, Grafana | `monitoring/.env` (server-side) |
|
|
|
|
## Rotation checklist — run after any leak suspicion
|
|
|
|
The production branch history contained real credentials (MinIO admin,
|
|
Casdoor passwords, server IP, DB dumps). All of the following must be
|
|
rotated once, on the VPS:
|
|
|
|
```bash
|
|
cd /srv/erp
|
|
|
|
# 1) MinIO root — then update MINIO_ROOT_* in /srv/erp/.env
|
|
mc alias set local http://127.0.0.1:9000 <old-user> '<old-pass>'
|
|
mc admin user add local <new-user> '<new-pass>' # then:
|
|
mc admin policy attach local consoleAdmin --user <new-user>
|
|
mc admin user remove local <old-user>
|
|
# (simplest equivalent: mc admin user svcacct or change root via env +
|
|
# `docker compose up -d minio` — MinIO persists root creds from env)
|
|
|
|
# 2) PostgreSQL — then update POSTGRES_PASSWORD in .env
|
|
docker exec -it procurement-postgres psql -U postgres -c \
|
|
"ALTER USER postgres PASSWORD '<new-strong-password>';"
|
|
docker compose up -d api # api re-reads the DSN from .env
|
|
|
|
# 3) Casdoor console admin (console → Identity → users → admin → reset
|
|
# password) — then update CASDOOR_ADMIN_PASSWORD in .env
|
|
# 4) Casdoor application client secret (console → Applications →
|
|
# inventory-app → edit secret) — then update CASDOOR_CLIENT_SECRET in .env
|
|
# 5) Seeded human users: reset each password in the Casdoor console.
|
|
# 6) Gitea webhook secret (Gitea → repo → Settings → Webhooks) — then update
|
|
# GITEA_WEBHOOK_SECRET in .env and restart the receiver (systemctl restart erp-deploy-webhook)
|
|
|
|
# 7) Apply everything:
|
|
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
|
```
|
|
|
|
- **SSH**: disable password login (`PasswordAuthentication no` in
|
|
`/etc/ssh/sshd_config`), use keys only, and prefer a non-root user with
|
|
sudo over `root` direct login.
|
|
- **Repo hygiene**: keep the repo private until the git history is either
|
|
purged (BFG / `git filter-repo` on the README + `backups/` blobs) or every
|
|
leaked value is rotated. Deleting a file in a later commit does **not**
|
|
remove it from history.
|
|
- **Verify**: after rotation, `docker compose logs api | grep -i error`,
|
|
log into the app, upload + download one attachment, and confirm the
|
|
webhook deploy still works with a test push.
|