Zum Hauptinhalt springen
Diese Website enthält mit KI erstellte Inhalte. Mehr erfahren
KI
Mittelstand · Mittelständisches Unternehmen, Sage-100-ERP

Sicheres Gateway für Microsoft-365-Copilot-Zugriff auf ERP-Daten

Ein Security-Reverse-Proxy zwischen Microsoft Entra ID und dem Sage-100-ERP, damit Copilot gezielt auf Artikel-, Kunden- und Belegdaten zugreifen kann, ohne die ERP-Instanz direkt aus dem Internet erreichbar zu machen.

Zurück zu allen Referenzen
Mittelstand
Projektstart bis Vollimplementierung: 4 Tage
~220 Commits in 3,5 Monaten
226 automatisierte Tests, alle grün
~12.000 Zeilen C#, ~5.400 Zeilen TypeScript

Ausgangslage

Microsoft 365 Copilot sollte gezielt und sicher auf ERP-Daten zugreifen können (Artikel, Kunden, Belege) — die Sage-100-Instanz durfte dafür aber nicht direkt aus dem Internet erreichbar sein. Mehrere Parteien waren beteiligt: Kunde (DMZ-VM, Firewall, ERP-Betrieb), ein Microsoft-Partner (Copilot-Connector) und ein ERP-Dienstleister (Sage-Seite).

Umsetzung

Ein eigens entwickeltes Gateway zwischen Entra ID und Sage: JWT-Validierung, segmentgenaue Pfad-Whitelist mit Fail-Closed-Prinzip, strikte Mandantentrennung über Entra-App-Rollen, Pflicht-Idempotency-Key gegen ERP-Doppelbuchungen und vollständiges Audit-Log mit Live-Stream.

Ergebnis

Vollständig implementiert innerhalb von vier Tagen, seither im laufenden Live-Betrieb kontinuierlich erweitert. Rund 220 Commits, Installer-Version 1.0.50 nach etwa 3,5 Monaten, 226 automatisierte Tests, zwei Security-Audits mit insgesamt 7 behobenen CVEs.

Ausgangslage

Ein mittelständisches Unternehmen mit Sage-100-ERP (Zugriff über die SData-REST-API) wollte Microsoft 365 Copilot gezielt auf ERP-Daten zugreifen lassen. Die zentrale Anforderung: Die Sage-Instanz darf dafür nicht direkt aus dem Internet erreichbar sein.

Drei Parteien waren beteiligt — der Kunde stellte DMZ-VM, Firewall und ERP-Betrieb, ein Microsoft-Partner baute den Copilot-Connector, ein ERP-Dienstleister lieferte die Sage-Seite. codekunst systems baute das Gateway dazwischen.

Lösung: ein Security-Reverse-Proxy zwischen Entra ID und Sage

Das Gateway prüft jeden Aufruf und tauscht das eingehende Token gegen Sage-Zugangsdaten aus. Die wichtigsten Bausteine:

BausteinUmsetzung
AuthentifizierungEntra-ID-JWT-Validierung, App-only (client_credentials) für den Connector, MSAL-Login für das Dashboard
AutorisierungSegmentgenaue Pfad-Whitelist mit HTTP-Method-Matching und Resource-Mappings, fail-closed
MandantentrennungEntra-App-Rollen (Dev/Test/Prod) plus Routing über Datenbank/Mandant — Test kann strukturell nie Prod erreichen
Upstream-AuthPro Mandant konfigurierbar, Bearer oder HTTP Basic
Schutz vor DoppelbuchungenPflicht-Header Idempotency-Key bei jedem POST, Replay bei identischem Body, 409 bei abweichendem Body
AuditAccess-Log (EF Core/SQLite) mit Live-Stream (SSE), Response-Body-Einsicht und Diagnose bei falscher Token-Audience
BetriebAdmin-Bereich mit Live-Reload, Whitelist, Sage-Mandanten und IP-Whitelist hinter WAF (Trusted-Proxy)
AusrollungInno-Setup-Installer (self-contained), Linux-Dockerfile, DPAPI-verschlüsselte Secrets

Technik

  • Backend: .NET 10, ASP.NET Core mit Controllern, YARP als Forwarder, Kestrel
  • Frontend: React, TypeScript und Vite im Security-Ops-Console-Design
  • Persistenz: EF Core mit SQLite
  • Entwicklung mit KI-Unterstützung nach dem Muster Spec → Plan → Subagent-Umsetzung → zweistufiges Review

Lektion 1: Entra-Audience-Fallen

Entra ID stellt die aud-Angabe im Token je nach App-Typ als App-ID-URI oder als nackte ClientId aus. Das führte zu einem produktiven 401 (IDX10214). Die Lösung war eine Liste gültiger Audiences statt eines Einzelwerts.

Lektion 2: Least Privilege konsequent umgesetzt

Eine einzelne App-Registrierung war ursprünglich API, SPA und Testclient zugleich. Sie wurde in ein 3-App-Modell getrennt: Gateway-API, Dashboard und Copilot-Connector als eigenständige Registrierungen.

Lektion 3: ein Mandant, eine App

Der ERP-Dienstleister wollte je Umgebung eine eigene Registrierung, damit ein Test-Request strukturell nie Prod treffen kann — diese Trennung wurde entsprechend umgesetzt.

Lektion 4: Verwechslungsgefahr bei GUIDs

ApplicationId (aud), ClientId (azp) und Object ID wurden während der Implementierung wiederholt vertauscht. Das Dashboard zeigt seither die unbekannte Audience direkt im Log an, statt nur einen generischen Fehler zu melden.

Lektion 5: ERP-Eigenheiten sind kein Proxy-Bug

Zwei Fälle zeigten, dass ein Fehler nicht automatisch am eigenen Code liegt:

  • Sage liefert ohne den Parameter include=$children still leere Daten zurück, ohne Fehlermeldung.
  • Ein selbstsigniertes Zertifikat der Sage-Instanz führte zu 502-Fehlern am Gateway.

Lektion 6: Duplikat-Risiko ernst nehmen

Sage hat keinen eingebauten Duplicate-Check. Deshalb wurde der Idempotency-Key als Pflicht-Header eingeführt — bewusst auch als Breaking Change gegenüber einer laxeren Ausgangsversion.

Lektion 7: Dependency-Hygiene und CVEs

Zwei Security-Audits behoben zusammen 7 CVEs, darunter einen als High eingestuften React-Router-CSRF-Fund. Zwei Versions-Pins sind bewusst dokumentiert begründet: Microsoft.OpenApi bleibt unter 3.x und TypeScript unter 7.x, weil ein Update jeweils den Build beziehungsweise das Linting bricht — eine bewusste, begründete Entscheidung statt eines übersehenen Findings.

Lektion 8: Barrierefreiheit nachgeprüft

Eine Fehlermeldung im Dashboard hatte im Light-Theme nur einen Kontrast von 1,31:1 — praktisch unlesbar. Nach der Korrektur liegt der Kontrast bei 12,8:1 (Light) beziehungsweise 14,6:1 (Dark).

Was bewusst nicht öffentlich ist

Firmen-, Domain- und Hostnamen, Tenant- und Client-IDs sowie Namen beteiligter Dienstleister und Ansprechpartner sind aus dieser Fallstudie bewusst entfernt.

Vergleichbares Projekt anfragen

Sagen Sie uns kurz, wie Ihre Umgebung aussieht — wir sagen Ihnen ehrlich, was ein vergleichbares Projekt bei Ihnen bedeuten würde.