chore(security): remove committed secrets/dumps, .env-based config, hardened deploy webhook

- 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
This commit is contained in:
MMD
2026-09-18 21:55:24 +03:30
parent 5a9ef408ff
commit 43e317eeba
15 changed files with 558 additions and 117 deletions
+33 -47
View File
@@ -105,8 +105,8 @@ for production builds).
| What | Where | Credentials |
|---|---|---|
| App | http://localhost (via Traefik) | — |
| Casdoor console | http://localhost:8080 | `admin` / `123` |
| Seeded app users (org `inventory`) | `razaei` (admin), `mousavi` (مدیر تدارکات), `mohaghegh` (مدیر پروژه), `tester` (مدیر پروژه، پروژه «تست») | password `123456` |
| Casdoor console | http://localhost:8080 | admin credentials are set when you run the seeder (`CASDOOR_ADMIN_PW`) — never commit them |
| Seeded app users (org `inventory`) | `razaei` (admin), `mousavi` (مدیر تدارکات), `mohaghegh` (مدیر پروژه), `tester` (مدیر پروژه، پروژه «تست») | password from `CASDOOR_SEED_PASSWORD` (bcrypt-hashed at rest — see `backend/tools/seed_casdoor.py`) |
Run `backend/tools/seed_casdoor.py` once after the first boot to create the
Casdoor organization, application and users (idempotent). Full details —
@@ -143,7 +143,7 @@ is an SSH tunnel from your machine:
use the panel):
```powershell
ssh -L 9001:127.0.0.1:9001 root@171.22.25.75
ssh -L 9001:127.0.0.1:9001 <user>@<server-host>
```
You will also land in a normal SSH session — that is fine; the tunnel
@@ -151,18 +151,17 @@ is an SSH tunnel from your machine:
2. In your browser, open: **http://localhost:9001**
3. Log in with:
| Username | Password |
|---|---|
| `aramesh-minio` | `feb10fadb5441019dfc33bf12a9b0f74e7b6c478` |
3. Log in with the production MinIO root credentials from the server's
`/srv/erp/.env` (`MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD`) — they are
**never** written in this repo or its history. See
[`docs/SECRET-MANAGEMENT.md`](docs/SECRET-MANAGEMENT.md).
You will see the **`procurement-files`** bucket — the invoices/proformas the
ERP stores, browsable and downloadable from there.
⚠️ These credentials are **full admin access to the file storage**. Treat
them as a production secret (this README is inside the repo, so anyone with
repo access can read them — rotate them on the server if they ever leak).
⚠️ Those credentials are **full admin access to the file storage**. They live
only in the server-side `.env`; if one ever leaks, rotate it immediately
(checklist in `docs/SECRET-MANAGEMENT.md`).
Close the PowerShell window (or `Ctrl+C`) when you are done to drop the
tunnel.
@@ -262,47 +261,33 @@ docker exec -it procurement-postgres psql -U postgres -d procurement
---
## Restoring the committed seed data
## Recovery / seeding
The databases are **not** in git — Docker volumes hold the data. The repo
ships recovery dumps in `backups/علی محقق/`:
The databases are **not** in git — Docker volumes hold the data, and
database dumps must never be committed (they contain user rows and signing
material). Two supported ways to bring a fresh stack up:
| File | Contents |
|---|---|
| `casdoor.dump` | Casdoor schema + console admin (`admin` / `123`), org `inventory`, app `inventory-app`, the JWT signing cert, and the single org user `ali.mohaghegh` |
| `procurement.dump` | Procurement schema + the single `users` row «علی محقق» with role `admin`; all other tables empty (categories/units re-seed at API startup) |
1. **Empty boot** — schema v2.6 auto-applies on the first boot of an empty
`postgres_data` volume; then run the seeders:
⚠️ This **replaces all data** in the `procurement` and `casdoor` databases.
On a running stack:
```powershell
docker compose stop api casdoor
docker cp "backups/علی محقق/procurement.dump" procurement-postgres:/tmp/
docker cp "backups/علی محقق/casdoor.dump" procurement-postgres:/tmp/
docker exec procurement-postgres pg_restore -U postgres -d procurement --clean --if-exists /tmp/procurement.dump
docker exec procurement-postgres pg_restore -U postgres -d casdoor --clean --if-exists /tmp/casdoor.dump
docker compose start casdoor api
```
Login afterwards: `ali.mohaghegh` / `hulkfuck002` (Casdoor rejects spaces
in usernames, so «ali mohaghegh» is not a valid login name).
Want demo data instead? Two seeders are still available (they create
different users — see the Login table above):
1. **App seeder** (realistic demo dataset; keeps the Casdoor org as-is):
```powershell
# Casdoor org + app + users (prints/generates credentials — do not commit them)
docker compose run --rm -e CASDOOR_BASE=http://casdoor:8000 api python tools/seed_casdoor.py
# Demo dataset (optional):
docker exec procurement-api sh -c "cd /srv/backend && python -m tools.seed"
```
2. **Casdoor seeder** (creates org `inventory` + app + users razaei /
mousavi / mohaghegh / tester — run once after a fresh volume):
2. **Restore your own dump** — create dumps on the server (kept server-side,
e.g. `/srv/erp/backups/`), never in git:
```powershell
python backend/tools/seed_casdoor.py # from the host, or from the api container
docker exec procurement-postgres pg_dump -U postgres -d procurement -Fc > /srv/erp/backups/procurement-$(date +%F).dump
docker exec procurement-postgres pg_dump -U postgres -d casdoor -Fc > /srv/erp/backups/casdoor-$(date +%F).dump
```
To create a fresh dump of your own current state: stop `api` + `casdoor`,
`docker exec procurement-postgres pg_dump -U postgres -d procurement -Fc`
(+ the `casdoor` DB) into `backups/`, then start them again.
Restore with `pg_restore --clean --if-exists` (stop `api` + `casdoor`
first — this **replaces all data**). The scheduled backup sidecar
(see `docs/OPERATIONS.md`) automates this.
## Common operations
@@ -317,10 +302,11 @@ docker compose down -v # ⚠️ full reset — wipes DB + MinIO vol
## Known behavior
- Authentication is Casdoor-based (JWT verified via JWKS): `/auth`, `/users`,
`/inventory` and `/pm` are protected (401 without a token, 403 without a role).
The procurement CRUD endpoints (quotes, suppliers, materials, …) are still
open — dev placeholder, do not expose to the public internet.
- Authentication is Casdoor-based (JWT verified via JWKS) and **required on
every API route** except `/health`, `/health/ready` and `/auth/login`
(401 without a token, 403 without a role). The former dev-header fallback
(`X-User-Role` / `X-User-Name-B64`) is disabled unless
`PROCUREMENT_DEV_LOGIN_ENABLED=true` — never enable it in production.
- Roles live in the app's `users` table (Casdoor is identity-only): `pm`,
`procurement_manager`, `admin`. Role changes are **admin-only** (RBAC);
the procurement manager can only assign projects to project managers.