- 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
3.4 KiB
3.4 KiB
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:
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 noin/etc/ssh/sshd_config), use keys only, and prefer a non-root user with sudo overrootdirect login. - Repo hygiene: keep the repo private until the git history is either
purged (BFG /
git filter-repoon 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.