Die Siemens Digital Industries Software ist ein Geschäftsbereich der Siemens AG und ist bekannt für seine Produkte der 3D-Darstellung für die CAD-Entwicklung und PLM-Software, die dem Kunden Hardware, Software und Unterstützung für die Digitale Fabrik aus einer Hand anbietet. Das Unternehmen bietet führende Produkte im Bereich Electronic Design Automation für die Halbleiterindustrie an und hat mehrere Tausend Mitarbeiter.
Hintergrund
Im Dezember 2021 wurde ich in Kooperation mit Hays von Siemens beauftragt um bei der Implementierung eines Reverse Proxies auszuhelfen.
Der Reverse Proxy befand sich bereits in Entwicklung und sollte die erhöhten Sicherheitsanforderungen im Bereich der 2-Faktorautorisierung zur Absicherung der bestehenden Azure DevOps Serverlandschaft abbilden.
Siemens betreibt weltweit mit die größten On Premise Azure DevOps Serverinstanzen in eigenen Rechenzentren. Da Microsoft eine 2-Faktorautorisierung lediglich in der Cloud für Azure DevOps anbietet, hat sich das Projektteam dazu entschieden, eine Lösung in Form eines Reverse Proxies zu entwickeln, der alle Anfragen an die hauseigene Azure DevOps Infrastruktur zusätzlich absichert, in dem die Nutzer dazu gezwungen werden, sich über einen weiteren Identity Provider zu authentifizieren.
Hierzu zählen alle Zugriffe über gängige Browser, Git Clients wie auch Anbindungen über die Azure DevOps APIs (z.B REST und SOAP über HTTP).
Anforderungen
Im Kern muss der 2FA Reverse Proxy folgenden Anforderungen gewährleisten:
- Optimiertes Hosting im IIS (Internet Information Services)
- Unterstützung folgender Authentifizierungs-Schemata
- Basic
- Bearer
- Negotiate (Kerberos)
- Impersonierung des Windows Accounts, sodass durch den Reverse Proxy als Vermittler stets die ursprüngliche Identität beim eigentlichen Azure DevOps Server ankommt
- Zusätzliche 2-Faktorauthentifizierung durch einen OIDC Provider (z.B. bei der Verwendung von Browsern oder einem Git Client)
- Die Nutzung von Personal Access Tokens (vom Azure DevOps Server ausgestellt) wird direkt weitergeleitet
Umsetzung
Da ein erster Entwurf des Reverse Proxy in Form einer ASP.NET Anwendung (.NET Framework 4.7) bereits vorlag, lagen die ersten Schritte in der Analyse und Optimierung des bestehenden Quellcodes.
So wurde u.a. Dependency Injection eingeführt, um die Lebensdauer vitaler Grundkomponenten, wie dem HttpClient
, zu gewährleisten. In der vorherigen Implementierung wurde für jeden HTTP Request ein neuer HttpClient
erzeugt, was zu verminderter Performance und bei erhöhter Last zu TCP Port Exhaustion führte.
Ferner wurde der Quellcode insoweit refaktoriert, dass möglichst immer die asynchronen Implementierungen der ASP.NET APIs verwendet werden, um Threads nicht zu blockieren und effektiver zu nutzen.
Logging
Neben den ersten Optimierungen des Quellcodes wurde ebenfalls das bisherige auf Textdateien basierte Logging auf eine zentralisierte Logging-Lösung mit Elasticsearch und Kibana implementiert. Hierbei wurde das Logging-Framework NLog verwendet und strukturiertes Logging eingeführt um möglichst effizient detaillierte Informationen zum Zustand aller Reverse Proxy Instanzen zu erhalten.
Das strukturierte Logging ermöglicht die Zuordnung einer eindeutigen Request ID und weiteren Metadaten eines Benutzers (wie z.B. dem User-Agent oder Accountnamen) zu einem oder mehreren Logeinträgen. Dies erwies sich als besonders hilfreich für die Analyse und Behebung erster Probleme und machte den allgemeinen Zustand der Reverse Proxy Instanzen wesentlich transparenter.
Performance
Der Großteil der Azure DevOps Server Umgebungen unterliegt einer dauerhaften Grundlast. Ebenso gibt es zu gewissen Zeiten größere Lastspitzen, an denen bis zu 3.000 Requests pro Sekunde verarbeitet werden. Somit spielt die grundlegende Performance des Reverse Proxy von Anfang an eine projektentscheidende Rolle.
Sowohl die Akzeptanz der Nutzer als auch die Betriebskosten hängen direkt mit einer performanten Serveranwendung zusammen.
Bei dem Rollout des Reverse Proxy für die kleiner dimensionierten Azure DevOps Server Umgebungen war die Performance des Reverse Proxy auf Basis des .NET Framework 4.7 noch ausreichend. Jedoch stellte sich durch die Analyse der Windows Performance Counter für das Netzwerk und dem IIS-Server schnell heraus, dass es einen Flaschenhals in der Verwaltung der TCP Sockets und Ports gab.
Nahezu jeder Request erzeugte einen neuen TCP Socket, der nicht wie eigentlich erwartet wiederverwendet wurde. Stattdessen führte eine zu hohe Last zu einer TCP Port Exhaustion und machte die Nutzung des Systems somit quasi unbrauchbar.
Ebenso lag die CPU-Auslastung auf einem kritischen Level, da insbesondere die APIs zur Impersonierung der Windows Nutzer durch den Reverse Proxy einen Großteil der Last ausmachte.
Dedizierte Lasttests mit Hilfe von JMeter deuteten daraufhin, dass die Impersionierung den Großteil der Last erzeugte.
Zusätzlich zu dieser Problematik stellte sich ebenfalls heraus, dass die Kerberos-Tickets für die lokale Impersonierung nicht wiederverwendet und stattdessen für jeden Request erneut vom Key Distribution Center (KDC) angefragt wurden. Dies erzeugte also zusätzliche Last bei Diensten auf den Domain Controllern.
Migration auf .NET 6
Da ein Update auf .NET Framework 4.8 keine nennenswerten Verbesserungen mit sich brachte, habe ich der Projektleitung empfohlen eine Migration des Quellcodes auf .NET 6 vorzunehmen.
Diese Empfehlung basierte auf einer Recherche um die Verwendung des HTTP Stacks in Verbindung mit einer Impersonierung eines Windows Accounts. Ein erster Proof of Concept, der die Mindestanforderungen des Reverse Proxy abbildet, wurde daraufhin entwickelt und vorgestellt.
Durch diesen Proof of Concept konnte belegt werden, dass eine Migration auf .NET 6 tatsächlich einen enormen Mehrwert bietet und alle festgestellten Performanceprobleme behebt.
Auf Basis der .NET Framework 4.8 Applikation wurde daraufhin der gesamte Quellcode zu einer ASP.NET Core Anwendung auf Basis von .NET 6 migriert und umgeschrieben.
Zusätzlich wurden einige Kernkomponenten des Reverse Proxy, die für die Weiterleitung der HTTP Requests zuständig sind, durch den hauseigenen Reverse Proxy von Microsoft ersetzt. Dieser Reverse Proxy mit dem Namen YARP steht als Open Source Software auf GitHub zur Verfügung und wurde von mir geforked und entsprechend auf die Bedürfnisse des Kunden angepasst.
Um die verschiedenen Anforderungen der jeweiligen Authentifizierung zu gewährleisten wurden mehrere ASP.NET Middlewares entworfen, die den jeweiligen Anwendungsfall abhandeln. So gibt es z.B. für die zusätzliche Autorisierung über den hausinternen Siemens OIDC Identity Provider eine spezifische Middleware die den gesamten OAuth 2.0 Authorization Code Flow abbildet.
Zur stetigen Optimierung von Code, der bei jedem Request ausgeführt wird (Hotpath), wurden zusätzlich Benchmarks entwickelt. Diese ermöglichen eine exakte Messung und lassen somit feststellen, was für einen Einfluss die Änderungen auf die Performance haben. Neben der Optimierung der CPU-Auslastung wurde gezielt Fokus auf die Reduzierung von Speicherallokationen gelegt, um sowohl den gesamten Arbeitsspeicherverbrauch zu senken, als auch den Einsatz des .NET Garbage Collector zu minimieren.
Insbesondere die Verwendung von Spans
im Zusammenhang mit String-Allokationen konnten den Speicherverbrauch spürbar senken.
Im Hinblick auf alle durchgeführten Optimierungen konnte die Performance und der Durchsatz an Requests pro Sekunde um ein vielfaches verbessert werden.
Rollout und Monitoring
Nach der Umsetzung der Performanceoptimierungen konnte der Reverse Proxy auch vor die größten Azure DevOps Server Instanzen geschaltet werden.
Je nach erwarteter Auslastung wurden zwei oder mehr Instanzen des Reverse Proxy hinter einem Loadbalancer verfügbar gemacht, um sowohl die Last zu verteilen als auch ausfallsicher agieren zu können. Das Deployment einer neuen Version des Reverse Proxy erfolgt hierbei voll automatisiert. Die Build Artefakte werden über eine Azure Pipeline CI erzeugt und daraufhin über eine weitere Pipeline auf den jeweiligen IIS Servern deployed.
Das Monitoring der Applikation erfolgte über verschiedene Grafana Dashboards, die über Prometheus verfügbar gemachte Metriken visualisieren. Sowohl technische als auch fachliche Metriken wurden im Reverse Proxy definiert und im entsprechenden Format für Prometheus veröffentlicht.
Betrieb
Seit 2023 sind alle Azure DevOps Server Umgebungen nur noch über den vorgeschalteten 2FA Reverse Proxy erreichbar. Somit läuft jegliche HTTP Kommunikation nur noch abgesichert über den Reverse Proxy ab.
Im Durchschnitt sind Werktags hierbei mehr als 10.000 Nutzer aktiv und generieren täglich ca. 100 Millionen Requests bei Spitzen von bis zu 3.000 Requests pro Sekunde. Der zusätzliche Roundtrip zwischen Nutzer und Zielsystem ist kaum spürbar und liegt im Bereich von nur wenigen Millisekunden.
Täglich werden mehr als 10 Terrabyte an Daten zwischen allen Reverse Proxies und den Azure DevOps Servern transferiert.
Seit der Produktivschaltung bin ich weiterhin beratend für den Kunden tätig und sorge neben der Wartung der Software ebenfalls für die Implementierung weiterer Features und die Unterstützung des Betriebsteams.
Fazit
Durch die großartige Zusammenarbeit im gesamten Projektteam konnten alle Anforderungen bewältigt werden. Die verantwortlichen Personen haben mir stets große Freiräume eingeräumt, um eigenständig im Team agieren zu können und Lösungen zu erarbeiten.
Der Wechsel von dem veralteten .NET Framework auf das neue .NET Core Framework hat einen sehr positiven Effekt auf das Projekt ausgeübt und zeigt, wie viel Potenzial die neuen Entwicklungen im Bereich von .NET mit sich bringen. Durch die vielfältigen Tätigkeiten konnte ich sowohl meine Expertise für den Kunden zufriedenstellend einbringen als auch selbst viel dazulernen.
Bisher (Juli 2023) gab es keine technischen Störungen oder Ausfälle der 2FA Reverse Proxies.
Feedback des Kunden
“Durch seine hohen Qualitätsansprüche und eigenständige Herangehensweise trug Herr Güldenpfennig wesentlich dazu bei, dass wir sämtliche Anforderungen gemäß unserer Planung rechtzeitig umsetzen und abschließen konnten. Seine fundierte technische Expertise und Beratung hatten einen enorm positiven Einfluss auf die Projektumsetzung und führten zu einem herausragenden Erfolg in unserem Unternehmen.”
Marija Küster, Software Development Solutions, Siemens