forked from jappel/leistungsbilanz-ts
Document authenticated deployment
This commit is contained in:
parent
847f4e9493
commit
3670f02ccd
9 changed files with 97 additions and 20 deletions
7
.env.example
Normal file
7
.env.example
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Nur beim ersten Start mit leerer Benutzertabelle erforderlich.
|
||||||
|
AUTH_BOOTSTRAP_USERNAME=
|
||||||
|
AUTH_BOOTSTRAP_PASSWORT=
|
||||||
|
AUTH_SESSION_HOURS=12
|
||||||
|
AUTH_SESSION_MAX_DAYS=7
|
||||||
|
# Hinter HTTPS true, bei direktem HTTP-Zugriff im LAN false.
|
||||||
|
COOKIE_SECURE=false
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,6 +1,9 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
.next/
|
.next/
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
# Streuprodukt von tsc; drizzle-kit liest drizzle.config.ts.
|
# Streuprodukt von tsc; drizzle-kit liest drizzle.config.ts.
|
||||||
/drizzle.config.js
|
/drizzle.config.js
|
||||||
data/*.db
|
data/*.db
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,10 @@ It must support circuits, device rows, project devices, drag-and-drop restructur
|
||||||
`src/db/repositories/user.repository.ts`. Every `/api/*` route requires a
|
`src/db/repositories/user.repository.ts`. Every `/api/*` route requires a
|
||||||
signed-in user (`src/server/index.ts`); `/api/users/*` additionally
|
signed-in user (`src/server/index.ts`); `/api/users/*` additionally
|
||||||
requires `admin`. Frontend gate: `AuthProvider` + `LoginGate`
|
requires `admin`. Frontend gate: `AuthProvider` + `LoginGate`
|
||||||
(`src/app/layout.tsx`). See `docs/authentication.md` for the full picture.
|
(`src/app/layout.tsx`). Every unsafe API request also requires the explicit
|
||||||
|
`X-Leistungsbilanz-Request: 1` application header, including over plain HTTP.
|
||||||
|
Account settings are available to every signed-in user; user administration
|
||||||
|
remains admin-only. See `docs/authentication.md` for the full picture.
|
||||||
|
|
||||||
The supported runtime model is Circuit-First. The former Consumer UI, API,
|
The supported runtime model is Circuit-First. The former Consumer UI, API,
|
||||||
tables and upgrade tooling are removed; do not reintroduce them.
|
tables and upgrade tooling are removed; do not reintroduce them.
|
||||||
|
|
|
||||||
21
README.md
21
README.md
|
|
@ -7,7 +7,9 @@ Gerätezeilen und wiederverwendbare Projektgeräte fachlich getrennt behandelt.
|
||||||
Das Projekt befindet sich in aktiver Entwicklung. Für SQLite stehen ein
|
Das Projekt befindet sich in aktiver Entwicklung. Für SQLite stehen ein
|
||||||
Docker-Server-Stack mit gebautem Anwendungscode und ein separater
|
Docker-Server-Stack mit gebautem Anwendungscode und ein separater
|
||||||
Entwicklungsstack bereit. Ein vollständig unterstütztes Produktionsdeployment
|
Entwicklungsstack bereit. Ein vollständig unterstütztes Produktionsdeployment
|
||||||
und Mehrbenutzerbetrieb fehlen weiterhin. Revit-CSV-Konfiguration, Vorschau
|
für mehrere gleichzeitig arbeitende Prozesse und ein vollständig unterstütztes
|
||||||
|
Produktionsdeployment fehlen weiterhin. Anmeldung und zwei feste Rollen sind
|
||||||
|
vorhanden. Revit-CSV-Konfiguration, Vorschau
|
||||||
und bestätigter Erstimport sind implementiert; die Editoroberfläche zur
|
und bestätigter Erstimport sind implementiert; die Editoroberfläche zur
|
||||||
Objektzuordnung, Folgeimporte und Rückexport stehen noch aus. Die weitere
|
Objektzuordnung, Folgeimporte und Rückexport stehen noch aus. Die weitere
|
||||||
Revit-Arbeit ist zurückgestellt und erfolgt später über den CSV-Export/-Import
|
Revit-Arbeit ist zurückgestellt und erfolgt später über den CSV-Export/-Import
|
||||||
|
|
@ -60,6 +62,8 @@ Voraussetzungen:
|
||||||
```powershell
|
```powershell
|
||||||
git clone <repository-url>
|
git clone <repository-url>
|
||||||
Set-Location leistungsbilanz-ts
|
Set-Location leistungsbilanz-ts
|
||||||
|
$env:AUTH_BOOTSTRAP_USERNAME = "admin"
|
||||||
|
$env:AUTH_BOOTSTRAP_PASSWORT = "<eigenes Startpasswort mit mindestens 12 Zeichen>"
|
||||||
docker compose up --build --detach
|
docker compose up --build --detach
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -68,9 +72,15 @@ Danach:
|
||||||
- Frontend: <http://localhost:3001>
|
- Frontend: <http://localhost:3001>
|
||||||
- API-Healthcheck: <http://localhost:3001/health> (über den Next.js-Rewrite)
|
- API-Healthcheck: <http://localhost:3001/health> (über den Next.js-Rewrite)
|
||||||
|
|
||||||
Die Express-API ist nicht mehr auf dem Host veröffentlicht. Sie hat keine
|
Beim ersten Start erzeugen diese beiden Variablen genau einen Administrator,
|
||||||
Authentifizierung und ist nur innerhalb des Compose-Netzes erreichbar; der
|
solange die Benutzertabelle noch leer ist. Danach werden sie ignoriert. Für
|
||||||
Zugriff läuft über den Rewrite im Web-Dienst.
|
einen dauerhaften Serverbetrieb gehören die Werte in eine nicht versionierte
|
||||||
|
`.env` neben `compose.yaml`; Details stehen unter
|
||||||
|
[Anmeldung und Benutzerverwaltung](docs/authentication.md).
|
||||||
|
|
||||||
|
Die Express-API ist nicht auf dem Host veröffentlicht. Sämtliche fachlichen
|
||||||
|
API-Routen verlangen eine gültige Anmeldung; der Zugriff läuft über den
|
||||||
|
Rewrite im Web-Dienst.
|
||||||
|
|
||||||
Der API-Container führt ausstehende Migrationen und die Schemaprüfung beim Start
|
Der API-Container führt ausstehende Migrationen und die Schemaprüfung beim Start
|
||||||
automatisch aus. Die SQLite-Datei liegt auf dem Host unter
|
automatisch aus. Die SQLite-Datei liegt auf dem Host unter
|
||||||
|
|
@ -116,6 +126,8 @@ Voraussetzungen:
|
||||||
npm ci
|
npm ci
|
||||||
npm run db:migrate
|
npm run db:migrate
|
||||||
npm run db:verify:circuit-schema
|
npm run db:verify:circuit-schema
|
||||||
|
$env:AUTH_BOOTSTRAP_USERNAME = "admin"
|
||||||
|
$env:AUTH_BOOTSTRAP_PASSWORT = "<eigenes Startpasswort mit mindestens 12 Zeichen>"
|
||||||
npm run dev:api
|
npm run dev:api
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -162,6 +174,7 @@ vor der Baseline werden nicht unterstützt.
|
||||||
- [Gebäudestruktur und Raumflächen](docs/building-structure.md)
|
- [Gebäudestruktur und Raumflächen](docs/building-structure.md)
|
||||||
- [Grobe Leistungsbilanz](docs/estimate-balance.md)
|
- [Grobe Leistungsbilanz](docs/estimate-balance.md)
|
||||||
- [Leitungsberechnung](docs/cable-sizing-module.md)
|
- [Leitungsberechnung](docs/cable-sizing-module.md)
|
||||||
|
- [Anmeldung und Benutzerverwaltung](docs/authentication.md)
|
||||||
- [Entwicklungs- und Contributor-Workflow](docs/development-workflow.md)
|
- [Entwicklungs- und Contributor-Workflow](docs/development-workflow.md)
|
||||||
- [Bekannte Einschränkungen](docs/circuit-list-editor-known-limitations.md)
|
- [Bekannte Einschränkungen](docs/circuit-list-editor-known-limitations.md)
|
||||||
- [Roadmap](docs/spec/07-implementation-phases-todo.md)
|
- [Roadmap](docs/spec/07-implementation-phases-todo.md)
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ services:
|
||||||
AUTH_BOOTSTRAP_USERNAME: "${AUTH_BOOTSTRAP_USERNAME:-}"
|
AUTH_BOOTSTRAP_USERNAME: "${AUTH_BOOTSTRAP_USERNAME:-}"
|
||||||
AUTH_BOOTSTRAP_PASSWORT: "${AUTH_BOOTSTRAP_PASSWORT:-}"
|
AUTH_BOOTSTRAP_PASSWORT: "${AUTH_BOOTSTRAP_PASSWORT:-}"
|
||||||
AUTH_SESSION_HOURS: "${AUTH_SESSION_HOURS:-12}"
|
AUTH_SESSION_HOURS: "${AUTH_SESSION_HOURS:-12}"
|
||||||
|
AUTH_SESSION_MAX_DAYS: "${AUTH_SESSION_MAX_DAYS:-7}"
|
||||||
|
COOKIE_SECURE: "${COOKIE_SECURE:-false}"
|
||||||
# Nur auf dem Loopback: die API darf nicht aus dem LAN erreichbar sein.
|
# Nur auf dem Loopback: die API darf nicht aus dem LAN erreichbar sein.
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:3000:3000"
|
- "127.0.0.1:3000:3000"
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ services:
|
||||||
LOG_LEVEL: "${LOG_LEVEL:-info}"
|
LOG_LEVEL: "${LOG_LEVEL:-info}"
|
||||||
# AUTH_BOOTSTRAP_* legen nur den allerersten Admin an (solange die
|
# AUTH_BOOTSTRAP_* legen nur den allerersten Admin an (solange die
|
||||||
# users-Tabelle leer ist) und gehoeren in eine lokale, nicht versionierte
|
# users-Tabelle leer ist) und gehoeren in eine lokale, nicht versionierte
|
||||||
# .env - siehe docs/AUTH.md.
|
# .env - siehe docs/authentication.md.
|
||||||
AUTH_BOOTSTRAP_USERNAME: "${AUTH_BOOTSTRAP_USERNAME:-}"
|
AUTH_BOOTSTRAP_USERNAME: "${AUTH_BOOTSTRAP_USERNAME:-}"
|
||||||
AUTH_BOOTSTRAP_PASSWORT: "${AUTH_BOOTSTRAP_PASSWORT:-}"
|
AUTH_BOOTSTRAP_PASSWORT: "${AUTH_BOOTSTRAP_PASSWORT:-}"
|
||||||
AUTH_SESSION_HOURS: "${AUTH_SESSION_HOURS:-12}"
|
AUTH_SESSION_HOURS: "${AUTH_SESSION_HOURS:-12}"
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,9 @@ Zugang wird von einem Administrator über **Config → Benutzer** angelegt.
|
||||||
|
|
||||||
Es gibt genau zwei Rollen (`src/shared/constants/user-roles.ts`):
|
Es gibt genau zwei Rollen (`src/shared/constants/user-roles.ts`):
|
||||||
|
|
||||||
- **admin** – wie `user`, zusätzlich Zugriff auf den Config-Bereich
|
- **admin** – wie `user`, zusätzlich Zugriff auf **Config → Benutzer**
|
||||||
(Benutzerverwaltung, eigenes Konto)
|
|
||||||
- **user** – volle Nutzung der Leistungsbilanz (Projekte, Stromkreislisten,
|
- **user** – volle Nutzung der Leistungsbilanz (Projekte, Stromkreislisten,
|
||||||
Grobbilanz, …), kein Zugriff auf Config
|
Grobbilanz, …) und Zugriff auf das eigene Konto, keine Benutzerverwaltung
|
||||||
|
|
||||||
Es gibt (bewusst, siehe unten) keine konfigurierbare Rechte-Matrix und keine
|
Es gibt (bewusst, siehe unten) keine konfigurierbare Rechte-Matrix und keine
|
||||||
Verknüpfung mit einer Mitarbeiter-/Personen-Stammdatentabelle.
|
Verknüpfung mit einer Mitarbeiter-/Personen-Stammdatentabelle.
|
||||||
|
|
@ -44,6 +43,8 @@ Verknüpfung mit einer Mitarbeiter-/Personen-Stammdatentabelle.
|
||||||
- **Frontend:** `AuthProvider` lädt `/api/auth/me`; `LoginGate`
|
- **Frontend:** `AuthProvider` lädt `/api/auth/me`; `LoginGate`
|
||||||
(`src/app/layout.tsx`) zeigt Login- bzw. Passwortformular, solange nicht
|
(`src/app/layout.tsx`) zeigt Login- bzw. Passwortformular, solange nicht
|
||||||
angemeldet bzw. `mustChangePassword` gesetzt ist, sonst die App.
|
angemeldet bzw. `mustChangePassword` gesetzt ist, sonst die App.
|
||||||
|
**Konto** ist für jede angemeldete Person erreichbar; **Benutzer** nur für
|
||||||
|
Administratoren.
|
||||||
|
|
||||||
## Schutzmechanismen
|
## Schutzmechanismen
|
||||||
|
|
||||||
|
|
@ -52,12 +53,12 @@ Verknüpfung mit einer Mitarbeiter-/Personen-Stammdatentabelle.
|
||||||
| Passwort-Raten (online) | `LoginThrottle`: 5 Fehlversuche je Anmeldename in 15 min → HTTP 429 bis das Fenster abläuft. Schlüssel ist der Name, nicht die IP (alle Anfragen kommen über den Next-Rewrite mit derselben Quell-IP). |
|
| Passwort-Raten (online) | `LoginThrottle`: 5 Fehlversuche je Anmeldename in 15 min → HTTP 429 bis das Fenster abläuft. Schlüssel ist der Name, nicht die IP (alle Anfragen kommen über den Next-Rewrite mit derselben Quell-IP). |
|
||||||
| Passwort-Raten (offline, DB-Diebstahl) | scrypt (N=16384, r=8, p=1), eigenes Salt je Hash |
|
| Passwort-Raten (offline, DB-Diebstahl) | scrypt (N=16384, r=8, p=1), eigenes Salt je Hash |
|
||||||
| Nutzernamen ausspähen (Timing) | Unbekannte Namen werden gegen einen festen Dummy-Hash geprüft – immer genau ein scrypt-Lauf; einheitliche Fehlermeldung |
|
| Nutzernamen ausspähen (Timing) | Unbekannte Namen werden gegen einen festen Dummy-Hash geprüft – immer genau ein scrypt-Lauf; einheitliche Fehlermeldung |
|
||||||
| CSRF | SameSite=Lax **plus** `requireSameOriginForWrites`: schreibende Anfragen nur mit `Sec-Fetch-Site: same-origin`. Nötig, weil andere Dienste auf demselben Host (andere Ports) als „same-site“ gelten |
|
| CSRF | SameSite=Lax **plus** `requireSameOriginForWrites`: jeder Schreibzugriff braucht `X-Leistungsbilanz-Request: 1`; fremde HTML-Formulare können den Header nicht setzen und ein fremder Fetch scheitert an der notwendigen CORS-Preflight-Anfrage. Ein vorhandener `Sec-Fetch-Site`-Header wird zusätzlich geprüft. |
|
||||||
| CSRF über reines HTTP | Browser senden `Sec-Fetch-Site` nur an HTTPS-Adressen. Beim Zugriff per `http://…` im LAN greift deshalb nur SameSite=Lax (schützt gegen fremde Domains, nicht gegen andere Ports desselben Hosts). |
|
| CSRF über reines HTTP | Der eigene Anwendungsheader gilt auch dann, wenn Browser im HTTP-LAN keinen `Sec-Fetch-Site`-Header senden. Skripte und manuelle API-Aufrufe müssen ihn bei `POST`, `PUT`, `PATCH` und `DELETE` ausdrücklich mitsenden. |
|
||||||
| Session-Fixation | Neue Session-ID bei jedem Login und nach jedem Passwortwechsel |
|
| Session-Fixation | Neue Session-ID bei jedem Login und nach jedem Passwortwechsel |
|
||||||
| Gestohlenes Cookie | httpOnly; gleitende Laufzeit (`AUTH_SESSION_HOURS`, 12 h) **und** harte Obergrenze (`AUTH_SESSION_MAX_DAYS`, 7 Tage) |
|
| Gestohlenes Cookie | httpOnly; gleitende Laufzeit (`AUTH_SESSION_HOURS`, 12 h) **und** harte Obergrenze (`AUTH_SESSION_MAX_DAYS`, 7 Tage) |
|
||||||
| Kompromittiertes Konto | Passwort-Reset und Deaktivieren beenden sofort alle Sessions des Kontos; Passwortwechsel beendet alle anderen |
|
| Kompromittiertes Konto | Passwort-Reset und Deaktivieren beenden sofort alle Sessions des Kontos; Passwortwechsel beendet alle anderen |
|
||||||
| Aussperren | Der letzte aktive Admin kann weder herabgestuft noch deaktiviert werden; eigener Zugang ist in der Liste gesperrt |
|
| Aussperren | Der letzte aktive Admin kann weder herabgestuft noch deaktiviert werden; Prüfung und Änderung laufen atomar in einer SQLite-Transaktion. Der eigene Zugang ist in der Liste gesperrt. |
|
||||||
| Rechteausweitung / Mass Assignment | Rolle kommt aus der DB, nie aus der Anfrage; zod-Schemas mit `.strict()` |
|
| Rechteausweitung / Mass Assignment | Rolle kommt aus der DB, nie aus der Anfrage; zod-Schemas mit `.strict()` |
|
||||||
| DoS über riesige Passwörter | Längengrenzen (Name 100, Passwort 200 Zeichen) vor scrypt |
|
| DoS über riesige Passwörter | Längengrenzen (Name 100, Passwort 200 Zeichen) vor scrypt |
|
||||||
| Look-alike-Namen | Anmeldename nur `a-z 0-9 . _ @ -`, klein geschrieben |
|
| Look-alike-Namen | Anmeldename nur `a-z 0-9 . _ @ -`, klein geschrieben |
|
||||||
|
|
@ -77,6 +78,18 @@ ignoriert; jeder weitere Zugang entsteht über Config → Benutzer. Die Werte
|
||||||
gehören in eine lokale, nicht versionierte `.env` auf dem Deploy-Host, nicht
|
gehören in eine lokale, nicht versionierte `.env` auf dem Deploy-Host, nicht
|
||||||
in `compose.yaml` selbst.
|
in `compose.yaml` selbst.
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
AUTH_BOOTSTRAP_USERNAME=admin
|
||||||
|
AUTH_BOOTSTRAP_PASSWORT=<eigenes Startpasswort mit mindestens 12 Zeichen>
|
||||||
|
AUTH_SESSION_HOURS=12
|
||||||
|
AUTH_SESSION_MAX_DAYS=7
|
||||||
|
COOKIE_SECURE=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Bei Zugriff über HTTPS muss `COOKIE_SECURE=true` gesetzt werden. Bei direktem
|
||||||
|
HTTP-Zugriff im LAN bleibt es `false`, da der Browser das Session-Cookie sonst
|
||||||
|
nicht speichert.
|
||||||
|
|
||||||
## Bewusst nicht umgesetzt (Stand jetzt)
|
## Bewusst nicht umgesetzt (Stand jetzt)
|
||||||
|
|
||||||
- Konfigurierbare Rechte-Matrix statt fixer `admin`/`user`-Rollen
|
- Konfigurierbare Rechte-Matrix statt fixer `admin`/`user`-Rollen
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,15 @@ mit Hot Reload. Das Frontend leitet `/api/*` über `API_INTERNAL_URL` an die API
|
||||||
weiter; beim gebauten Stand wird das Ziel zur Build-Zeit festgelegt. Die
|
weiter; beim gebauten Stand wird das Ziel zur Build-Zeit festgelegt. Die
|
||||||
Datenbank liegt über einen Host-Mount außerhalb des Containers.
|
Datenbank liegt über einen Host-Mount außerhalb des Containers.
|
||||||
|
|
||||||
|
Vor allen fachlichen `/api/*`-Routen liegt die Cookie-basierte Anmeldung aus
|
||||||
|
`src/server/auth`. Nur Login, Status und Logout sind ohne Session erreichbar.
|
||||||
|
Schreibzugriffe verlangen zusätzlich den Anwendungsheader
|
||||||
|
`X-Leistungsbilanz-Request: 1`, damit der Schutz auch beim HTTP-Zugriff im LAN
|
||||||
|
ohne Fetch-Metadata-Header greift. `/api/users/*` ist Administratoren
|
||||||
|
vorbehalten. Konten sind globale Betriebsdaten und gehören nicht zu
|
||||||
|
Projektkommandos, Snapshots oder Projekttransfers. Sessions liegen im Speicher
|
||||||
|
des einzelnen API-Prozesses und enden bei dessen Neustart.
|
||||||
|
|
||||||
Neue IDs für Stromkreise, Gerätezeilen, Gruppen und Verteilerkomponenten erzeugt
|
Neue IDs für Stromkreise, Gerätezeilen, Gruppen und Verteilerkomponenten erzeugt
|
||||||
der Editor über `src/frontend/utils/client-id.ts` (`createClientId`). Die
|
der Editor über `src/frontend/utils/client-id.ts` (`createClientId`). Die
|
||||||
Hilfsfunktion nutzt `crypto.randomUUID()`, sofern verfügbar, andernfalls
|
Hilfsfunktion nutzt `crypto.randomUUID()`, sofern verfügbar, andernfalls
|
||||||
|
|
@ -57,6 +66,11 @@ vor dem Command und bleiben über Undo/Redo unverändert.
|
||||||
- `src/frontend/components/circuit-grid-*.ts` – reine Grid-Projektion,
|
- `src/frontend/components/circuit-grid-*.ts` – reine Grid-Projektion,
|
||||||
Zellbesitz, Einfügen und Sicherheitsregeln
|
Zellbesitz, Einfügen und Sicherheitsregeln
|
||||||
- `src/frontend/utils/api.ts` – typisierte Frontend-API-Aufrufe
|
- `src/frontend/utils/api.ts` – typisierte Frontend-API-Aufrufe
|
||||||
|
- `src/frontend/components/AuthProvider.tsx` und `LoginGate.tsx` – Anmeldung
|
||||||
|
und erzwungener Wechsel eines Startpassworts
|
||||||
|
- `src/server/auth/` – Passwort-Hashing, Sessions und Zugriffsschutz
|
||||||
|
- `src/domain/services/user.service.ts` und `src/domain/ports/user.store.ts` –
|
||||||
|
kontobezogene Regeln und schmale Persistenzgrenze
|
||||||
- `src/server/index.ts` und `src/server/routes/` – API-Komposition
|
- `src/server/index.ts` und `src/server/routes/` – API-Komposition
|
||||||
- `src/domain/services/` – fachliche Command- und Synchronisierungsregeln
|
- `src/domain/services/` – fachliche Command- und Synchronisierungsregeln
|
||||||
- `src/server/composition/` – Verdrahtung fachlicher Services mit konkreten
|
- `src/server/composition/` – Verdrahtung fachlicher Services mit konkreten
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ und macOS keine inotify-Events durchreichen; das kostet dauerhaft CPU, auch
|
||||||
ohne Benutzeraktivität. Dieser Stack gehört deshalb nur auf einen
|
ohne Benutzeraktivität. Dieser Stack gehört deshalb nur auf einen
|
||||||
Entwicklungsrechner.
|
Entwicklungsrechner.
|
||||||
|
|
||||||
Beide Stacks enthalten weder TLS, Authentifizierung, einen vorgeschalteten
|
Beide Stacks enthalten Anmeldung mit festen Rollen, aber weder TLS, einen
|
||||||
Reverse Proxy noch ein zentral betriebenes Datenbanksystem. Die oben genannten
|
vorgeschalteten Reverse Proxy noch ein zentral betriebenes Datenbanksystem.
|
||||||
Container-Schutzmaßnahmen ersetzen diese Funktionen nicht. Der Stack darf
|
Passwörter und Session-Cookies werden bei reinem HTTP unverschlüsselt
|
||||||
deshalb nicht öffentlich erreichbar gemacht werden.
|
übertragen. Der Stack darf deshalb nicht öffentlich erreichbar gemacht werden.
|
||||||
|
|
||||||
## Topologie
|
## Topologie
|
||||||
|
|
||||||
|
|
@ -29,9 +29,10 @@ deshalb nicht öffentlich erreichbar gemacht werden.
|
||||||
| Express API | 3000 | nicht veröffentlicht (Server) / `127.0.0.1:3000` (Dev) | `GET /health` | `./data:/app/data` |
|
| Express API | 3000 | nicht veröffentlicht (Server) / `127.0.0.1:3000` (Dev) | `GET /health` | `./data:/app/data` |
|
||||||
| SQLite | Datei | – | Integritäts-/FK-Prüfung via Backup und Skript | `data/leistungsbilanz.db` |
|
| SQLite | Datei | – | Integritäts-/FK-Prüfung via Backup und Skript | `data/leistungsbilanz.db` |
|
||||||
|
|
||||||
Die Express-API wird im Server-Stack nicht mehr auf dem Host veröffentlicht
|
Die Express-API wird im Server-Stack nicht auf dem Host veröffentlicht
|
||||||
(`expose` statt `ports`). Sie hat keine Authentifizierung; jeder, der sie
|
(`expose` statt `ports`). Alle fachlichen API-Routen verlangen eine aktive
|
||||||
erreicht, kann jedes Projekt lesen und ändern. Erreichbar ist sie nur aus dem
|
Session; Schreibzugriffe zusätzlich den Anwendungsheader
|
||||||
|
`X-Leistungsbilanz-Request: 1`. Erreichbar ist die API nur aus dem
|
||||||
Compose-Netz, praktisch also über den Next.js-Rewrite. Im Entwicklungs-Stack
|
Compose-Netz, praktisch also über den Next.js-Rewrite. Im Entwicklungs-Stack
|
||||||
hängen beide Ports am Loopback und sind nicht aus dem LAN erreichbar.
|
hängen beide Ports am Loopback und sind nicht aus dem LAN erreichbar.
|
||||||
|
|
||||||
|
|
@ -72,6 +73,26 @@ Verwendete Umgebungsvariablen:
|
||||||
JSON-Loggers (`error`, `warn`, `info`, `verbose`, `debug`), Standard `info`.
|
JSON-Loggers (`error`, `warn`, `info`, `verbose`, `debug`), Standard `info`.
|
||||||
Setzbar über eine `.env`-Datei neben `compose.yaml` oder
|
Setzbar über eine `.env`-Datei neben `compose.yaml` oder
|
||||||
`LOG_LEVEL=verbose docker compose up`.
|
`LOG_LEVEL=verbose docker compose up`.
|
||||||
|
- `AUTH_BOOTSTRAP_USERNAME` und `AUTH_BOOTSTRAP_PASSWORT` – erzeugen nur bei
|
||||||
|
leerer Benutzertabelle den ersten Administrator; das Startpasswort hat
|
||||||
|
mindestens 12 Zeichen.
|
||||||
|
- `AUTH_SESSION_HOURS` – Inaktivitätsgrenze einer Session, Standard 12 Stunden.
|
||||||
|
- `AUTH_SESSION_MAX_DAYS` – absolute Session-Laufzeit, Standard 7 Tage.
|
||||||
|
- `COOKIE_SECURE` – hinter HTTPS `true`; beim direkten HTTP-Zugriff im LAN
|
||||||
|
`false`, da Browser das Cookie sonst nicht speichern.
|
||||||
|
|
||||||
|
Vor dem ersten Start müssen mindestens die Bootstrap-Werte in einer lokalen,
|
||||||
|
nicht versionierten `.env` neben `compose.yaml` gesetzt werden:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
AUTH_BOOTSTRAP_USERNAME=admin
|
||||||
|
AUTH_BOOTSTRAP_PASSWORT=<eigenes Startpasswort mit mindestens 12 Zeichen>
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach dem ersten erfolgreichen Start werden diese Werte ignoriert, solange
|
||||||
|
mindestens ein Benutzer existiert. Weitere Zugänge legt ein Administrator in
|
||||||
|
**Config → Benutzer** an. Details stehen in
|
||||||
|
[Anmeldung und Benutzerverwaltung](authentication.md).
|
||||||
|
|
||||||
Beim API-Start laufen zuerst die Migrationen und die Schemaprüfung
|
Beim API-Start laufen zuerst die Migrationen und die Schemaprüfung
|
||||||
(`scripts/run-migrations.js` und `scripts/db-verify-circuit-schema.js`, im
|
(`scripts/run-migrations.js` und `scripts/db-verify-circuit-schema.js`, im
|
||||||
|
|
@ -137,7 +158,8 @@ bleibt `sh` der Vordergrundprozess, und `SIGTERM` erreicht Node nie.
|
||||||
Vor einer produktiven Installation werden mindestens benötigt:
|
Vor einer produktiven Installation werden mindestens benötigt:
|
||||||
|
|
||||||
- TLS-Termination und Reverse Proxy
|
- TLS-Termination und Reverse Proxy
|
||||||
- Authentifizierung, Autorisierung und Benutzer-/Rollenmodell
|
- zentraler Identitätsanbieter oder eine feinere Rechte-Matrix, falls die zwei
|
||||||
|
festen Rollen später nicht ausreichen
|
||||||
- definierte Secrets- und Konfigurationsverwaltung
|
- definierte Secrets- und Konfigurationsverwaltung
|
||||||
- persistenter, gesicherter Datenbankbetrieb
|
- persistenter, gesicherter Datenbankbetrieb
|
||||||
- kontrollierter einmaliger Migrationsschritt vor dem API-Rollout
|
- kontrollierter einmaliger Migrationsschritt vor dem API-Rollout
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue