chore: migrate project documentation into repo (18 files)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# 06a – Auth-Audit (Security & Data-Engineering)
|
||||
|
||||
**Projekt:** CRM System v1.0
|
||||
**Datum:** 2026-06-04
|
||||
**Auditor:** Security Data Engineer (Phase 6)
|
||||
**Repository:** `Leopoldadmin/crm-system`, Branch `main`
|
||||
**Scope:** JWT-Implementation, Password-Hashing, Auth-Endpoints, CORS/CSP-Header, Token-Rotation, Secrets in Git-Verlauf
|
||||
|
||||
---
|
||||
|
||||
## Findings
|
||||
|
||||
| ID | Severity | Kategorie | Beschreibung | Empfehlung |
|
||||
|----|----------|-----------|-------------|------------|
|
||||
| AUTH-01 | **PASS** | JWT-Algorithmus | HS256 mit `python-jose[cryptography]==3.3.0` gemäß Architecture-Decision Section 13.1. `decode_access_token()` validiert Signatur und Ablauf korrekt über `jwt.decode()` mit explizitem Algorithmus-Parameter. | Beibehalten. Für v1.2 RS256 evaluieren (bessere Rotation, kein Shared-Secret). |
|
||||
| AUTH-02 | **PASS** | Secret-Länge | `AUTH_SECRET` wird in `config.py` mit `Field(..., min_length=32)` validiert. Ein benutzerdefinierter Validator `validate_auth_secret()` lehnt Platzhalter wie `replace-me`, `changeme` und den Literal `secret` ab. Hard-Fail bei fehlendem oder zu kurzem Secret (kein Fallback). | Keine Änderung nötig. Erfüllt NFR-2 und Architecture R-5. |
|
||||
| AUTH-03 | **PASS** | Token-Expiry | 24h über `JWT_EXPIRY_HOURS` in `config.py` konfigurierbar. `create_access_token()` setzt `exp`-Claim korrekt via `datetime.now(UTC)` + `timedelta`. `decode_access_token()` fängt `JWTError` ab (deckt auch Expiry). | Kurzfristigeres Expiry (z.B. 2h) + Refresh-Token in v1.1 für höhere Sicherheit. |
|
||||
| AUTH-04 | **PASS** | Token-Validation | `get_current_user` in `deps.py` nutzt `decode_access_token()`, prüft `sub`-Claim und User-Existenz (inkl. `deleted_at IS NULL`). 401-Response mit `token_expired_or_invalid` für konsistente Client-Behandlung. | Validierung ist robust. Zusätzlicher Check auf `iat`-Claim könnte Replay-Angriffe erschweren (optional). |
|
||||
| AUTH-05 | **PASS** | Password-Hashing | bcrypt via `passlib[bcrypt]==1.7.4` mit `bcrypt==4.0.1`. `pwd_context` mit `deprecated="auto"`. `BCRYPT_ROUNDS=12` konfigurierbar. `hash_password()` und `verify_password()` korrekt implementiert. | Keine Änderung nötig. bcrypt 4.0.1 ist aktuell und sicher. |
|
||||
| AUTH-06 | **PASS** | Kein Default-Admin | Bootstrap-Registrierung via `POST /api/v1/auth/register` nur bei leerer `users`-Tabelle. Nach erstem User → 403 (`BootstrapAlreadyCompleted`). Kein `admin/admin`-Fallback. | Erfüllt Architecture R-3. |
|
||||
| AUTH-07 | **PASS** | Register-Endpoint | `POST /api/v1/auth/register` validiert via `UserRegisterRequest`: `email: EmailStr`, `password: str(min_length=8, max_length=128)`, `name: str(min_length=1, max_length=255)`. 409 bei doppelter Email (generisch), 201 bei Erfolg. | Validiert korrekt. Rate-Limiting fehlt noch (v1.1, siehe Architecture 13.4). |
|
||||
| AUTH-08 | **PASS** | Login-Endpoint | `POST /api/v1/auth/login` (OAuth2Form) + `/login/json` (JSON). 401 mit generischer Meldung `"Invalid email or password"` – leakt nicht, ob Email existiert. `WWW-Authenticate: Bearer` Header gesetzt. | Erfüllt FR-1.2 Akzeptanzkriterien. |
|
||||
| AUTH-09 | **PASS** | Logout-Endpoint | `POST /api/v1/auth/logout` validiert Token (Dependency `get_current_user`), aber keine serverseitige Blacklist. Client-seitiger Token-Discard dokumentiert. | Für v1 akzeptabel. Server-seitige Blacklist erst in v1.1. |
|
||||
| AUTH-10 | **PASS** | /users/me-Endpoint | `GET /api/v1/users/me` via `get_current_user` geschützt. 401 ohne Token, 401 mit expired Token (`token_expired_or_invalid`), `password_hash` nie im Response. | Erfüllt FR-1.6 Akzeptanzkriterien AC#7-#9. |
|
||||
| AUTH-11 | **PASS** | CORS-Whitelist | `CORS_ORIGINS` aus Env-Var (Komma-separiert), Default `http://localhost:5500,http://localhost:8000`. Kein `*`. `settings.cors_origins_list` parsed korrekt. | Erfüllt Architecture R-4. |
|
||||
| AUTH-12 | **INFO** | CSP-Header | In `main.py` `security_headers_middleware` gesetzt. Dev: `script-src 'self' 'unsafe-inline' ...` (für Alpine.js). Prod: nur `script-src 'self' ...` (ohne unsafe-inline) – Alpine.js-Inline-Skripte würden blockiert. X-Content-Type-Options, X-Frame-Options, HSTS (Prod) gesetzt. | **Vor Produktion:** Nonce-basierte CSP für Alpine.js implementieren (v1.1 ToDo). Aktuelle Prod-CSP würde Frontend blockieren. |
|
||||
| AUTH-13 | **INFO** | Refresh-Token-Rotation | `/api/v1/auth/refresh` existiert, re-signed aber nur mit gleichem Secret – keine echte Rotation. Rotation ist für v1.1 geplant und im Code-Kommentar dokumentiert. | Kein Sicherheitsrisiko für v1, da Token-Expiry 24h beträgt. Für v1.1: Refresh-Token mit separatem Secret + Rotation. |
|
||||
| AUTH-14 | **WARN** | Secrets im Git-Verlauf | `git log -p` zeigt Passwörter in Test-Dateien (`test_auth.py`, `test_smoke.py`, `conftest_helper.py`), z.B. `"password": "Test1234!"`, `"password": "SuperSecret123!"`. Dies sind Test-Credentials ohne Produktionsrelevanz. | Kein kritisches Risiko, aber Good-Practice: Test-Passwörter aus Git-Verlauf entfernen (via `git filter-branch` oder `git rebase`). Kein Blocker für Phase 7. |
|
||||
|
||||
---
|
||||
|
||||
## Summary: **PASS** ✅
|
||||
|
||||
Das Auth-System ist sicher und erfüllt alle Anforderungen aus 01-requirements.md (FR-1.x, NFR-2) und 02-architecture.md (13.1–13.5). JWT-Implementation, Passwort-Hashing und Endpoint-Access-Control sind korrekt implementiert. Keine kritischen Findings.
|
||||
|
||||
**Einzig offener Punkt:** CSP-Header muss vor Produktion auf Nonce umgestellt werden (AUTH-12), da die aktuelle Prod-CSP Alpine.js-Inline-Skripte blockieren würde. Dies ist ein geplanter v1.1-Task.
|
||||
|
||||
---
|
||||
|
||||
## Empfehlungen für Phase 7 (Quality-Reviewer)
|
||||
|
||||
1. **CSP-Nonce-Migration vor Deployment** – Prod-CSP aktuell ohne `unsafe-inline` → Frontend funktioniert nicht. Muss vor Production-Release behoben werden.
|
||||
2. **Password-Hashing-Verifikation** – Sicherstellen, dass `bcrypt==4.0.1` korrekt gepinnt ist (4.1+ bricht passlib).
|
||||
3. **Token-Expiry-Test automatisieren** – `test_auth.py:test_expired_token_returns_401` prüft explizit `token_expired_or_invalid`, aber Integration-Test könnte race-condition bei `iat`/`exp` haben.
|
||||
4. **Rate-Limiting-Akzeptanz prüfen** – Ohne LoginAttempt-Tabelle (v1.1) ist der Login-Endpoint ungebremst. In Phase 7 dokumentieren, ob dies für v1-Go-Live akzeptabel ist.
|
||||
@@ -0,0 +1,41 @@
|
||||
# 06b – Input-Validation-Audit (Security & Data-Engineering)
|
||||
|
||||
**Projekt:** CRM System v1.0
|
||||
**Datum:** 2026-06-04
|
||||
**Auditor:** Security Data Engineer (Phase 6)
|
||||
**Repository:** `Leopoldadmin/crm-system`, Branch `main`
|
||||
**Scope:** Pydantic-Schemas, SQL-Injection-Prävention, XSS-Schutz, File-Upload-Security
|
||||
|
||||
---
|
||||
|
||||
## Findings
|
||||
|
||||
| ID | Severity | Kategorie | Beschreibung | Empfehlung |
|
||||
|----|----------|-----------|-------------|------------|
|
||||
| IN-01 | **PASS** | Pydantic-Schemas (Auth) | `UserRegisterRequest`: `email: EmailStr`, `password: str(min_length=8, max_length=128)`, `name: str(min_length=1, max_length=255)`, `role: UserRole` (Enum). `UserLoginRequest`: `email: EmailStr`, `password: str(min_length=1, max_length=128)`. Keine Raw-String-Felder ohne Constraints. | Erfüllt NFR-2 (Input-Validation via Pydantic v2). |
|
||||
| IN-02 | **PASS** | Pydantic-Schemas (CRUD) | Alle CRUD-Endpoints nutzen typisierte Pydantic-Modelle mit `Field(min_length=...)`, `EmailStr`, `HttpUrl`, `Decimal`. Accounts/Contacts/Deals/Activities haben eigene Create-/Update-/Response-Schemas. Polymorphe Felder (`parent_type`, `parent_id`) validiert über `Literal['account', 'contact', 'deal']`. | Keine SQL-Injection über untypisierte Inputs möglich. |
|
||||
| IN-03 | **PASS** | SQL-Injection-Prävention | Kein `f"SELECT..."` oder `f"INSERT..."` in der gesamten Codebase gefunden (globale Suche negativ). Alle DB-Queries nutzen SQLAlchemy ORM mit `session.execute(select(Model).where(...))` – parametrisierte Queries. Raw-SQL nur in `health.py` (`text("SELECT 1")`) und Alembic-Migrationen. | Erfüllt NFR-2 (SQL-Injection-Schutz). Raw-SQL in Migrationen ist akzeptabel (statisch). |
|
||||
| IN-04 | **PASS** | XSS-Prävention – Backend | CSP-Header blockiert script-src ohne `'unsafe-inline'` in Prod, X-Content-Type-Options: nosniff, X-Frame-Options: DENY. Alle API-Responses sind JSON (kein HTML-Rendering serverseitig). | Starke XSS-Mitigation auf Backend-Seite. |
|
||||
| IN-05 | **PASS** | XSS-Prävention – Frontend | Kein `innerHTML` in der gesamten Frontend-Codebase gefunden (globale Suche negativ). Alpine.js nutzt `x-text` (escapet automatisch) und `x-model` (bindet an DOM-Properties, kein HTML-Injection-Vektor). JWT im localStorage ist via CSP abgesichert. | Frontend-Patterns sind XSS-resistent. |
|
||||
| IN-06 | **PASS** | eval/exec | Keine `eval`- oder `exec()`-Aufrufe im gesamten Python-Code gefunden. | Erwartet für Secure-Codebase. |
|
||||
| IN-07 | **PASS** | Type-Hints (mypy strict) | Architecture 13.7 fordert `async def` überall + SQLAlchemy `AsyncSession`. Code-Analyse bestätigt: alle Router und Services sind async. `pyproject.toml` enthält mypy-Konfiguration mit `strict = true`. | Typisierung reduziert Laufzeitfehler und Injection-Vektoren. |
|
||||
| IN-08 | **INFO** | File-Upload-Security | Kein File-Upload-Endpoint in v1 (gemäß Requirements OP-1: Avatar-Upload = Nein). `avatar_url` ist ein `HttpUrl`-Feld – User geben externe URL an, kein Binary-Upload. | Kein Risiko in v1. Für v1.1: File-Upload-Endpoint mit MIME-Type-Validierung und Size-Limit implementieren. |
|
||||
| IN-09 | **INFO** | Rate-Limiting | Kein Rate-Limiting auf Auth-Endpoints in v1 (gemäß Architecture 13.4: LoginAttempt-Tabelle in v1.1). `pyproject.toml` listet keine SlowAPI oder ähnliche Middleware. | Für v1-Demo akzeptabel. Vor Production: Rate-Limiting auf Login/Register (z.B. 5 Versuche / 15 min) implementieren. |
|
||||
| IN-10 | **INFO** | Password-Constraints | `UserRegisterRequest` akzeptiert `password` mit `min_length=8`. Keine Komplexitätsanforderung (Groß/Klein/Zahl/Sonderzeichen) in Pydantic oder explizit in Requirements definiert. | Optional: `regex`-Constraint auf Password-Feld (`(?=.*[A-Z])(?=.*[0-9])`) für bessere Passwort-Hygiene in v1.1. |
|
||||
|
||||
---
|
||||
|
||||
## Summary: **PASS** ✅
|
||||
|
||||
Die Input-Validierung ist durchgängig und sicher implementiert. Alle Request-Bodies werden über stark typisierte Pydantic-v2-Schemas validiert, SQL-Queries sind ausschließlich parametrisiert, und das Frontend enthält keine XSS-Vektoren (kein `innerHTML`, kein `eval`).
|
||||
|
||||
**Offene Punkte:** Rate-Limiting und File-Upload-Security sind für v1 nicht relevant (siehe Architecture-Decisions), Password-Komplexität ist minimal (nur Länge ≥ 8).
|
||||
|
||||
---
|
||||
|
||||
## Empfehlungen für Phase 7 (Quality-Reviewer)
|
||||
|
||||
1. **Pydantic-Schema-Coverage prüfen** – Sicherstellen, dass ALLE 51 API-Endpoints ein dediziertes Request-Schema haben und keine `dict`-Payloads verarbeiten.
|
||||
2. **Password-Komplexität evaluieren** – Sollte v1 bereits `regex`-Validierung für Groß/Klein/Zahl erzwingen? Entscheidung in Requirements dokumentieren.
|
||||
3. **Rate-Limiting-Readiness** – Prüfen, ob der Code bereits auf Middleware-basiertes Rate-Limiting vorbereitet ist (z.B. via `slowapi` in `pyproject.toml`).
|
||||
4. **File-Upload-Design für v1.1** – Validierungs-Patterns für Binary-Uploads (MIME-Check, Size-Limit, Virenscan-Integration) im Vorfeld designen.
|
||||
@@ -0,0 +1,41 @@
|
||||
# 06c – Secrets-Handling-Audit (Security & Data-Engineering)
|
||||
|
||||
**Projekt:** CRM System v1.0
|
||||
**Datum:** 2026-06-04
|
||||
**Auditor:** Security Data Engineer (Phase 6)
|
||||
**Repository:** `Leopoldadmin/crm-system`, Branch `main`
|
||||
**Scope:** .env/.gitignore-Prüfung, AUTH_SECRET-Handling, DATABASE_URL-Credentials, Coolify-ENV-Vars, Hardcoded-Fallback-Kontrolle
|
||||
|
||||
---
|
||||
|
||||
## Findings
|
||||
|
||||
| ID | Severity | Kategorie | Beschreibung | Empfehlung |
|
||||
|----|----------|-----------|-------------|------------|
|
||||
| SEC-01 | **PASS** | .gitignore-Regel | `.gitignore` enthält `.env`, `.env.*` mit Ausnahmen `!.env.example` und `!.env.docker.example`. Diese Konfiguration ist korrekt: Die `.env`-Datei (mit realen Dev-Secrets) ist nicht im Git-Tree (`git ls-files --error-unmatch .env` → "did not match"). | Keine Änderung nötig. Sicherstellen, dass `.env.docker` (falls jemals erstellt) ebenfalls exkludiert ist (aktuell durch `.env.*` abgedeckt). |
|
||||
| SEC-02 | **PASS** | .env.example (Template) | Enthält `AUTH_SECRET=replace-me-with-a-secure-random-string-at-least-32-chars-long` mit klarer Anweisung zum Generieren. Kein echter Secret-Wert committed. | Akzeptabel als Entwickler-Dokumentation. |
|
||||
| SEC-03 | **PASS** | .env (Dev) | Lokale `.env` enthält `AUTH_SECRET=test-secret-with-at-least-thirty-two-characters-for-development` – 62 Zeichen, kein Platzhalter, aber ein Dev-Secret. Datei ist nicht committed. | Nur für lokale Entwicklung akzeptabel. Sollte vor einem versehentlichen Commit durch `.gitignore` geschützt sein – ist es. |
|
||||
| SEC-04 | **PASS** | AUTH_SECRET-Validierung | `config.py` `Settings.AUTH_SECRET: str = Field(..., min_length=32)` – zwingendes Feld ohne Default, Hard-Fail bei fehlendem Wert. Zusätzlicher `field_validator` lehnt Platzhalter (`replace-me`, `changeme`, `secret`) ab. Erfüllt Architecture R-5 (NO JWT secret fallback). | Robust implementiert. Kein Angriffspunkt. |
|
||||
| SEC-05 | **PASS** | AUTH_SECRET-Generierung | `COOLIFY_SETUP.md` dokumentiert Secret-Generierung mit `python -c "import secrets; print(secrets.token_urlsafe(48))"`. Empfohlene Länge: 48 Zeichen (Base64-encoded, ~384 Bit Entropie). | Entspricht Best Practices. Empfehlung für Rotation: `cron`-Job, der AUTH_SECRET rotiert und alle Tokens invalidiert (v1.1). |
|
||||
| SEC-06 | **PASS** | Hardcoded-Fallback-Kontrolle | Kein `AUTH_SECRET = "dev-secret"`-Fallback im Code. `Field(...)` (Ellipsis) in Pydantic bedeutet: Wert MUSS gesetzt sein, sonst ValidationError beim App-Start. `get_settings()` via `@lru_cache` cached die Settings-Instanz – jede Änderung an Env-Vars erfordert App-Neustart. | Erfüllt Architecture R-5. Kein Soft-Fallback vorhanden. |
|
||||
| SEC-07 | **PASS** | DATABASE_URL | Enthält Credentials im Format `postgresql+asyncpg://crm_user:<PW>@<host>:5432/crm_db`. Wird via Coolify Env-Var `DATABASE_URL` gesetzt (nicht im Repo). Dev-Default `sqlite+aiosqlite:///./dev.db` enthält keine Credentials. In `COOLIFY_SETUP.md` dokumentiert: Passwort mit `secrets.token_urlsafe(24)` generieren. | Production-Passwort muss stark sein (≥ 16 Zeichen). Aktuelles Dev-Setup (SQLite) ist credential-frei. |
|
||||
| SEC-08 | **PASS** | Coolify-ENV-Vars | Alle erforderlichen Secrets in `COOLIFY_SETUP.md` dokumentiert: `DATABASE_URL`, `AUTH_SECRET`, `CORS_ORIGINS`, `ENVIRONMENT`, `LOG_LEVEL`, `BCRYPT_ROUNDS`, `JWT_ALGORITHM`, `JWT_EXPIRY_HOURS`. Bulk-Update-Skript via Coolify-API bereitgestellt. | Vollständig dokumentiert. Keine weiteren Secrets nötig. |
|
||||
| SEC-09 | **INFO** | Secrets-Rotation | Keine Rotation von `AUTH_SECRET` oder `DATABASE_URL`-Passwort in v1 vorgesehen. Token-Invalidierung bei Secret-Rotation würde alle aktiven Sessions beenden – kein Mechanismus dafür implementiert. | Für v1 akzeptabel. In v1.1: Secret-Rotation mit invalidierungs-Mechanismus planen. |
|
||||
| SEC-10 | **WARN** | dev.db im Repository | Datei `dev.db` (286.720 Bytes) existiert im Working-Tree, ist aber durch `.gitignore`-Regel `*.db` geschützt. `git ls-files` zeigt sie nicht an. Dennoch: SQLite-DB mit potenziell echten Testdaten sollte nie im Repo liegen. | Aktuell geschützt durch .gitignore. Vor Release: `dev.db` aus Working-Tree löschen und sicherstellen, dass `.git/info/exclude` oder `.gitignore` alle DB-Dateien blockiert. |
|
||||
|
||||
---
|
||||
|
||||
## Summary: **PASS** ✅
|
||||
|
||||
Das Secrets-Handling ist sicher. `.env` ist korrekt exkludiert, `AUTH_SECRET` hat Hard-Fail-Validierung ohne Fallback, und alle Coolify-ENV-Vars sind dokumentiert. Keine kritischen Findings.
|
||||
|
||||
**Offene Punkte:** Secrets-Rotation ist für v1 nicht implementiert, und die lokale `dev.db` sollte vor Release aus dem Working-Tree entfernt werden.
|
||||
|
||||
---
|
||||
|
||||
## Empfehlungen für Phase 7 (Quality-Reviewer)
|
||||
|
||||
1. **dev.db-Bereinigung** – Vor Release: `dev.db` aus Working-Tree löschen und `.gitignore` auf DB-Dateien prüfen.
|
||||
2. **Secrets-Rotation-Planung** – Dokumentieren, wie AUTH_SECRET und DB-Password in Coolify rotiert werden (v1.1 ToDo).
|
||||
3. **Environment-Parity-Check** – Sicherstellen, dass alle in `.env.example` dokumentierten Keys auch in Coolify gesetzt sind (und umgekehrt).
|
||||
4. **Secrets-Audit in CI/CD** – Optional: `detect-secrets` oder `git-secrets` Pre-Commit-Hook für automatische Secrets-Erkennung.
|
||||
@@ -0,0 +1,43 @@
|
||||
# 06d – Backup-/Recovery-Audit (Security & Data-Engineering)
|
||||
|
||||
**Projekt:** CRM System v1.0
|
||||
**Datum:** 2026-06-04
|
||||
**Auditor:** Security Data Engineer (Phase 6)
|
||||
**Repository:** `Leopoldadmin/crm-system`, Branch `main`
|
||||
**Scope:** DB-Backup-Strategie, Coolify-Backup-Konfiguration, Wiederherstellungs-Test, Disaster-Recovery-Plan, RTO/RPO
|
||||
|
||||
---
|
||||
|
||||
## Findings
|
||||
|
||||
| ID | Severity | Kategorie | Beschreibung | Empfehlung |
|
||||
|----|----------|-----------|-------------|------------|
|
||||
| BKP-01 | **WARN** | Backup-Strategie | NFR-6 fordert tägliches PostgreSQL-Dump-Backup via Coolify mit 7 Tagen Retention. In `COOLIFY_SETUP.md` und `docker-compose.yml` ist keine Backup-Konfiguration dokumentiert. Coolify bietet native Database-Backups (S3-kompatibler Storage), aber diese sind weder eingerichtet noch dokumentiert. | **Vor Production:** Coolify-Database-Backup-Schedule konfigurieren (täglicher Dump, 7d Retention, Storage-Backend definieren). Backup-Konfiguration als Code dokumentieren (z.B. Coolify-API-Script in `/scripts/backup-setup.sh`). |
|
||||
| BKP-02 | **WARN** | Restore-Runbook | NFR-6 fordert ein Restore-Runbook unter `docs/runbook-backup-restore.md`. Diese Datei existiert nicht im Repository (`ls docs/runbook-backup-restore.md` → nicht vorhanden). | **Vor Production:** Runbook erstellen mit Schritt-für-Schritt-Anleitung: 1) Coolify-Backup auswählen, 2) PostgreSQL-Restore-Kommando, 3) App-Neustart, 4) Smoke-Test. |
|
||||
| BKP-03 | **WARN** | Wiederherstellungs-Test | Kein dokumentierter Backup-Restore-Test durchgeführt. Ohne Test kann nicht garantiert werden, dass Backups im Ernstfall wiederherstellbar sind. | **Vor Production:** Restore-Drill durchführen: Backup aus Coolify herunterladen, in lokales PostgreSQL einspielen, App starten, Healthcheck + Login-Smoke-Test. Ergebnis dokumentieren. |
|
||||
| BKP-04 | **INFO** | RTO 4h / RPO 24h | Requirements (NFR-6) definieren Recovery-Time-Objective ≤ 4h und Recovery-Point-Objective ≤ 24h. Diese Ziele sind mit täglichem Coolify-Backup + manuellem Restore erreichbar, aber nicht formal verifiziert. | RTO/RPO in Runbook verankern und im Restore-Drill messen. Coolify-Restore-Zeit für PostgreSQL 16 (Alpine) typischerweise < 30 min – innerhalb 4h. |
|
||||
| BKP-05 | **INFO** | Backup-Dokumentation | `COOLIFY_SETUP.md` erwähnt keine Backups. `README.md` und `docker-compose.yml` enthalten keine Backup-Referenzen. Einziger Anhaltspunkt: NFR-6 in `01-requirements.md`. | Backup-Dokumentation in Coolify-Setup integrieren oder als separates `docs/backup-strategy.md` führen. |
|
||||
| BKP-06 | **PASS** | Datenbank-Volume | `docker-compose.yml` definiert benanntes Volume `pgdata` für PostgreSQL-Daten (`pgdata:/var/lib/postgresql/data`). Volumes sind persistent und können unabhängig vom Container gesichert werden. | Docker-Volume-Backup (z.B. `docker run --rm -v crm_pgdata:/data -v $(pwd):/backup alpine tar czf /backup/pgdata-backup.tar.gz -C /data .`) als Fallback für Coolify-Backup dokumentieren. |
|
||||
| BKP-07 | **PASS** | Pre-Start-Migration | `prestart.sh` führt `alembic upgrade head` aus – idempotente Migration vor jedem App-Start. Dies stellt sicher, dass ein Restore aus einem älteren Backup funktioniert, solange das DB-Schema kompatibel ist. | Alembic-Migrationen sind Forward-kompatibel. Backup-Restore + `alembic upgrade head` ist ein gültiger Recovery-Pfad. |
|
||||
| BKP-08 | **INFO** | Diskrepanz PostgreSQL vs. SQLite | Entwicklung nutzt SQLite (`dev.db`), Produktion PostgreSQL. Backups sind nur für PostgreSQL relevant, aber SQLite-DB könnte Entwicklerdaten enthalten, die gesichert werden müssen (z.B. vor Branch-Wechsel oder DB-Reset). | Entwickler-Backup-Strategie dokumentieren: `sqlite3 dev.db ".backup dev-backup-$(date +%Y%m%d).db"` oder Migration zu PostgreSQL auch in Dev. |
|
||||
|
||||
---
|
||||
|
||||
## Summary: **WARN** ⚠️
|
||||
|
||||
Die Backup-Strategie ist **nicht produktionsreif**. Während die technischen Voraussetzungen (PostgreSQL-Volume, Alembic-Migrationen, Coolify-Database-Backup-Feature) gegeben sind, fehlen die konkrete Konfiguration, das Restore-Runbook und ein verifizierter Wiederherstellungs-Test.
|
||||
|
||||
**Kritisch vor Production-Go-Live:**
|
||||
1. Coolify-Backup-Schedule konfigurieren (BKP-01)
|
||||
2. Restore-Runbook erstellen (BKP-02)
|
||||
3. Restore-Drill durchführen (BKP-03)
|
||||
|
||||
---
|
||||
|
||||
## Empfehlungen für Phase 7 (Quality-Reviewer)
|
||||
|
||||
1. **Backup-Konfiguration prüfen** – Ist der Coolify-Backup-Schedule aktiv und getestet? Existiert ein Storage-Backend (S3, SFTP, oder lokaler Pfad)?
|
||||
2. **Runbook-Review** – Runbook auf Vollständigkeit prüfen: Deckt es alle Fehlerszenarien ab (Datenbank-Korruption, versehentliches Löschen, Coolify-Ausfall)?
|
||||
3. **RTO/RPO-Messung** – Im Restore-Drill die tatsächliche Recovery-Zeit messen und mit den 4h-RTO abgleichen. Wenn nicht erreichbar: Automatisierte Restore-Prozedur implementieren.
|
||||
4. **Backup-Monitoring** – Healthcheck-Endpoint (`/health`) sollte DB-Connectivity prüfen, aber nicht Backup-Status. Optional: Coolify-Health-Webhook, der Backup-Erfolg meldet.
|
||||
5. **Release-Readiness-Entscheidung** – Ohne konfiguriertes Backup und Restore-Runbook ist das Deployment gemäß Requirements (NFR-6) nicht freigabefähig. Phase 7 muss dies als Blocker behandeln.
|
||||
Reference in New Issue
Block a user