konzept vertraulich owner: matus
Flowable-Passthrough `/process-api/**` umgeht die Tenant-Prüfung
Flowable-Passthrough /process-api/** umgeht die Tenant-Prüfung
Status (2026-08-15): für dieses Produkt ERLEDIGT — der Pfad existiert nicht mehr. · strukturelle Ursache in Zwirn weiterhin offen · Ursache: Zwirn, betrifft vier Produkte
Nachtrag 2026-08-15 — die Angriffsfläche ist weg, nicht abgesichert.
tuxametrics-process-hub-cockpitzieht seit dem Wiederanschluss des Prozess-Cockpits nicht mehrdev.zwirn:zwirn-process-hub-flowable-spring(Engine plusflowable-spring-boot-starter-rest), sondernorg.flowable:flowable-spring-boot-starter-process(nur Engine) zusammen mitzwirn-process-api/zwirn-process-flowable/zwirn-process-hub-router-spring./process-api/**wird damit gar nicht mehr registriert, und die beiden Proxy-Regeln auf/flowable-api/process-api/(tuxametrics-ui/vite.config.ts,tuxametrics-ui/nginx-locations.conf) sind entfallen.Das unten diskutierte Abwägen zwischen „
denyAll()per Default", „eigeneSecurityFilterChain" und „auf Zwirn warten" ist damit gegenstandslos: keine dieser Optionen wurde gewählt, die Route ist ersatzlos verschwunden. Was an ihre Stelle tritt, ist ausschließlich lesend, mandantengescoped und capability-geprüft (txm.process.*, vier Schlüssel, sieheprocess-hub-wiederanschluss.md).Zwei Punkte, die dieser Nachtrag NICHT behauptet:
- Der Befund unten war richtig und ist nicht widerlegt — er gilt unverändert für jedes Produkt, das den alten Starter noch fährt. Er ist hier nur nicht mehr anwendbar.
- Die strukturelle Ursache (Zwirns
UserContextFilterüberspringt die Mismatch-Prüfung stillschweigend, sobald der Pfad-Regex nicht greift) sitzt weiter in Zwirn und ist unverändert offen. Sie trifft jeden Pfad ohne/tenants/-Segment, nicht nur Flowables. Der Abschnitt „Strukturelle Ursache" unten bleibt deshalb gültig.Ein Nebenbefund aus dem Umbau, der hierher gehört: der Satz unten „Kein Repo-eigener Code ruft
/process-api/**" stimmte seit dem 2026-08-14 nicht mehr.tuxametrics-uisdomains/testfaelle/api/flowableApi.ts(Gold-Path-Prozessdiagramm) rief Flowables REST direkt an — also genau die Route, die hier als konsumentenlos geführt wurde. Sie ist beim Umbau auf den Prozess-Vertrag umgestellt worden (prozessApi.ts) und war damit fast die Ursache eines stillen Ausfalls: das Entfernen der Proxy-Regel hätte das Diagramm ohne Fehlermeldung leer gelassen. Lehre: ein „hat keine Konsumenten"-Befund altert, sobald jemand ohne Kenntnis dieses Dokuments etwas Naheliegendes baut.
Ursprünglicher Status (2026-08-10): Sofortmaßnahme umgesetzt · strukturelle Ursache offen und bewusst NICHT hier zu lösen · Priorität: hoch, aber nicht akut — die Engine ist leer
Der Befund
tuxametrics-process-hub-cockpit zieht dev.zwirn:zwirn-process-hub-flowable-spring
(tuxametrics-process-hub-cockpit/pom.xml:58-62). Dieser Starter bündelt bewusst
flowable-spring-boot-starter-rest mit — also Flowables rohe REST-API unter /process-api/**.
Das ist kein Versehen des Starters, sondern sein Zweck: ohne diese Dependency liefert ein
Cockpit-Frontend 404 (Zwirn-ADR-0004).
Die Sicherheitsfolge wurde dabei nicht mitgedacht. application.yaml:28-30 setzt
zwirn:
security:
tenant-path-pattern: "/tenants/([^/]+)"
Kein Flowable-Pfad (/process-api/runtime/process-instances,
/process-api/repository/deployments, …) enthält /tenants/. Der Regex kann dort nicht greifen,
extractTenantKey liefert null — und die Mismatch-Prüfung in Zwirns UserContextFilter:77 beginnt
mit genau diesem null:
if (pathTenantKey != null && !allowedTenantKeys.isEmpty() && !allowedTenantKeys.contains(pathTenantKey)) {
Sie wird also übersprungen. Der Zugriff ist authentifiziert (Zwirns SecurityConfig fordert
anyRequest().authenticated(), und Flowables FlowableSecurityAutoConfiguration registriert keine
konkurrierende SecurityFilterChain — am Bytecode geprüft), aber nicht tenant-geprüft. Und die
Flowable-REST-API ist nicht read-only: sie kann Prozesse starten, Variablen setzen, Tasks
abschließen, Instanzen löschen und BPMN deployen.
Wie schlimm ist es hier konkret
Heute: nicht schlimm, und das ist keine Beschönigung.
- Die Engine ist leer.
find . -name "*.bpmn*"über das Repo: null Treffer.check-process-definitions: falseverhindert zusätzlich, dass beim Start etwas aus dem Classpath deployt wird. Es gibt keine fremden Prozessdaten, die geleakt werden könnten. - Kein Repo-eigener Code ruft
/process-api/**. Der einzige Aufrufer ist das externe Paket@zwirn/bpmnüber den Vite-Dev-Proxy (tuxametrics-ui/vite.config.ts:36-43). - Der Stack läuft nirgends nicht-lokal.
Trotzdem nicht ignorierbar: Schreibzugriff braucht keine vorhandenen Daten. Ein Realm-Nutzer
konnte bisher über den auf 0.0.0.0 gebundenen Port 8124 eine eigene BPMN-Definition in die Engine
deployen. Und sobald tuxametrics den Gold Path doch als BPMN modelliert — im Scope-Dokument
ausdrücklich offengelassen —, kippt „leer" ohne weiteres Zutun.
Was am 2026-08-10 getan wurde
Nur eine Sache, bewusst die kleinste: In docker-compose.yml ist der Port des Cockpits von
"8124:8124" auf "127.0.0.1:8124:8124" geändert, mit der Begründung im Kommentar darüber.
Warum genau das und nichts anderes:
- Es kostet nichts. Der Vite-Dev-Proxy zielt ohnehin auf
127.0.0.1:8124; Container untereinander reden über das Compose-Netz, nicht über den Host-Port. Kein dokumentierter Arbeitsablauf bricht. - Es ist rückbaubar und in einer Zeile lesbar.
- Es behebt die Lücke nicht und behauptet das auch nicht: wer Zugang zum Host oder zum Compose-Netz hat, kommt weiterhin durch. Es verkleinert nur die Angriffsfläche von „jedes Interface der Maschine" auf „Loopback".
Was bewusst NICHT getan wurde
Die strukturelle Lücke wurde hier nicht repariert. Sie sitzt in
zwirn-process-hub-flowable-spring und betrifft vier Produkte: dot-process-hub,
evepop-process-hub, prozesswerkstatt-process-hub (DKFZ, Port 28120) und dieses Cockpit. Ein
tuxametrics-lokaler Alleingang — etwa eine eigene SecurityFilterChain, die /process-api/**
sperrt — würde:
- die anderen drei Produkte nicht schützen,
- beim nächsten Starter-Update wieder auseinanderlaufen, und
- genau das Drift-Problem reproduzieren, dessen Beseitigung die Existenzberechtigung dieses Starters ist.
Das ist eine produktübergreifende Entscheidung. Sie gehört in ein Zwirn-ADR.
Nächste Schritte
-
Live verifizieren (steht noch aus; der Befund ist statisch am Code belegt). Mit einem gültigen Realm-Token gegen den laufenden Hub:
# Erwartung laut Befund: 200 mit Daten, nicht 403 - obwohl kein Tenant im Pfad steht. curl -i -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8124/process-api/runtime/process-instancesKommt ein 403/401, ist eine Annahme falsch — dann hier korrigieren, nicht löschen.
-
Die Frage an Zwirn stellen. Drei Optionen sind im ausführlichen Schwester-Eintrag des DKFZ-Repos beschrieben (
dkfz-prozesswerkstatt/prozesswerkstatt-platform/agentic-engineering/ backlog/flowable-passthrough-tenant-isolation.md): Passthrough per DefaultdenyAll(), ein tenant-erzwingender Filter davor, oder den Passthrough ganz entfernen. Für tuxametrics wäre Option „denyAll() per Default" schmerzfrei — das Repo ruft/process-api/**nirgends selbst auf, nur@zwirn/bpmntut es, und das Cockpit ist ohnehin leer. -
Die allgemeinere Frage nicht vergessen:
UserContextFilter:77ist auch unabhängig von Flowable eine Fail-Open-Konstruktion — kein Tenant im Pfad → keine Prüfung. Ob ein Modul deklarieren muss, welche Pfade tenantfrei sein dürfen (Allowlist statt stillschweigender Ausnahme), ist die wertvollere Frage, weil sie die nächste Variante desselben Fehlers verhindert. -
Wenn tuxametrics je BPMN modelliert: Flowables
tenantIdist ein optionaler Query-Filter des Aufrufers, kein erzwungener Scope. Instanzen müssen aktiv mit einertenantIdgestartet werden, sonst ist die Trennung auch auf einer tenant-gescopten Eigen-API leer.