Im Netz gibt es zahlreiche Beispiele für die Verwendung von Prozess-Engines in verschiedenen Anwendungsfällen. Dabei ist es wichtig, nicht nur die einfacheren Beispiele zu betrachten, sondern auch Lösungen für den produktiven Einsatz zu finden. Eine produktive Umgebung erfordert Stabilität und Sicherheit, insbesondere im DEVOPS-Kontext. Bei der Installation einer Anwendung und der Auswahl der Komponenten muss auch die Umgebung und die Absicherung berücksichtigt werden. In vielen Microservice-Umgebungen wird bereits OAuth2 für das Identity und Access Management eingesetzt, zum Beispiel mit Hilfe von Keycloak. Dieser Blog-Artikel bietet einen Lösungsansatz, wie ein System mit der Camunda Prozess-Engine (Version 7) und den erforderlichen Komponenten aufgebaut werden kann, um den Sicherheitsanforderungen eines produktiven Systems gerecht zu werden. Es werden die relevanten Konzepte und Komponenten erläutert.

 

Architektur und Implementierung

Als Basis für die Standalone-Process-Engine kann ein fertiges Docker-Image aus dem offiziellen Camunda Repository bei Docker-Hub verwendet werden. Das Einbauen diverser Erweiterung würde hier jedoch schnell an Grenzen stoßen bzw. ist einfacher zu bewerkstelligen, wenn das spätere Container-Image mit Hilfe eines eigenen Dockerfiles gebaut wird.

Ein Architektur-Übersicht  wie in Abb.1 zu sehen ist, enthält den möglichen System-Aufbau und umfasst auch die notwendigen externen Ressourcen. Als Identity-Provider habe ich hier Keycloak gewählt, welches dann per Plugin an Camunda angebunden wird. 

Da ich grundsätzlich einen Container-Ansatz gewählt habe, macht es Sinn in den entsprechenden Container-Netzen einen Web-Server vor zu schalten, um die SSL-Verbindungen aus dem Netz heraus und in das Netz hinein unkomplizierter zu gestalten. Hier kann Apache, oder NGINX (wie in dem Beispiel) verwendet werden. Für die Entwicklung und Staging reicht es aus auch Docker als Ausführungsumgebung zu verwenden. In produktiven Systemen würden die entsprechenden Images jedoch meist in Cloud-Systeme wie AWS installiert. Dies ändert jedoch nichts an der prinzipiellen Architektur - mit einer Ausnahme: ich lasse hier die Skalierung außer Acht. Als Hinweis für alle diejenigen, die mehr Wert auf Themen wie Skalierbarkeit und Cloud legen, sollten sich auch mit der Camunda Platfom 8 näher beschäftigen.

Abbildung zu Architektur und Implementierung
Abbildung zu Architektur und Implementierung

Setup

Ein Service, eine Benutzeroberfläche, eine Standalone-Camunda-Instanz mit exponierter REST-Schnittstelle, eine Keycloak-Instanz. Es müssen nun alle Anfragen des Systems über sichere Verbindungen aufgebaut werden, also TLS und zusätzlich müssen die Anfragen der Komponenten unter einander authentifiziert und autorisiert werden:

  1. Aufrufe der Benutzerinnen und Benutzer an die Benutzeroberfläche
  2. Kommunikation zwischen der Benutzeroberfläche und der Prozessapplikation
  3. Kommunikation zwischen der Prozessapplikation und der Prozess-Engine
  4. Anfragen der Camunda-Instanz mit den Services
Abbildung zu Setup
Abbildung zu Setup

Basis Projekt 

Für die Realisierung verwende ich hier ein Maven-Projekt. Wie eingangs bereits erwähnt, ist es notwendig einige Erweiterungen zur Camunda-Instanz hinzuzufügen. Die pom.xml enthält daher neben den Abhängigkeiten zu Spring-Boot, auch die notwendigen Komponenten für die Camunda-Engine, sowie die "keycloak-identity-provider-extension". Zusätzlich soll eine Prozess-Instanz auch mit REST-Schnittstellen direkt kommunizieren können. Dazu werden das Camunda-Connector-Packages benötigt, also auch SPIN.

Eine produktive Applikation würde den Zustand der Prozess-Engine darüber nicht in einer In-Memory-Datenbank, wie der H2 persistieren, sondern eine persistente Datenbank verwenden - hier PostgreSQL.

code beispiel
code beispiel

Ein paar Worte zu der Abbildung Abb.2. Hier gibt es eine Prozessapplikation und eine Prozess-Engine. Obige pom.xml beinhaltet jedoch beide Komponenten. Dies ist sicherlich korrekt, jedoch enthält eine Prozessapplikation noch weitere Komponenten wie Delegates u.ä. . In der obigen Abbildung jedoch können diese beiden Komponenten getrennt dargestellt werden, um entsprechenden Komponenten zu identifizieren.

Nachdem mit obiger pom.xml das Basis-Projekt erzeugt wird, folgt nun die Container-Konfiguration. Hier in Form eines Dockerfile:

code beispiel
code beispiel

Dieses baut das Projekt und im Anschluss ein Docker-Image. Im Grunde könnte der kommentierte Entrypoint verwendet werden, es hatte sich jedoch als hilfreich erwiesen eine Skriptdatei "start.sh" zu verwenden. Diese kann einige Arbeiten zuvor übernehmen z.B. das Bestücken des JDK-Keystore mit den notwendigen Zertifikaten.

Konfigurationen

Zunächst möchte ich auf die unten gezeigte Spring-Boot Konfiguration eingehen. Aus der pom.xml ist bereits ersichtlich gewesen, dass unter anderem auch OAtuh2-Komponenten aus dem Spring-Framework verwendet werden. Die OAuth2 / Keycloak Konfiguration in meinem Beispiel sieht dann wie folgt aus:

code beispiel
code beispiel

 

Ausgehend von der obigen Konfiguration, müssen nun noch zusätzliche Konfigurationen eingetragen werden, die das Identity-Provider-Plugin für Camunda betreffen:

code beispiel
code beispiel

Erweiterungen

Ein genereller Vorteil der Camunda Platform ist die Erweiterbarkeit. Diese kann, wie in meinem Beispiel, aus verschiedenen Konnektoren (Connectors) und Erweiterungen (Extensions) bestehen. Im Wesentlichen werden die hier aufgeführten Erweiterungen durch die Nutzung der Keycloak identity provider extension und der Architektur (REST-basiert) vorgegeben.

SPIN - Damit auch simple Operationen auf REST-Calls aus einem Prozess heraus z.B. per Skript durchgeführt werden können, wird die SPIN library benötigt. 

Keycloak identity provider extension - Eine Erweiterung, mit deren Hilfe ein Keycloak identity provider an Camunda angebunden werden kann.

Neben der oben erwähnten SPIN-library kann auch eine Mail extension eingesetzt werden, die es ermöglicht z.B. Benutzer-Rückmeldung aus dem Prozess heraus zu realisieren. Die kann einfach eine Rückmeldung sein, wie dass ein Task der/m betreffenden Benutzer/in zugewiesen worden ist, oder auch einen Deep-Link zu einer entsprechenden Benutzeroberfläche enthalten, in der die Taskliste eines Benutzers dargestellt wird. 

Stolpersteine

Nicht triviale Zugriffsrechte

Zu den Stolpersteinen in einem produktiven Szenario gehören zweifellos nicht triviale Zugriffsrechte von Benutzerinnen und Benutzern. Ein Beispiel eines solchen Falls könnte wie folg lauten:

In einem Identity-Management System werden Zugriffsrechte für viele verschiedene System verwaltet. So soll auch Camunda als eines dieser Systeme hinzugefügt werden. Ein erster Wurf sieht vor, dass zwischen Benutzerinnen und Benutzern unterschieden werden muss, die Zugriff auf Prozesse - ganz allgemein - bekommen sollen und solche, die Administrationsrechte bekommen sollen. Oftmals bilden Identity-Management-Systeme jedoch auch Strukturen des Unternehmens ab. Also z.B. verschiedene Abteilungen. Jetzt sollen jedoch Personen aus verschiedenen Abteilungen Rechte für die Prozess-Engine eingeräumt werden. Kein Problem. Alle Benutzerinnen und Benutzer, die der Abteilung A angehören bekommen die GruppeA und auch eine Gruppe CamundaUser, oder eben CamundaAdmin.  So weit, so einfach. Nur was, wenn Benutzerinnen der Abteilung A nur Zugriff auf Prozesse der Abteilung A bekommen sollen und nicht auch aller andren Abteilungen? Ein naiver Ansatz wäre entsprechende Rechtegruppen einzuführen z.B. nicht mehr CamundaUser, sondern ABT-A-CAM-USR oder ABT-A-CAM-ADM. Je nachdem wieviel Abteilungen das sind, entstehen jedoch schnell sehr viele neue Gruppen. Camunda bietet die Möglichkeit nach Tenants zu unterschieden und damit würde Abteilung A ein Tenant und Abteilung B ein anderer Tenant.

Was würde nun aber geschehen, wenn innerhalb einer Abteilung auch die Prozesse nach Domänen aufgeteilt sind und diese z.B. Webseite- und HR- Prozesse beinhalten. Es entsteht erneut ein Problem, wenn nur Benutzer und Administratoren unterschiedliche Gruppen bekommen. So könnten Mitarbeiterinnen und Mitarbeiter der Webseiten-Entwicklung auch Prozesse der HR-Abteilung sehen und eventuell auch starten können. Um dies wiederum zu verhindern, müssten erneut fein granularer Rechte vergeben werden. Also erneut die Überlegung, nun anstatt CamundaUser / CamundaAdmin, die Gruppen HR-CAM-USR/ADM und WEB-CAM-USR/ADM.

All diese Probleme sind zu bewältigen. Es muss nur bei der Konzeption genau darauf geachtet werden, wie die Benutzer-Rechte aufgeteilt und verwaltet werden, um sich schwierige Änderungen, die eventuell Auswirkungen auf die Benutzerrechte vieler Benutzerinnen und Benutzer haben, zu vermeiden.

Testen des Systems

Nun da das System steht, die Benutzerrechte abgebildet sind, bleibt die Frage, wie alle getestet werden kann. Je nach der Art und Weise, wie Prozesse in das System eingespielt werden - also als Artefakt oder per Schnittstelle, kann es sein, oder auch nicht, z.B. mit JUnit-Tests zu arbeiten. Weit häufiger wird jedoch, wenn man meinem Architektur-Vorschlag folgt, ein zentrales System etabliert, das von verschiedenen Stakeholdern verwendet werden kann. Wichtig ist allgemein beim Einsatz einer Prozess-Engine die niederschwellige Nutzung. Wenn ein aufwendiger Release-Prozess zum Einspielen neuer Prozesse aufgesetzt wird, der mehrere Artefakte mit der Beteiligung von Fachabteilungen und der Entwicklung voraussetzt, wird dieses Ziel wohl eher nicht erreicht. Wenn jedoch im Idealfall eine Fachabteilung ihre Prozesse selbst auf die Prozess-Engine  ausbringen kann, bleibt erneut die Frage, wie man einen Ende-zu-Ende-Test durchführen kann. Dazu ein Ansatz, der mir als Entwickler sehr gute Dienste geleistet hat. Durch das Design als REST-API getriebene Prozess-Engine, die sowohl über ein Benutzer-Interface (UI) gesteuert werden kann, oder eben auch durch Werkzeuge wie Postman, gibt es die Möglichkeit Test-Suiten zu entwickeln, die Ende-zu-Ende-Tests ermöglichen. 

Abbildung zu Testen des Systems
Abbildung zu Testen des Systems

Zusammenfassung

REST-basierte Architektur ermöglicht einfache Nutzung der Funktionalitäten der Camunda-Prozess-Engine gerade auch in modernen Microservice- bzw. Service-Umgebungen. Diese Architektur ermöglicht auch das Testen des Systems.

Mit Containern lassen sich leicht verschiedene Umgebungen aufsetzen, die später auch in produktiven Umgebungen eingesetzt werden können. Die Kapselung der Komponenten in den Containern erhöhen auch die Sicherheit, da speziell die Schnittstellen in die Container-Netze und aus den Container-Netzen heraus betrachtet und gesichert werden können. Es müssen insbesondere keine Ports nach außen geöffnet werden, die nicht unmittelbar für die Verwendung der Komponenten zwingend erforderlich sind. Z.B. Datenbank Ports.

Einfache Konfiguration von Zugriffsrechten mit Hilfe einer OAuth2-Umgebung. Oftmals wird damit eine Integration in bereits bestehende Strukturen in Bezug auf die Rollen und Rechte von Benutzern und Services erleichtert.

Die Verbindungen zwischen den Komponenten und der Zugriff auf die Benutzerschnittstellen, sollten per SSL abgesichert sein. Die Verbindungen zwischen den Services und der Prozess-Engine sollten dabei nicht vergessen werden.

Autor

Patrick Hefele

randstad digital germany