Update README.md with enhanced configuration details and security measures
- Clarified SMTP sending options, including BCC support and email validation for distribution lists. - Updated instructions for setting environment variables in `.env`, emphasizing security requirements for production. - Added notes on password policies, rate limiting, and user role management in the admin section. - Enhanced Docker/Compose setup with security hardening features and clarified the automatic build process.
This commit is contained in:
133
README.md
133
README.md
@@ -21,13 +21,15 @@ Produktionsnahes internes Tool zur Erstellung von Thomas-Krenn.AG Newslettern au
|
|||||||
- Plain Text (für Outlook)
|
- Plain Text (für Outlook)
|
||||||
- Raw HTML
|
- Raw HTML
|
||||||
- Optionaler SMTP-Versand:
|
- Optionaler SMTP-Versand:
|
||||||
- UI-konfigurierbar
|
- UI-konfigurierbar (nur für Admins)
|
||||||
- Verteilerlisten
|
- Verteilerlisten mit E-Mail-Validierung
|
||||||
- flexibler Zeitplan (täglich / wöchentlich an bestimmten Wochentagen / monatlich an einem Tag) über einen Hintergrund-Scheduler
|
- Versand per **BCC** (Empfänger nicht im sichtbaren `To:`-Header)
|
||||||
|
- flexibler Zeitplan (täglich / wöchentlich / monatlich) über einen Hintergrund-Scheduler
|
||||||
- eigene Inhalts-Vorgaben für den geplanten Versand (Zeitraum, Artikel-Auswahl, Kategorie)
|
- eigene Inhalts-Vorgaben für den geplanten Versand (Zeitraum, Artikel-Auswahl, Kategorie)
|
||||||
- manueller Versand nur per Button – „Generieren“ verschickt nie automatisch
|
- **„Generieren“ verschickt nie** – manueller Versand nur per Button „Newsletter jetzt senden“
|
||||||
- Versandprotokoll
|
- automatischer Versand nur mit **zwei** aktiven Häkchen: Zeitplan + Bestätigung „ohne manuelle Freigabe“
|
||||||
- Docker/Compose Betrieb, inkl. automatischem Image-Build & Push in die Gitea Container Registry (per Versions-Tag)
|
- Versandprotokoll (nur für Editor/Admin sichtbar)
|
||||||
|
- Docker/Compose Betrieb mit Container-Hardening, inkl. automatischem Image-Build & Push in die Gitea Container Registry (per Versions-Tag)
|
||||||
|
|
||||||
## Start mit Docker
|
## Start mit Docker
|
||||||
|
|
||||||
@@ -37,11 +39,17 @@ Produktionsnahes internes Tool zur Erstellung von Thomas-Krenn.AG Newslettern au
|
|||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Wichtige Werte in `.env` setzen:
|
2. Wichtige Werte in `.env` setzen (siehe auch [Konfiguration](#konfiguration-env)):
|
||||||
- `HOST_PORT` (z. B. `8080`, falls `8000` bereits belegt ist)
|
|
||||||
- `SECRET_KEY`
|
| Variable | Lokal (Entwicklung) | Produktion |
|
||||||
- `ADMIN_BOOTSTRAP_EMAIL`
|
|----------|---------------------|------------|
|
||||||
- `ADMIN_BOOTSTRAP_PASSWORD`
|
| `ENVIRONMENT` | `development` | `production` |
|
||||||
|
| `HOST_PORT` | z. B. `8080` | nach Bedarf |
|
||||||
|
| `SECRET_KEY` | beliebig lang | mind. 32 zufällige Zeichen |
|
||||||
|
| `ALLOWED_HOSTS` | `localhost,127.0.0.1` | konkreter Hostname |
|
||||||
|
| `COOKIE_SECURE` | `false` | `true` (mit TLS) |
|
||||||
|
| `ADMIN_BOOTSTRAP_EMAIL` | Admin-E-Mail | Admin-E-Mail |
|
||||||
|
| `ADMIN_BOOTSTRAP_PASSWORD` | Bootstrap-Passwort | starkes Passwort |
|
||||||
|
|
||||||
3. Start:
|
3. Start:
|
||||||
|
|
||||||
@@ -52,6 +60,30 @@ Produktionsnahes internes Tool zur Erstellung von Thomas-Krenn.AG Newslettern au
|
|||||||
4. Zugriff:
|
4. Zugriff:
|
||||||
- [http://localhost:8080/login](http://localhost:8080/login) (oder dein `HOST_PORT`)
|
- [http://localhost:8080/login](http://localhost:8080/login) (oder dein `HOST_PORT`)
|
||||||
|
|
||||||
|
> **Hinweis:** Mit `ENVIRONMENT=production` prüft die App beim Start strikt die Konfiguration. Fehlen starke Secrets oder sind unsichere Defaults gesetzt, startet der Container nicht.
|
||||||
|
|
||||||
|
## Konfiguration (`.env`)
|
||||||
|
|
||||||
|
Vollständige Vorlage: `.env.example`
|
||||||
|
|
||||||
|
| Variable | Beschreibung |
|
||||||
|
|----------|--------------|
|
||||||
|
| `ENVIRONMENT` | `development` = lokale Entwicklung ohne harte Checks; `production` = erzwingt sichere Einstellungen |
|
||||||
|
| `SECRET_KEY` | JWT-Signierung und Verschlüsselung von SMTP-Passwörtern in der DB |
|
||||||
|
| `ALGORITHM` | JWT-Algorithmus (Standard: `HS256`) |
|
||||||
|
| `ACCESS_TOKEN_EXPIRE_MINUTES` | Gültigkeit der Login-Session in Minuten (Standard: `120`) |
|
||||||
|
| `JWT_ISSUER` / `JWT_AUDIENCE` | JWT-Claims zur Token-Validierung |
|
||||||
|
| `HOST_PORT` | Host-Port für Docker Compose (nur Compose, nicht App-intern) |
|
||||||
|
| `DATABASE_URL` | SQLite (Standard) oder PostgreSQL |
|
||||||
|
| `WIKI_API_URL` | MediaWiki-API-Endpunkt |
|
||||||
|
| `ALLOWED_HOSTS` | Kommagetrennte erlaubte Host-Header |
|
||||||
|
| `COOKIE_SECURE` | `Secure`-Flag für Session- und CSRF-Cookies (Pflicht `true` in Produktion) |
|
||||||
|
| `HSTS_MAX_AGE` | HSTS-Header-Dauer in Sekunden (nur bei HTTPS) |
|
||||||
|
| `ADMIN_BOOTSTRAP_EMAIL` / `ADMIN_BOOTSTRAP_PASSWORD` | Erster Admin-Account beim Start |
|
||||||
|
| `ADMIN_BOOTSTRAP_RESET` | Admin auf `.env`-Werte zurücksetzen (nur Entwicklung/Notfall, in Produktion verboten) |
|
||||||
|
| `EDITOR_TIP_MAX_LENGTH` | Max. Zeichen für Redaktionsnotiz (Standard: `10000`) |
|
||||||
|
| `HIGHLIGHTS_MAX_LENGTH` | Max. Zeichen für Top-Highlights (Standard: `5000`) |
|
||||||
|
|
||||||
## Container-Image (Gitea Packages)
|
## Container-Image (Gitea Packages)
|
||||||
|
|
||||||
Fertige Images werden automatisch in die Gitea Container Registry veröffentlicht:
|
Fertige Images werden automatisch in die Gitea Container Registry veröffentlicht:
|
||||||
@@ -71,6 +103,9 @@ docker run -d --name tk-newsletter-admin \
|
|||||||
--env-file .env \
|
--env-file .env \
|
||||||
-p 8080:8000 \
|
-p 8080:8000 \
|
||||||
-v newsletter_data:/app/data \
|
-v newsletter_data:/app/data \
|
||||||
|
--read-only \
|
||||||
|
--tmpfs /tmp \
|
||||||
|
--security-opt no-new-privileges:true \
|
||||||
git.hexahost.dev/smueller/tk-wiki-newsletter:latest
|
git.hexahost.dev/smueller/tk-wiki-newsletter:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -83,11 +118,14 @@ services:
|
|||||||
# build: . # <- nicht mehr nötig, wenn das fertige Image genutzt wird
|
# build: . # <- nicht mehr nötig, wenn das fertige Image genutzt wird
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Die mitgelieferte `docker-compose.yml` setzt bereits `read_only`, `no-new-privileges` und ein `tmpfs` für `/tmp`. Persistente Daten liegen im Volume `newsletter_data` unter `/app/data`.
|
||||||
|
|
||||||
### Automatischer Build (CI/CD)
|
### Automatischer Build (CI/CD)
|
||||||
|
|
||||||
Der Workflow `.gitea/workflows/docker-build.yml` baut und pusht das Image über Gitea Actions.
|
Der Workflow `.gitea/workflows/docker-build.yml` baut und pusht das Image über Gitea Actions.
|
||||||
|
|
||||||
- **Auslöser**: nur beim Setzen eines Versions-Tags `v*` (kein Build bei normalen Pushes auf `dev`/`main`).
|
- **Auslöser**: nur beim Setzen eines Versions-Tags `v*` (kein Build bei normalen Pushes auf `dev`/`main`).
|
||||||
|
- **Vor dem Build**: `pip-audit` prüft die Python-Abhängigkeiten aus `requirements.txt`.
|
||||||
- **Release-Ablauf** (Build bei Merge `dev` → `main` mit Version):
|
- **Release-Ablauf** (Build bei Merge `dev` → `main` mit Version):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -106,25 +144,74 @@ Der Workflow `.gitea/workflows/docker-build.yml` baut und pusht das Image über
|
|||||||
## Admin-Login & Passwort zurücksetzen
|
## Admin-Login & Passwort zurücksetzen
|
||||||
|
|
||||||
- Das Login erfolgt über **E-Mail + Passwort** (kein separater Benutzername). Beim ersten Start wird der Admin aus `ADMIN_BOOTSTRAP_EMAIL` / `ADMIN_BOOTSTRAP_PASSWORD` angelegt.
|
- Das Login erfolgt über **E-Mail + Passwort** (kein separater Benutzername). Beim ersten Start wird der Admin aus `ADMIN_BOOTSTRAP_EMAIL` / `ADMIN_BOOTSTRAP_PASSWORD` angelegt.
|
||||||
|
- **Passwortpolitik:** mindestens 10 Zeichen, mindestens ein Großbuchstabe, ein Kleinbuchstabe und eine Ziffer.
|
||||||
|
- **Rate-Limiting:** nach 5 fehlgeschlagenen Anmeldeversuchen pro IP/E-Mail innerhalb von 5 Minuten wird der Login temporär blockiert.
|
||||||
- **Wichtig:** Existiert der Admin bereits (persistente DB im Volume `newsletter_data`), wird eine spätere Passwort-Änderung in der `.env` normalerweise **nicht** übernommen.
|
- **Wichtig:** Existiert der Admin bereits (persistente DB im Volume `newsletter_data`), wird eine spätere Passwort-Änderung in der `.env` normalerweise **nicht** übernommen.
|
||||||
- Um Passwort/Rolle/Status auf die `.env`-Werte zurückzusetzen: `ADMIN_BOOTSTRAP_RESET=true` setzen und Container neu starten:
|
- Um Passwort/Rolle/Status auf die `.env`-Werte zurückzusetzen (**nur Entwicklung**): `ADMIN_BOOTSTRAP_RESET=true` setzen und Container neu starten:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d --force-recreate
|
docker compose up -d --force-recreate
|
||||||
```
|
```
|
||||||
|
|
||||||
Nach erfolgreichem Login `ADMIN_BOOTSTRAP_RESET=false` setzen, damit UI-Passwortänderungen nicht bei jedem Neustart überschrieben werden.
|
Nach erfolgreichem Login `ADMIN_BOOTSTRAP_RESET=false` setzen. In `ENVIRONMENT=production` ist `ADMIN_BOOTSTRAP_RESET=true` nicht erlaubt – die App startet dann nicht.
|
||||||
|
|
||||||
## Sicherheits-Hinweise für Produktion
|
- **Passwort ändern** (Profil) oder **Abmelden** beendet alle aktiven Sessions des Benutzers.
|
||||||
|
|
||||||
- Reverse Proxy (z. B. Nginx/Traefik) mit TLS vor den Container setzen.
|
|
||||||
- `SECRET_KEY` lang und zufällig setzen.
|
|
||||||
- Bootstrap-Admin-Passwort nach erstem Login ändern.
|
|
||||||
- Netzwerkzugriff auf interne IPs/Netze beschränken.
|
|
||||||
- Regelmäßige Backups des `data` Volumes.
|
|
||||||
|
|
||||||
## Rollenmodell
|
## Rollenmodell
|
||||||
|
|
||||||
- `reader`: darf Ergebnisse sehen
|
| Rolle | Rechte |
|
||||||
- `editor`: darf Newsletter generieren und sofort versenden
|
|-------|--------|
|
||||||
- `admin`: zusätzlich Benutzerverwaltung und SMTP-Konfiguration
|
| `reader` | Newsletter-Vorschau und Artikellisten ansehen |
|
||||||
|
| `editor` | Newsletter generieren, exportieren und manuell versenden; Versandprotokoll und Empfängerliste einsehen |
|
||||||
|
| `admin` | Zusätzlich Benutzerverwaltung (anlegen, Rolle ändern, aktivieren/deaktivieren) und SMTP-Konfiguration |
|
||||||
|
|
||||||
|
Admins können in der Benutzerverwaltung (`/admin/users`):
|
||||||
|
|
||||||
|
- neue Benutzer mit Rolle anlegen
|
||||||
|
- die Rolle bestehender Benutzer ändern (beendet deren Sessions)
|
||||||
|
- Benutzer aktivieren oder deaktivieren (beendet deren Sessions)
|
||||||
|
|
||||||
|
Das eigene Konto kann weder deaktiviert noch die eigene Rolle geändert werden.
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
### Eingebaute Schutzmaßnahmen
|
||||||
|
|
||||||
|
- **bcrypt** für Passwort-Hashes
|
||||||
|
- **CSRF** Double-Submit-Cookie auf allen POST-Formularen
|
||||||
|
- **HttpOnly** + **SameSite=Strict** Session-Cookies; `Secure` bei HTTPS (`COOKIE_SECURE=true`)
|
||||||
|
- **JWT** mit `iss`, `aud`, Ablaufzeit und Session-Version (`session_version`)
|
||||||
|
- Session-Invalidierung bei Logout, Passwortwechsel, Rollen- oder Statusänderung
|
||||||
|
- **SMTP-Passwort** verschlüsselt in der Datenbank (Fernet), nicht im HTML-Formular
|
||||||
|
- **Security-Header:** CSP, `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, HSTS (bei HTTPS), `Cache-Control: no-store` auf geschützten Seiten
|
||||||
|
- **OpenAPI** (`/docs`, `/redoc`) in Produktion deaktiviert
|
||||||
|
- Abhängigkeiten in `requirements.txt` versioniert; CI führt `pip-audit` aus
|
||||||
|
|
||||||
|
### Produktions-Checkliste
|
||||||
|
|
||||||
|
Bei `ENVIRONMENT=production` erzwingt die App beim Start:
|
||||||
|
|
||||||
|
- `SECRET_KEY` mit mindestens 32 Zeichen (keine bekannten Defaults)
|
||||||
|
- starkes `ADMIN_BOOTSTRAP_PASSWORD`
|
||||||
|
- `ADMIN_BOOTSTRAP_RESET=false`
|
||||||
|
- `ALLOWED_HOSTS` mit konkreten Hostnamen (nicht `*`)
|
||||||
|
- `COOKIE_SECURE=true`
|
||||||
|
|
||||||
|
Zusätzlich empfohlen:
|
||||||
|
|
||||||
|
- Reverse Proxy (z. B. Nginx/Traefik) mit TLS vor den Container; Proxy muss `X-Forwarded-Proto: https` setzen
|
||||||
|
- Bootstrap-Admin-Passwort nach erstem Login im Profil ändern
|
||||||
|
- Netzwerkzugriff auf interne IPs/Netze beschränken
|
||||||
|
- Regelmäßige Backups des `newsletter_data` Volumes (SQLite-Datei unverschlüsselt)
|
||||||
|
|
||||||
|
### Beispiel `.env` für Produktion
|
||||||
|
|
||||||
|
```env
|
||||||
|
ENVIRONMENT=production
|
||||||
|
SECRET_KEY=<mind. 32 zufällige Zeichen>
|
||||||
|
ALLOWED_HOSTS=newsletter.intern.firma.de
|
||||||
|
COOKIE_SECURE=true
|
||||||
|
ADMIN_BOOTSTRAP_EMAIL=admin@firma.de
|
||||||
|
ADMIN_BOOTSTRAP_PASSWORD=<starkes Passwort>
|
||||||
|
ADMIN_BOOTSTRAP_RESET=false
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user