Kubernetes und Docker: Ab in den Container
Container-Technologien wie Docker und Kubernetes werden immer beliebter. Dafür gibt es eine Reihe von Gründen, doch viel zu oft werden die Herausforderungen bei der Implementierung übersehen. Mit fatalen Folgen für den Ausgang von Projekten und die IT-Sicherheit.
Container-Technologien erleben in den vergangenen zwei Jahren einen regelrechten Höhenflug. Inzwischen finden sich viele Artikel, die ambitionierten Laien ganz selbstverständlich den Umgang mit Docker-Images erklären.
Die Containerisierung hat bei Entwicklern und Administratoren einen Nerv getroffen. Das ist wenig überraschend, wandern doch immer mehr Daten und Workloads in Cloud-Infrastrukturen. Der Einsatz von Containern gilt inzwischen als zuverlässige Technologie, um Applikationen und Services sicher in der Cloud auszurollen und schafft an vielen Stellen eine deutlich höhere Flexibilität in Entwicklung und Betrieb.
Der Einsatz von Docker und Kubernetes bringt aber auch zahlreiche Herausforderungen und Fragen mit sich, die in vielerlei Hinsicht über den Erfolg einzelner Projekte, aber auch der gesamten Strategie entscheiden können.
Container sind keine Wunderwaffe
Die Begeisterung für neue Technologien und Innovationen ist Teil der DNA der IT-Branche. Im Zuge der Begeisterung für einen Ansatz, dessen Vorteile überzeugend dargestellt werden, sind dann oft Fehlentwicklungen zu beobachten. So scheint die Containerisierung das ideale Werkzeug zur Erreichung von mehr Agilität zu sein. Und als "agil" möchte sich heute jedes Unternehmen präsentieren. Schnell werden Container und Pods als regelrechte Wunderwaffe gesehen, mit deren Einsatz nun alles (auf einmal wieder) viel besser und leichter wird.
Hier bewegen sich doch einige Unternehmen zwischen zwei Extremen. Statt sich intensiv die Frage zu stellen, welche Probleme und Herausforderungen mit den in Containern laufenden Anwendungen eigentlich gelöst werden sollen, greifen die Entwicklungsteams bedenkenlos in den Werkzeugkasten mit der Aufschrift Docker oder Kubernetes.
Geht es um den Betrieb einiger weniger Services, sollte die Frage eher sein, ob sich das nicht genauso einfach mit den berühmten Bordmitteln von Providern wie AWS, Azure oder Google erreichen lässt. Mit Kubernetes hier eine Art Cloud in der Cloud zu bauen, erscheint doch überdimensioniert. Niemand käme wohl auf die Idee, sofort zur Bohrmaschine zu greifen, wenn es auch ein Nagel täte. Doch getrieben von der Begeisterung, neue Technologie statt etwas Altem zu verwenden, wandern die Services dann in Container. Das erhöht allerdings die Komplexität extrem. Wenn das Team dann nicht von Expertinnen und Experten bereichert wird, die über tiefgreifende Kenntnisse etwa in Kubernetes verfügen, frisst der Aufbau der Infrastrukturen den Vorteil der eigentlich agil betriebenen Entwicklung schlicht auf.
Führen zusätzlich Sicherheitsanforderungen und Unternehmensstrategie dazu, Kubernetes vor Ort betreiben zu wollen, entwickelt sich daraus dann schnell ein Riesenprojekt. Schließlich muss sich das Team dann mit allen Detailfragestellungen intensiv beschäftigen, vom Netzwerk bis zu den API-Servern. Dabei hätte es bei einem kommerziellen Anbieter eine viel einfachere Lösung schon gegeben.
Beim zweiten Extrem wiederholen sich die Erfahrungen mit der Einführung von agilen Methoden selbst. Unter dem Motto "Wir machen jetzt auch etwas mit Containern", wird ein bereits bestehendes und funktionierendes Gebilde aus Applikation und Datenbank in einen Container gepackt. Das ist dann nichts anderes als l’art pour l’art, aber weder eine Problemlösung noch eine Verbesserung im Sinne des Use Cases.
Es kommt eben auf den Workload an
Damit stellt sich die Frage, wann die Containerisierung echte Vorteile bringt? Dies dürfte am eindrucksvollsten im Rahmen einer Microservices-Architektur der Fall sein. Die Entwicklung kann so jeden Service in einem eigenen Container bereitstellen. Mit Orchestrierungswerkzeugen wird dann die Gesamtanwendung zusammengestellt. Rein technisch gesehen wäre die Bereitstellung eines Services in einer eigenen virtuellen Maschine auch möglich. Im direkten Vergleich dürften die kurzen Startzeiten von Containern für sich sprechen. Doch auch die Containerisierung – obwohl eine recht junge Erscheinung – ist bereits Moden unterworfen. So konnte eine deutliche Bewegung weg von Docker in Richtung containerd als bevorzugter Runtime für Container im Umfeld von Kubernetes festgestellt werden [1].
Ein großer Vorteil bei der Nutzung von Containern liegt in der Option, Anwendungstests vor der eigentlichen Bereitstellung durchführen zu können. Da die Tests in der gleichen Umgebung ablaufen, die auch im Produktivbetrieb genutzt wird, minimiert dies die implizit vorhandenen Risiken, die sich aus Unterschieden der Test- respektive Entwicklungsumgebung und der Produktionsumgebung ergeben.
Mit diesem Vorteil geht einher, dass Entwickler Funktionen bereits in einem früheren Stadium der Anwendungsentwicklung prüfen können. Da ein Container wie Docker auch auf einem einfachen PC ablaufen kann, muss nicht erst eine dedizierte Testumgebung geschaffen werden. Ist die Anwendung gereift, kann immer noch eine Testumgebung aufgesetzt werden. Dank der Portabilität der Containerisierung geht die Migration der Anwendung in die Testumgebung schneller.
Geht es um Workloads in einer Multi-Cloud- oder Hybrid-Cloud-Umgebung, sind Container ein dazu passendes Werkzeug. Das Verschieben des Containers (und somit der Anwendung) macht maximal kleinere Änderungen an der Konfiguration notwendig. Die Bereitstellung der Anwendung in der gewünschten Cloud-Umgebung ist ansonsten problemlos. Und auch der Weg von der Cloud in Richtung lokaler Instanzen gestaltet sich mit diesem Ansatz viel einfacher.
Was für die Cloud selbst gilt, trifft auch für das Betriebssystem zu. Spezielle Anpassungen an einzelne Plattformen oder Derivate – man denke an die zahlreichen Linux-Distributionen – sind normalerweise nicht nötig. Docker ist zwar nicht völlig agnostisch gegenüber dem Betriebssystem, aber innerhalb einzelner Familien vereinfacht der Container-Ansatz die Distribution. Auf die unterschiedlichen Voraussetzungen und Abhängigkeiten, die Folge der verschiedenen Paketverwaltungssysteme ist, muss keine Rücksicht genommen zu werden. Gehört es zur Charakteristik der Zielanwendung oder der Strategie, möglichst betriebsunabhängig einen Service bereitzustellen, ist die Containerisierung ein adäquates Mittel, um Aufwände zu minimieren.
Potenziell bietet der Einsatz von Containern auch Einsparpotenziale bei Infrastrukturkosten. Die Nutzung von Container anstelle klassischer virtueller Maschinen hat zumindest rechnerisch Vorteile, weil der Ressourcenbedarf geringer sein sollte. Gegenüber den konkreten Vorteilen bei der Entwicklung und dem Deployment ist der Kostengesichtspunkt indes eher sekundär.
Wie eingangs bereits erwähnt, steigt mit der Orchestrierung verschiedener Container und Service auch die Komplexität der gesamten Infrastruktur. Das ist nur dann sinnvoll, wenn bereits mit zahlreichen Services geplant wird, oder die Entwicklungsstrategie in dieser Richtung verläuft. Einige wenige Dienste, die noch dazu nicht häufig verändert werden, sind in einer klassischen virtuellen Maschine besser aufgehoben.
Intensiv muss der Sicherheitsaspekt diskutiert werden. Sofern aus technischer Sicht und Compliance-Vorgaben die strikte Isolation einer Anwendung notwendig ist, dürfte eine VM die bessere Entscheidung sein. In dieser Hinsicht sind sie Container-Umgebungen überlegen, da diese nicht im gleichen Maß isoliert sind, was auch für die Container untereinander gilt.
Sicherheit bleibt die große Herausforderung
Mehr Agilität, die Nähe zur Cloud und die Möglichkeiten, die sich im Zusammenspiel von Containern im Zusammenhang mit Microservices ergeben, sprechen für sich. Die Aufspaltung ehemals monolithischer Systeme in kleinere Einheiten, schafft im Gegenzug allerdings mehr Komplexität in den IT-Strukturen.
Die Absicherung beginnt bei Betriebssystem und Netzwerk-Host.
Ein gängiger Fehler besteht hier darin, sich zu stark auf den einzelnen Container zu fokussieren. Es ist notwendig, sich auch die Laufzeitumgebung genau anzusehen. Der Container selbst mag noch so sicher und gut abgesichert sein; das nützt nichts, wenn er über den Host kompromittiert werden kann. Jedes einzelne System, das eine Container-Plattform hostet, sollte auf jedem einzelnen und angreifbaren Layer abgesichert werden. Die Absicherung von Containern beginnt somit bei Betriebssystem und dem Netzwerk-Host.
Ein weiteres potenzielles Sicherheitsproblem beginnt bereits im Entwicklungsprozess selbst. Viel zu oft wird davon ausgegangen, dass die zur Entwicklung benutzten Code-Libraries sicher sind. Development-Umgebungen und Frameworks binden beim Start unzählige öffentliche Bibliotheken und externe Ressourcen ein. Selbst in einfachen Projekten greift das in Java-Umgebungen beliebte Spring Boot bereits auf 50 solcher Ressourcen zu. Die gesamte Infrastruktur und das Zusammenspiel unterschiedlicher Container und Cloud-Strukturen wird unter dem Aspekt der Systemsicherheit unübersichtlicher und damit anfälliger für Attacken und die Ausnutzung von Sicherheitslücken. Als besonders gefährlich können sich Libraries von Drittanbietern erweisen. Deren Einbindung spart zwar vordergründig Entwicklungsressourcen und damit Zeit, kann sich aber als extremes Sicherheitsrisiko erweisen.
Somit ist zu empfehlen, die Sicherheit bereits während des Entwicklungsprozesses stärker zu beachten. Eine Option ist hier die Einbindung der von der NIST bereitgestellten CVE-Datenbank, die es erlaubt, schon bei der Code-Entwicklung und im Build-Prozess auf Schwachstellen hinzuweisen. So kann überlegt werden, ob die Sicherheitslücke überhaupt eine Auswirkung hat, oder es wird nach Alternativen gesucht. Vorteil eines solchen "Static Application Security Testing" (SAST) ist, dass es bereits in einer frühen Phase verwendet werden kann, in der Code noch nicht vollständig ausführbar sein muss. Die Systemsicherheit und damit die Sicherheit des gesamten Unternehmens steigt, wenn sich die Zahl eingebundener Ressourcen auf das Mindestmaß beschränkt.
Ratsam ist darüber hinaus, augenscheinlich besonders gefährdete Container in einem kritischen Review von mehreren unabhängigen internen Instanzen im Unternehmen prüfen zu lassen.
Keinesfalls sollten externe Images ungeprüft in den Entwicklungsprozess übernommen werden. Denn hier erwachsen gleich in zweierlei Hinsicht mögliche Risiken. Dies betrifft einerseits die Codebasis. Nur weil ein Image öffentlich zur Verfügung gestellt wird, bedeutet das ja nicht, dass es vom Anbieter intensiv überprüft wurde. Wenn dieses Image dann die Basis für eigene Images für den Produktivbetrieb bilden sollte, ist es ratsam, es genauso wie eine eingehende Datei oder E-Mail auf Malware und Lücken zu überprüfen.
Das zweite Risiko ergibt sich sowohl bei der Nutzung externer Images wie dem Aufbau eigener Container. Die Zugriffsrechte und Privilegien des Systems sollten stets kritisch geprüft und hinterfragt werden. Unnötige Berechtigungen in der Host-Umgebung können so eskalieren, dass nicht nur der Container kompromittiert wird, sondern auch das gesamte Host-System. Während des Prozesses der Erstellung ist es nicht unüblich, in der Image-Spezifikation Root-Rechte zu vergeben, weil es teilweise den Prozess vereinfacht. Diese weitgehenden Rechte sind dann aber schnell vergessen und landen oftmals auf dem Produktivsystem. Auch vor diesem Hintergrund müssen externe Images, die verwendet werden sollen, genau überprüft werden. Generell sollte die Rechtevergabe in einem Container so granular wie möglich erfolgen. Idealerweise werden alle Befehle und Kommandos innerhalb eines Containers nur von unprivilegierten Benutzern ausgeführt.
Ein potenzieller Angriffsvektor ergibt sich schließlich aus einer zu großen Sichtbarkeit der Container und deren Kommunikationswege im Netz. Es sollen nur absolut notwendige Kommunikationskanäle geöffnet sein. Dabei gilt es, jede Interaktion zwischen dem Container, den Netzwerken, Prozessen oder gar externen Datenträgern kritisch in Augenschein zu nehmen. Die Frage lautet dabei stets, ob dies für das Funktionieren der Anwendung tatsächlich notwendig ist.
Wissen, was läuft – Monitoring in Container-Umgebungen
Eine Herausforderung in der Arbeit mit Container ist deren kontinuierliche Überwachung. Sie ist allerdings bereits unter zwei Gesichtspunkten notwendig.
Da ist zum einen der erwähnte Aspekt der Sicherheit. Bekanntlich werden täglich neue Sicherheitslücken entdeckt (und ausgenutzt). Mit anderen Worten ist das Image, das gestern noch aus Komponenten erstellt wurde, zu denen keine Sicherheitslücken bekannt waren, heute vielleicht bereits gefährdet. Und kann in letzter Konsequenz dann auch das Gesamtsystem gefährden, wenn es nicht wie beschrieben ausreichend isoliert ist. Somit ist es erforderlich, eine Container-Umgebung kontinuierlich mit einer Security-Lösung zu überwachen, die Anomalien und böswilligen Aktivitäten darin zuverlässig erkennt und darüber informiert.
Die Überwachung ist insofern anspruchsvoll, als dass in einer modernen IT-Umgebung eine "Anwendung" aus zahlreichen Containern und externen Komponenten bestehen kann. Für ein Operations-Team ist die rein manuelle Überwachung nicht zu leisten. Die gewählte Security- und Überwachungslösung sollte somit in der Lage sein, ausgehend von einem Policy-Framework die Container schnell zu identifizieren, die gegen die Sicherheitsrichtlinien verstoßen. Mittels Methoden des maschinellen Lernens lassen sich Anomalien heute auch frühzeitig entdecken. Auf diese Weise gelingt es möglicherweise, eine sich gerade erst herausbildende Attacke frühzeitig zu unterbinden.
Eine ausgereifte Container-Security und Überwachung verhindert faktisch, dass ein Container mit Sicherheitsschwachstellen bereitgestellt wird und identifiziert Container, die gefährdete Komponenten verwenden.
Nun geht es nicht allein darum, sichere Software zu bauen, sondern auch funktionierende Anwendungen, die die Erwartungen der Nutzer erfüllen. Container und Microservice-Architekturen bilden die Basis für unterschiedliche interne und externe Anwendungen. Das Monitoring muss somit den Zustand der Anwendung insgesamt aus allen notwendigen Perspektiven verstehen und überwachen. Das umfasst operative Kennzahlen (Work Metrics) und Werte, die die Ressourcen betreffen (Resource Metrics). Leistung und Verfügbarkeit aller Anwendungen innerhalb eines Containers müssen genauso geprüft werden, wie CPU- und Speicherverbrauch in Hinblick auf einen Maximalwert. Anforderungsraten, Datendurchsatz und Fehlerquote sind wichtige Kenngrößen. Genauso wichtig ist indes auch, die User-Experience im Blick zu behalten. Reaktions- und Ladezeiten und die Zugänglichkeit insgesamt entscheiden etwa im E-Commerce über die Akzeptanz einer Anwendung und somit den wirtschaftlichen Erfolg.
Bei der Anschaffung einer Überwachungslösung ist es somit ratsam, dass Daten aus Infrastruktur, Netzwerk, Anwendungen und IT-Security idealerweise an einer zentralen Stelle zusammenlaufen. Die Überwachung der Systemauslastung, das automatisierte Scannen auf Schwachstellen, ein Threat-Modelling beispielsweise auf Basis des MITRE ATT&CK-Frameworks werden hier ebenso berücksichtigt und zentralisiert dargestellt, wie die Informationen zur Systemgesundheit [2].
Die vielfach noch vorhandene Trennung in "Real User Monitoring" und "Application Performance Monitoring" sollte in Container-Umgebungen durch den Einsatz der passenden Tools überwunden und vereinheitlicht werden. Eine wichtige Voraussetzung besteht hier darin, dass alle Mitglieder eines Teams auf die gleichen Informationen zugreifen können, um so mit der Expertise aus Development und Security gemeinsam darauf zu schauen.
Weg zu mehr Automatisierung
Die Beliebtheit von Containern und Umgebungen wie Kubernetes sind das sichtbare Zeichen für einen Paradigmenwechsel in der IT. Dieser begann mit der Entwicklung agiler Vorgehensmodelle, die stark auf die Automatisierung setzen. Statt einzelne Komponenten oder Bibliotheken zu aktualisieren, werden neue Image-Dateien erstellt und als Ganzes automatisiert ausgerollt. Mit "Infrastructure as Code" und "Configuration as Code" sind Entwickler heute in der Lage, die Infrastruktur selbst zu provisionieren und zu skalieren. Das Konzept DevOps öffnet die Tür zu kontinuierlicher Bereitstellung und einfachen Rollbacks. Änderungen am Code einer Anwendung landen im Versionskontrollsystem und spiegeln sich schneller in der tatsächlichen Umgebung wider. Kubernetes und Container befriedigen technisch das Bedürfnis nach einer höheren Automatisierung.
Der Gewinn an Geschwindigkeit und Agilität sollte indes nicht zulasten der Sicherheit gehen. Je komplexer die Strukturen werden, umso schwerer haben es auch Sicherheitsexperten, sich hier einzubringen und das Gesamtsystem nachhaltig zu testen. Die aktuell zu beobachtende Entwicklung, die DevOps in Richtung DevSecOps transformiert, um dabei Sicherheitstests bereits in einem früheren Stadium der Entwicklung zu berücksichtigen (Shifting Left), trägt zu einer höheren Softwarequalität und Sicherheit bei. So kann "Static Application Security Testing" (SAST) bereits in einer frühen Phase verwendet werden, in der Code noch nicht vollständig ausführbar sein muss.
Containerisierung schafft zahlreiche Vorteile, wenn sie denn sinnvoll im Rahmen der gestellten Aufgabe eingesetzt wird. Sie unterstützt höhere Effizienz und Agilität durch das Automatisierungspotential. Sie stellt aber auch konkrete Herausforderungen in Hinblick auf die Systemsicherheit und somit gerade auch das Monitoring, das auf eine solche Umgebung optimal abgestimmt sein muss.