Brainversum · tuxametrics Graph Admin

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-cockpit zieht seit dem Wiederanschluss des Prozess-Cockpits nicht mehr dev.zwirn:zwirn-process-hub-flowable-spring (Engine plus flowable-spring-boot-starter-rest), sondern org.flowable:flowable-spring-boot-starter-process (nur Engine) zusammen mit zwirn-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", „eigene SecurityFilterChain" 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, siehe process-hub-wiederanschluss.md).

Zwei Punkte, die dieser Nachtrag NICHT behauptet:

  1. 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.
  2. 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-uis domains/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.

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:

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:

  1. die anderen drei Produkte nicht schützen,
  2. beim nächsten Starter-Update wieder auseinanderlaufen, und
  3. 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

  1. 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-instances
    

    Kommt ein 403/401, ist eine Annahme falsch — dann hier korrigieren, nicht löschen.

  2. 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 Default denyAll(), 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/bpmn tut es, und das Cockpit ist ohnehin leer.

  3. Die allgemeinere Frage nicht vergessen: UserContextFilter:77 ist 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.

  4. Wenn tuxametrics je BPMN modelliert: Flowables tenantId ist ein optionaler Query-Filter des Aufrufers, kein erzwungener Scope. Instanzen müssen aktiv mit einer tenantId gestartet werden, sonst ist die Trennung auch auf einer tenant-gescopten Eigen-API leer.