From ee4266495b57687678998abeb815c57711e5c5e5 Mon Sep 17 00:00:00 2001 From: Thomas Stallinger Date: Tue, 4 Aug 2026 16:51:38 +0200 Subject: [PATCH] docs: ADR-0011 Kundenplattform als getrennter Dienst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dokumentiert die Entscheidung, die Auftragskatalog-Web-UI nicht in provisioning-server einzubauen, sondern als eigenständigen Dienst mit eigener DB, eigenem Service-Token und Backend-for-Frontend-Muster zu bauen. Sicherheits-/Mehrmandanten-/Verfügbarkeitsgründe siehe Kontext. Co-Authored-By: Claude Sonnet 5 --- adr/0011-kundenplattform-getrennter-dienst.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 adr/0011-kundenplattform-getrennter-dienst.md diff --git a/adr/0011-kundenplattform-getrennter-dienst.md b/adr/0011-kundenplattform-getrennter-dienst.md new file mode 100644 index 0000000..e063484 --- /dev/null +++ b/adr/0011-kundenplattform-getrennter-dienst.md @@ -0,0 +1,37 @@ +# ADR-0011: Kundenplattform als eigenständiger, von Anode getrennter Dienst + +**Status:** Beschlossen +**Datum:** 04.08.2026 + +## Kontext + +Für den heute gebauten Auftragskatalog (ADR-0010) sollte eine Web-UI entstehen, über die Kunden Aufträge geräteweise aus-/abwählen können. Die naheliegende erste Idee — die UI direkt als weitere Routen in `provisioning-server` (anode) zu bauen — wurde verworfen: Anode bedient den kompletten Geräte-Flotten-Traffic (Aktivierung, Agent-Check-in/Report für potenziell tausende Schulrechner) über maschinenorientierte Endpoints mit einem einzigen globalen Admin-Token, ohne jedes Kunden-/Benutzerkonto-Konzept. + +Eine menschliche, sitzungsbasierte Kunden-Login-Oberfläche im selben Prozess/derselben Vertrauenszone zu betreiben hätte drei konkrete Nachteile: + +1. **Sicherheit:** Eine Web-UI mit Login/Sessions ist eine deutlich größere Angriffsfläche (XSS, CSRF, Session-Handling) als reine Maschine-zu-Maschine-Endpoints. Ein UI-Bug könnte potenziell die Flotten-Infrastruktur mitgefährden. +2. **Mehrmandantenfähigkeit:** Der bestehende `TUXFLOTTE_ADMIN_TOKEN` ist ein einzelnes globales Betreiber-Secret, keine pro-Organisation skalierende Authentifizierung. Ein echtes Kunden-Login braucht Accounts, die einer Organisation zugeordnet sind und nur deren eigene Geräte sehen dürfen. +3. **Verfügbarkeit:** Anode muss zuverlässig für die Geräte-Flotte erreichbar bleiben. UI-Traffic (z.B. viele gleichzeitig eingeloggte Kunden) sollte diese Zuverlässigkeit nicht gefährden können. + +Im Repo existiert noch kein Benutzerkonto-Konzept (nur Maschinen-Secrets für Geräte, siehe `devices.agent_secret_hash`) und keine bestehende "Kundenplattform"-Dokumentation — nur vereinzelte Prosa-Erwähnungen einer künftigen "Verwaltungsoberfläche"/eines "Self-Service-Portals" (u.a. ADR-0008, das bereits eine Neugeräte-Bestätigung über eine solche künftige Oberfläche vorsieht, dort aber nicht spezifiziert). + +## Entscheidung + +Ein neuer, eigenständiger Dienst **Kundenplattform** entsteht als getrenntes Repository (`kundenplattform`), getrennter Prozess (eigener systemd-Dienst, eigener Port) und getrennte Datenbank (eigene Postgres-Datenbank im selben Cluster, eigene Credentials, keine Cross-Database-Queries oder Foreign Keys zu `provisioning-server`s Datenbank). + +**Hosting:** vorerst auf demselben physischen Host wie anode, da aktuell keine Last-/Sicherheitsdringlichkeit für einen zweiten Host besteht — die Trennung ist auf Prozess-/Auth-/Datenbankebene bereits vollständig, ein späterer Umzug auf einen eigenen Host ist jederzeit möglich und kein großer Aufwand. + +**Backend-for-Frontend-Muster:** Kundenplattform authentifiziert Menschen (E-Mail/Passwort, signierte Session-Cookie) und spricht selbst mit anodes API über ein **eigenes** Service-Token (`TUXFLOTTE_KUNDENPLATTFORM_TOKEN`), getrennt vom bestehenden `TUXFLOTTE_ADMIN_TOKEN` — Kompromittierung oder Rotation des einen betrifft den anderen nicht. Anode bekommt dafür einen neuen Endpoint `GET /api/v1/organizations/{organization_id}/devices` zur Geräteauflistung; die bestehenden Auftragskatalog-Endpoints akzeptieren zusätzlich das neue Token. + +**Autorisierung bewusst auf Anwendungsebene, nicht in Anode:** Anode selbst bekommt noch keine feingranulare Pro-Kunde-Autorisierung (z.B. Row-Level-Security nach Organisation). Kundenplattform prüft vor jedem Aufruf gegen anode selbst, ob das angefragte Gerät zur Organisation des eingeloggten Benutzers gehört (Abgleich gegen die zuvor geladene Geräteliste). Das ist kein Sicherheitsloch, da Kundenplattform der einzige Inhaber des neuen Service-Tokens ist und somit die einzige Instanz, die diese Endpoints überhaupt aufrufen kann — es ist eine bewusste, dokumentierte Vereinfachung für die erste Version, keine dauerhafte Architekturentscheidung. + +**Technologie:** FastAPI + serverseitig gerendertes Jinja2 (kein SPA-Framework) — passt zum bestehenden minimalistischen Python/FastAPI-Stack im Projekt, erfordert kein neues Build-Tooling. Bei Bedarf später mit gezielter Interaktivität (z.B. HTMX) nachrüstbar, ohne einen SPA-Umbau zu erzwingen. + +**Umfang der ersten Version:** ausschließlich der Auftragskatalog (Login, Geräteliste, geräteweise Auswahl). Weitere Anforderungen — insbesondere die in ADR-0008 vorgesehene Neugerät-Bestätigung/Workspace-Zuweisung — sind spätere, eigenständige Ergänzungen auf derselben Architektur, keine Neuarchitektur. + +## Konsequenzen + +- Zwei separat deploybare Dienste statt einem: mehr Betriebsaufwand (zwei systemd-Einheiten, zwei Datenbanken, zwei Auth-Tokens zu verwalten), aber klare Trennung der Vertrauenszonen. +- Anode braucht für jeden neuen Kundenplattform-Anwendungsfall künftig potenziell weitere, gezielt scoped Endpoints (wie den neuen Organizations-Devices-Endpoint) statt direkter Datenbankzugriffe — das ist beabsichtigt und hält die Kopplung auf API-Ebene explizit. +- Solange Kundenplattform die einzige Inhaberin des `TUXFLOTTE_KUNDENPLATTFORM_TOKEN` ist, bleibt die Organisationszugehörigkeits-Prüfung dort zentralisiert. Sollte anode selbst irgendwann direkte externe Aufrufer (z.B. eine zweite Kundenplattform-Instanz oder Drittintegrationen) bekommen, muss die Autorisierung in anode selbst nachgezogen werden — das ist hier bewusst nicht vorweggenommen. +- Kein Self-Service-Registration/Invite-Flow in v1 — Kundenkonten werden manuell angelegt (analog zur bestehenden manuellen Aktivierungscode-Vergabe). Eine spätere Erweiterung ist ohne Schema-Bruch möglich (`benutzer`-Tabelle ist bereits generisch genug).