Project-Image-Uploader/FeatureRequests/done/FEATURE_REQUEST-FrontendPublic.md

287 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
Feature Request: Public vs. Intranet UI/API by Subdomain
Datei erstellt: 22.11.2025
-->
# Feature Request: Frontend/Public API per Subdomain
## Kurzbeschreibung
Es soll unterschieden werden, welche Funktionen der App abhängig von der aufgerufenen Subdomain verfügbar sind:
- `deinprojekt.meindomain.de` (extern erreichbar): Nur Uploads und das Editieren via zugewiesenem Link (Management-UUID) sollen möglich sein. Keine Moderations-, Gruppen- oder Slideshow-Funktionen sichtbar oder nutzbar.
- `deinprojekt.lan.meindomain.de` (Intranet): Vollständige Funktionalität (Slideshow, Groupsview, Moderation, Admin-Endpunkte) sowie volle Navigation/Buttons im Frontend.
Die Anwendung läuft in Docker auf einem Ubuntu-Server, vorgeschaltet ist ein `nginx-proxy-manager`. Die Domain `*.lan.meindomain.de` ist nur intern erreichbar und besitzt ein gültiges SSL-Zertifikat für das Intranet (dns challenge letsencrypt).
## Ziele
- Sicherheit: Slideshow, Groupview und Admin-/Moderations-Funktionalität niemals über die öffentliche Subdomain erreichbar machen.
- UX: Im öffentlichen (externen) Kontext nur die Upload-Experience sichtbar und bedienbar. (die Upload Seite ist bereits so gestalltet, dass keine Menüpunkte sichtbar sind)
## Vorschlag — Technische Umsetzung (hoher Level)
1) Host-Erkennung
- Backend und Frontend erkennen die Subdomain via `Host` bzw. `X-Forwarded-Host` Header. Alternativ über eine runtime `env-config.js` (`/public/env-config.js`) die beim Request vom Backend dynamisch befüllt wird.
2) Backend: Gatekeeping-Middleware
- Neue Middleware (z.B. `middlewares/hostGate.js`) prüft `req.hostname` / `x-forwarded-host`.
- Wenn Request von öffentlicher Subdomain: schränke verfügbare API-Routen ein — nur `/api/upload` und `/api/manage/:token` (oder die minimalen Endpoints) werden zugelassen.
- Wenn Request von interner Subdomain: volle Route-Registrierung (Admin, System, Migration usw.).
- Schleifen-/Edge-Cases: Allowlist für einzelne externe Hosts (z. B. externe public-frontend-Host), sodass ein extern gehostetes UI die public-API nutzen darf.
3) Frontend: Menü- und Feature-Visibility
- Beim Laden prüft das Frontend `window.location.host` (oder die runtime `env-config.js`).
- Wenn public-host: Navigation reduziert — nur Upload, ggf. Hilfe/Impressum. Alle Buttons/Links zu Moderation/Slideshow/Gruppen ausgeblendet/gesperrt.
- Wenn internal-host: komplette Navigation und Admin-Funktionen sichtbar.
4) Reverse Proxy / nginx
- `nginx-proxy-manager` muss Host-Header weiterreichen (standard). Wichtig: `proxy_set_header Host $host;` so dass Backend den Host erkennt.
- SSL: bereits vorhanden für beide Host-Namespaces (extern + lan).
- Alternative: Public-Frontend extern hosten -> Proxy/Firewall so konfigurieren, dass nur die erlaubten API-Routen erreichbar sind (oder API-Server hinter VPN nur für `*.lan.` erreichbar).
5) CORS & Security
- Public-API: enge CORS-Regel (nur erlaubte public-frontend-origin, falls extern gehostet).
- Rate-Limiting für public Uploads stärker setzen.
- Upload-Validierung (Dateityp, Größe), Scanner/Virus-Check bedenken.
## Akzeptanzkriterien (Metrisch / Testbar)
- Auf `deinprojekt.meindomain.de` sind nur Upload und Management-by-UUID erreichbar — Aufrufe von `/api/admin/*` erhalten 403/404.
- Auf `deinprojekt.lan.meindomain.de` sind Admin- und Moderation-Endpunkte erreichbar und die Navigation zeigt alle Menüpunkte.
- Unit-/Integrationstest: Backend-Middleware hat Tests für Host-Varianten (public/internal/external-frontend)
- End-to-End: Test-Upload über public-host funktioniert, Moderation-API von dort nicht.
## Änderungsumfang (Konkrete Dateien/Orte)
- Backend
- `src/middlewares/hostGate.js` (neu) — enthält Host-Prüfung und Policy
- `src/server.js` / `src/index.js` — Routen nur registrieren oder mounten, falls Host-Policy es erlaubt; oder Middleware pro Route
- `src/middlewares/auth.js` — ggf. anpassen, um Host-Checks in Kombination mit Auth zu berücksichtigen
- Frontend
- `public/env-config.js` (runtime) oder `env-config.js` (build-time) — Flag `PUBLIC_MODE=true/false` bzw. `APP_ALLOWED_FEATURES`
- Menü-Komponenten (z. B. `Components/Pages/*`) — Feature-Visibility anhand `window.location.host` oder runtime-config
- Infrastruktur
- `docker/dev/*` nginx-proxy-manager Konfiguration prüfen: Host-Header, Zertifikate
## Sicherheits-Überlegungen
- Admin-Endpoints müssen serverseitig geblockt sein — niemals nur per Frontend-UI verstecken.
- Public Uploads: individuelle Rate-Limits, Captcha-Optionen, Virus/Malware-Scanning.
- Logging & Audit: Uploads von extern sollten besondere Logging-Flags bekommen (IP, Host, Herkunfts-Header).
## Fragen / Punkte zur Konkretisierung — und Antworten aus der Projektdokumentation
Nach Durchsicht von `README.md`, `README.dev.md`, `CHANGELOG.md` und `AUTHENTICATION.md` habe ich viele offene Punkte direkt beantwortet und die verbleibenden Entscheidungen auf das Nötigste reduziert. Unten: jeweils die Frage, was die Doku bereits festlegt, und die noch offenen Bestätigungen, die Du kurz geben solltest.
1. Domains — exakte Hosts
- Dokumentation: Platzhalter-Hosts wurden als Beispiele verwendet (z. B. `deinprojekt.meindomain.de` und `deinprojekt.lan.meindomain.de`).
- Empfehlung / Bitte bestätigen: Nenne bitte die echten Subdomains, die Dein Deployment verwenden wird. BeispielAntwort reicht: `deinprojekt.hobbyhimmel.de` und `deinprojekt.lan.hobbyhimmel.de`.
2. Host-Check vs. zusätzliche Checks
- Doku: AdminAPI ist bereits serverseitig per Admin Login geschützt. ManagementAPI nutzt UUIDToken mit RateLimits (10 req/h) und BruteForceSchutz.
- Empfehlung: Primär HostHeader (`Host` / `X-Forwarded-Host`) prüfen (einfach, zuverlässig). Zusätzlich empfehle ich für AdminAPIs die Kombination aus BearerToken + HostCheck (defense in depth). Bitte bestätigen, ob IPWhitelist gewünscht ist.
3. Externes Hosting des publicFrontends -> nicht mehr nötig
4. ManagementUUID (Editieren von extern)
- Doku: ManagementTokens sind permanent gültig bis Gruppe gelöscht; Token sind URLbasiert und Ratelimited (10 req/h). README zeigt, dass ManagementPortal für SelfService gedacht ist und kein zusätzliches network restriction vorgesehen ist.
- Schlussfolgerung: Editieren per UUID ist technisch erlaubt und im Projekt vorgesehen. Wenn Du das beibehalten willst, ist keine weitere technische Änderung nötig. Falls Du TTL für Tokens möchtest, bitte angeben.
5. AdminAPIs: Hostonly oder zusätzlich BearerToken?
- ~~Doku: Admin APIs sind bereits durch BearerToken geschützt (`ADMIN_API_KEY`).~~
- ~~Empfehlung: Behalte BearerToken als Hauptschutz und ergänze HostRestriction (Admin nur intern erreichbar) für zusätzliche Sicherheit. Bitte bestätigen.~~
6. RateLimits / Quotas für public Uploads
- Doku: Management hat 10 req/h per IP; UploadRateLimits für public uploads sind nicht konkret spezifiziert.
- Vorschlag: Default `20 uploads / IP / Stunde` für public subdomain + strengere throttling für unauthenticated bursts. Bestätige oder nenne anderes Limit.
7. Logging / Monitoring
- Doku: Es gibt umfassende Audit-Logs (`management_audit_log`, `deletion_log`).
- Empfehlung: Ergänze ein Feld/Label `source_host` oder `source_type` für public vs. internal Uploads für bessere Filterbarkeit. Bestätigen? Passt!
8. Assets / CDN
- Doku: Bilder und Previews werden lokal gespeichert; kein CDN-Flow vorhanden. Du hast klargestellt: Bilder sind intern und nur über UUIDLinks zugänglich.
- Entscheidung: Default bleibt interne Auslieferung. Externe CDN-Auslieferung ist möglich, aber muss aus Privacy/AccessControlGründen extra implementiert (signed URLs, TTL, ACLs). Keine Aktion nötig, wenn Du interne Auslieferung beibehältst.
---
Bitte bestätige die wenigen noch offenen Punkte (Hosts, publicgroupview ja/nein (siehe unten), ManagementUUID extern ja/nein (bestätigt als ja), desired ratelimit, zusätzliche Adminrestrictions, logginglabel). Ich habe die Dokumentation soweit wie möglich angepasst (siehe Änderungen weiter unten). Sobald Du diese 34 Punkte bestätigst, erstelle ich die konkreten Patches (Middleware, kleine FrontendVisibilityÄnderung, Tests, READMEErweiterung).
## Vorschlag: Minimal umsetzbare erste Iteration (MVP)
1. Implementiere `middlewares/hostGate.js` mit einfacher Host-Allowlist (`PUBLIC_HOSTS`, `INTERNAL_HOSTS` in Env).
2. Im Backend: prüfe bei jedem Request, ob Route erlaubt ist — für public-Hosts nur Upload & manage-by-uuid.
3. Im Frontend: beim Laden `window.location.host` prüfen und Navigation entsprechend reduzieren.
4. Dokumentation: `README.dev.md` (API-Abschnitt) und `frontend/MIGRATION-GUIDE.md` um Hinweise erweitern.
5. Tests: Unit-Test für Middleware + Integrationstest (Supertest) der Host-Policies.
## Nächste Schritte / Implementierungs-Checklist
- [ ] Domain-/Host-Liste bestätigen (Fragen beantworten)
- [ ] Env-Variablen definieren und in `docker/*/config/.env` ergänzen
- [ ] Backend: Middleware implementieren + Tests
- [ ] Frontend: runtime `env-config.js` + menu-visibility implementieren
- [ ] Proxy/Nginx: Konfiguration prüfen & SSL testen
- [ ] Review & Security-Check (rate-limits, CORS, logging)
- [ ] Deployment-Plan / Rollout-Notes
---
Bitte beantwort e die Fragen im Abschnitt "Fragen / Punkte zur Konkretisierung" oben. Sobald ich Deine Antworten habe, kann ich ein konkretes Implementierungs-Patch (Backend-Middleware + Frontend-Visibility) erstellen und Tests hinzufügen.
## FAQ: Trennung von Frontend und Backend — Frage des Nutzers & Antwort
**Frage:**
Wie kann ich Frontend und Backend räumlich trennen, ohne dass sicherheitsrelevante (Bilder) theoretisch extern erreichbar sind? Im Moment laufen Frontend und Backend in einer Docker Compose. Ist das sinnvoll? Wie trennt man das am Besten?
**Antwort (zusammengefasst & praxisorientiert):**
- Development: Für lokale Entwicklung ist eine gemeinsame `docker-compose`-Umgebung (Frontend + Backend) sinnvoll — HMR, einfache Debugging und Tests.
- Produktion: Trennung empfohlen. Ziel: Frontend öffentlich (statisch/CDN) betreiben, Backend streng begrenzen und Assets (Originale/Previews) niemals ohne Authorisierung öffentlich machen.
Architektur-Optionen (Kurz):
- Single-Server mit `nginx` ReverseProxy (empfohlen, einfach): `nginx` routet `/` zum statischen Frontend und `/api/*` zum Backend; Backend nicht direkt öffentlich.
- Frontend extern (CDN/Netlify) + Backend intern hinter ReverseProxy: Frontend ist skalierbar, Backend nur über Proxy erreichbar; für Bilder: presigned URLs oder BackendProxy verwenden.
- Vollständige Trennung (Backend nur im privaten Netz / VPN): Sehr sicher, aber komplexer (VPN/VPC). Admin-/Moderation nur über LAN/VPN erreichbar.
Wie Bilder sicher halten (Pattern):
- Pattern A — Backendproxied images: Bilder nur auf Backend speichern; Zugriff nur über BackendEndpunkte (prüfen ManagementUUID / Host), keine direkte öffentliche URL.
- Pattern B — Private Object Storage + presigned URLs: Nutze privaten S3/Bucket; generiere kurzlebige presigned URLs nach Auth/Zugriffsprüfung; kombiniere mit CDN (Origin Access).
- Pattern C — CDN + signed URLs für Previews: Nur Previews via CDN mit signed URLs; Originals bleiben intern oder ebenfalls presigned.
Konkrete Maßnahmen (umsetzbar sofort):
1. ReverseProxy (`nginx`) einführen: zwei vhosts (public / internal). Auf public vhost `/api/admin` und `/groups` blockieren; nur `/api/upload` und `/api/manage/:token` erlauben.
2. DockerNetzwerke: Backend in `internal_net` ohne veröffentlichte Ports; `reverse-proxy` hat öffentliche Ports und verbindet zu `backend` intern.
3. HostGate Middleware (Express): `req.isPublic` setzen via `Host`/`X-Forwarded-Host`, serverseitig Routen (Admin/Groups) für public blocken — defense in depth.
4. CORS & RateLimit: CORS auf erlaubte Origins, strengere RateLimits für public Uploads (z. B. 20 Uploads/IP/Stunde) und Captcha prüfen.
5. Logging: AuditLogs erweitern (z. B. `source_host`) um public vs internal Uploads zu unterscheiden.
Beispiel nginxSnippet (konzeptionell):
```
server {
server_name public.example.com;
location / { root /usr/share/nginx/html; try_files $uri /index.html; }
location ~ ^/api/(upload|manage) { proxy_pass http://backend:5001; proxy_set_header Host $host; }
location ~ ^/api/admin { return 403; }
location ~ ^/groups { return 403; }
}
server {
server_name internal.lan.example.com;
location / { proxy_pass http://frontend:3000; }
location /api/ { proxy_pass http://backend:5001; }
}
```
DockerCompose Hinweis (prod): Backend ohne `ports:` veröffentlichen; `reverse-proxy` expose Ports 80/443 und verbindet intern:
```
services:
reverse-proxy:
ports: ["80:80","443:443"]
networks: [public_net, internal_net]
backend:
networks: [internal_net]
# no ports
networks:
internal_net:
internal: true
```
Checklist (schnell umsetzbar)
- [ ] `nginx` reverseproxy hinzufügen
- [ ] BackendPorts entfernen (nur interner Zugriff)
- [ ] vhostRegeln: public vs internal (Admin blockieren auf public)
- [ ] `hostGate` middleware implementieren (Express)
- [ ] CORS, RateLimit, Captcha konfigurieren
- [ ] AuditLog `source_host` ergänzen
Wenn Du möchtest, implementiere ich als nächsten Schritt die `hostGate`Middleware, BeispielnginxVHosts und die `docker-compose`Änderungen als Patch hier im Repository. Sag mir kurz, welche Hostnames (Platzhalter sind OK) und ob Du Frontend lokal im selben Host behalten willst oder extern hosten willst.
## Technische Details & Voraussetzungen
Im Folgenden findest Du eine vertiefte, technische Zusammenfassung der Architekturoptionen, Voraussetzungen und Sicherheitsmaßnahmen — als Entscheidungsgrundlage für die Implementierung des Subdomainabhängigen Verhaltens.
1) Ziel und Sicherheitsprinzip
- Zweck: Subdomainabhängig unterschiedliche UX und APIZugänglichkeit (Public: Upload + Manage-UUID; Intranet: FullFeature).
- Sicherheitsprinzip: Nie ausschließlich auf FrontendSteuerung vertrauen — serverseitige Blockierung ist Pflicht.
2) InfrastrukturVarianten
- Variante A — Single Host + `nginx` ReverseProxy: Einfach, kontrollierbar, Proxy hostet TLS, routet an Backend; Backend nicht direkt erreichbar.
- Variante B — Frontend extern (CDN/Netlify) + Backend intern: Skalierbar; Bilder per presigned URLs oder BackendProxy ausliefern.
- Variante C — Backend nur im privaten Netz/VPN: Höchste Sicherheit, mehr Betriebskomplexität.
3) HostErkennung und Defense in Depth
- Proxy muss `Host` bzw. `X-Forwarded-Host` weiterreichen (`proxy_set_header Host $host`).
- Implementiere serverseitig eine `hostGate`Middleware, die `req.isPublic` bzw. `req.isInternal` setzt und schütze kritische Routen zusätzlich (Admin, Groups Listing, Cleanup).
- Kombiniere ProxyRegeln + Middleware + BearerToken (für Admin) + Firewall für maximale Sicherheit.
4) Speicherung und Auslieferung von Bildern
- Standard: Bilder lokal in `backend/src/data/images` und `.../previews`.
- Pattern A (empfohlen kleinbetrieblich): Backendproxied images — keine direkten öffentlichen Pfade; Backend kontrolliert Zugriffe (UUID, Host).
- Pattern B (Skalierung): Privater ObjectStore (S3compatible) + presigned URLs (TTL kurz) + CDN (Origin Access) für Performance.
- Previews können weniger restriktiv gehandhabt werden (kurze TTLs / signed URLs), Originals sollten restriktiver sein.
5) ManagementUUID (Risiken & Optionen)
- Aktuell: UUIDs permanent gültig bis Löschung (convenience). Risiko: Leak bedeutet Zugriff.
- Optionen: Beibehalten + RateLimit/Audit (empfohlen), oder TTL/Rotation/Optin Passwortschutz (sicherer, schlechtere UX).
6) CORS, CSRF, TLS
- CORS: Nur erlaubte Origins eintragen (public frontend origin(s) und/oder intranet origin).
- CSRF: REST API mit token/UUID im Pfad ist weniger CSRFanfällig, trotzdem sicherheitsbewusst durchführen.
- TLS/HSTS: Pflicht für öffentliche Hosts.
7) RateLimiting und AbuseProtection
- Public Uploads streng limitieren (z. B. 20 uploads/IP/Stunde) + Dateigrößenlimits + MIME/Exif/TypeValidation.
- Optional Captcha für Uploads bei hohem Traffic/Abuse Verdacht.
8) Logging und Monitoring
- Ergänze AuditLogs um `source_host`/`source_type` und `request_origin`.
- Metriken für ratelimit hits, 403s, upload errors, health checks; optional Sentry/Prometheus.
9) Docker/Bereitstellungsempfehlungen
- Dev: `docker/dev/docker-compose.yml` mit exposed ports OK.
- Prod: Backend keinem Hostport aussetzen (`ports:` entfernen). Reverseproxy exponiert 80/443; backend nur im internen DockerNetz.
- Verwende ein `internal` DockerNetzwerk oder separate Netzwerke für Public/Private.
10) nginxproxymanager Hinweise
- Konfiguriere ProxyHosts für public vs. internal mit passenden Headern (`Host`, `X-Forwarded-*`).
- Verwende ProxyRegeln, um `/api/admin` & `/groups` auf public Host zu blocken; teste mit `curl`.
11) DeploymentPrerequisites (konkret)
- DNS für beide Subdomains (public + intranet) vorhanden.
- TLS für public (Let's Encrypt) und internes Zertifikat für LAN.
- `ADMIN_API_KEY` sicher gesetzt, `PUBLIC_HOSTS` / `INTERNAL_HOSTS` konfiguriert.
- Backup/RestorePolicy für DB & images.
12) Entscheidungsfragen / Tradeoffs
- UUID permanent vs TTL: UX vs Security.
- Previews via CDN vs BackendProxy: Performance vs Kontrolle.
- Frontend lokal hinter nginx vs extern gehostet: Einfachheit vs Skalierbarkeit.
13) Prüfbare Akzeptanzkriterien (Beispiele)
- `curl -I https://public.example.com/api/admin/deletion-log` → 403
- Upload via public Host funktioniert (POST to `/api/upload`), Moderation API returns 403.
- Backend nicht per `docker ps`/published port extern erreichbar.
14) Vorschlag: nächste nonimplementierende Schritte
- Definiere endgültig: Public/Internal Hostnames; ManagementUUID Policy (TTL ja/nein); RateLimit Wert; CDN für Previews ja/nein.
- Ich kann danach ein SecurityDesignDokument (nginx rules, env vars, checklist) erstellen oder direkt ImplementierungsPatches liefern.
Bitte bestätige kurz die vier entscheidenden Punkte, damit ich das Design final zuspitze:
- Hosts: welche Subdomains sollen verwendet werden? (z. B. `deinprojekt.meindomain.de`, `deinprojekt.lan.meindomain.de`)
- ManagementUUID extern erlaubt? (Ja/Nein)
- RateLimit für public Uploads? (z. B. `20 uploads/IP/Stunde`)
- Previews via CDN erlaubt? (Ja/Nein)
---
Bitte sag mir, ob ich diese detaillierte Sektion so übernehmen soll (ich habe sie bereits in dieses FeatureRequest eingefügt). Wenn ja, kann ich auf Wunsch noch ein kurzes SecurityDesignPDF oder konkrete nginxSnippetDateien ergänzen.
<!-- Ende Feature Request -->