Blog
Kubernetes-Secret-Verwaltung im großen Maßstab (II): Über „SealedSecrets“ hinaus – mit Azure Key Vault

Im vorigen Kapitel haben wir erläutert, wie wir die Speicherung von Geheimnissen im Klartext in unseren GitHub-Repositorys eingestellt und stattdessen mit der Verwendung von SealedSecrets begonnen haben. Diese Änderung löste unser dringendstes Problem: Die tatsächlichen Werte waren für niemanden mehr einsehbar, der über Lesezugriff auf den Code verfügte. Eine Zeit lang reichte dies aus.
Doch wenn eine Lösung täglich von verschiedenen Teams und in unterschiedlichen Clustern genutzt wird, treten nach und nach kleinere Probleme auf. In unserem Fall traten diese alle gleichzeitig auf: immer dann, wenn ein Geheimnis angelegt, erneuert oder rotiert werden musste. Der Kubeseal-Client funktionierte unter Windows nicht einwandfrei und stand im Konflikt mit unseren Sicherheitskontrollen. Der Zugriff auf die Cluster war auf das interne Unternehmensnetzwerk beschränkt, sodass nur wenige Personen einen neuen Wert versiegeln konnten, und einige von ihnen mussten dafür Ad-hoc-Tools verwenden, die innerhalb des Clusters liefen. Zudem markierten die Code-Review-Bots die versiegelten Werte immer wieder als offengelegte Geheimnisse, obwohl diese verschlüsselt waren. Keines dieser Probleme stellte für sich genommen ein Sicherheitsrisiko dar, doch in ihrer Gesamtheit verwandelten sie eine einfache Passwortänderung in ein kleines Projekt.
Am Ende des ersten Kapitels haben wir festgehalten, was wir von der nächsten Lösung erwarteten. Wir wollten Geheimnisse von einem zentralen Ort aus verwalten, mit Tools, die nicht von einem bestimmten Betriebssystem abhängig sind. Nur Personen mit höheren Berechtigungen sollten in der Lage sein, diese zu verwalten. Die Erstellung und Rotation von Geheimnissen in der Produktion sollte so weit wie möglich automatisiert werden. Und die Repositorys sollten keine Geheimnisse mehr enthalten, auch nicht in verschlüsselter Form.
In diesem Artikel geht es um die Diskussionen, die wir danach geführt haben. Es geht noch nicht um Tools. Es geht darum, wie wir uns entschieden haben, das Problem anzugehen, und um ein Detail, das wir zu Beginn unterschätzt haben: die Zugangsdaten, die zum Auslesen der Geheimnisse erforderlich sind.
Die Frage neu formulieren
Bei SealedSecrets lautete die Frage, die wir beantwortet hatten: „Wie können wir Geheimnisse sicher in Git speichern?“. Als wir begannen, die neuen Anforderungen zu analysieren, wurde uns klar, dass die letzte davon alles veränderte. Wenn die Repositorys keine Geheimnisse enthalten sollten – auch keine verschlüsselten –, dann stellte sich eine andere Frage: Wie können wir Geheimnisse außerhalb von Git aufbewahren und sie dennoch in den Clustern bereitstellen, wenn die Anwendungen sie benötigen?
Dies war für uns keine geringfügige Änderung. Unser DevOps-Team arbeitet nach dem GitOps-Prinzip, und ArgoCD nutzt die Informationen in den Repositorys als einzige verlässliche Quelle für die bereitgestellten Inhalte. Wie wir im ersten Kapitel erläutert haben, war dies einer der Gründe, warum wir die Secrets nicht einfach manuell in den Clustern erstellen konnten. Alles, was außerhalb von Git erstellt wurde, könnte von ArgoCD als nicht synchronisiert erkannt werden und verloren gehen.
Die Lösung bestand darin, zwei Dinge voneinander zu trennen, die bis dahin stets miteinander verbunden waren: die Definition eines Secrets und dessen Wert. Git kann weiterhin beschreiben, welches Geheimnis eine Anwendung benötigt, in welchem Namensraum es vorhanden sein muss und welche Komponente es verwenden wird. Das ist Konfiguration, und es ist völlig in Ordnung, diese in einem Repository zu speichern, wo sie wie der restliche Code überprüft und versioniert wird. Was Git nicht wissen muss, ist das Passwort selbst.
Somit hätten wir zwei Wahrheitsquellen statt einer. Git für den gewünschten Zustand der Infrastruktur und der Anwendungen sowie einen externen Secret-Speicher für die sensiblen Werte. Das mag wie ein Widerspruch zu GitOps klingen, ist es in der Praxis jedoch nicht. ArgoCD stellt weiterhin genau das bereit, was im Repository definiert ist, und das Repository beschreibt weiterhin die vollständige Bereitstellung. Es verweist lediglich auf den Wert, anstatt ihn selbst zu enthalten.
Es gab noch einen weiteren Vorteil, über den wir im Team ausführlich gesprochen haben. Bei diesem Modell muss die Person, die eine Anwendung bereitstellt, das Datenbankpasswort nicht kennen, und die Person, die dieses Passwort wechselt, muss kein Helm-Chart bearbeiten. Jedes Mal, wenn ein Secret kopiert, in eine Datei eingefügt oder in ein Terminal eingegeben wird, besteht eine neue Gefahr, einen Fehler zu machen oder es versehentlich offenzulegen. Diese Risiken zu minimieren, war für uns ebenso wichtig wie die Verschlüsselung selbst.
Ein zentraler Tresor: Azure Key Vault
Nachdem wir uns darauf geeinigt hatten, dass die Werte außerhalb von Git gespeichert werden sollten, stellte sich als Nächstes die Frage, wo. Die Antwort war recht einfach. Unsere Plattform läuft auf Azure, alle unsere Anwendungen werden in AKS-Clustern bereitgestellt, und Azure Key Vault stand im Unternehmen bereits zur Verfügung. Nach einigen Diskussionen im Team beschlossen wir, diesen Dienst als zentralen Speicherort für alle unsere Geheimnisse zu nutzen.
Key Vault bot uns das, was SealedSecrets nicht leisten konnte. Der Zugriff wird über Azure gesteuert, und zwar mit demselben Identitäts- und Berechtigungsmodell, das wir bereits für den Rest der Infrastruktur verwenden, sodass wir genau festlegen können, wer die einzelnen Geheimnisse lesen oder ändern darf. Es muss kein Client auf dem Laptop des Benutzers installiert werden, wodurch die Probleme mit Windows entfallen. Und die Rotation wird zumindest theoretisch wesentlich einfacher: Der Wert wird an einer einzigen Stelle geändert, anstatt für jeden Cluster einen neuen versiegelten Wert zu generieren und diesen in mehrere Repositorys zu übertragen.
Dieses „theoretisch“ ist wichtig. Die Speicherung der Geheimnisse in Key Vault löst das Problem, wo sie gespeichert werden. Es löst jedoch nicht das Problem, wie sie zu den Pods gelangen.
Wie gelangen die Werte zu den Clustern?
Dies war der längste Teil unserer Diskussionen. Bevor wir uns konkrete Tools ansahen, versuchten wir zu verstehen, welche Fragen die Lösung beantworten musste, denn wir hatten bereits bei SealedSecrets gelernt, dass ein Tool, das anfangs vielversprechend erscheint, später viel Arbeit verursachen kann.
Die erste Frage lautete, wer den Wert in den Cluster einbringt. Eine Möglichkeit besteht darin, dass der Wert von außen während der Bereitstellung durch denselben Prozess eingefügt wird, der auch die Manifeste anwendet. Die andere Möglichkeit ist, dass eine innerhalb des Clusters ausgeführte Komponente den Wert aus Key Vault ausliest und lokal erstellt. Die erste Variante ist einfach und passt gut zu GitOps, macht die Verwaltung von Secrets jedoch vom Bereitstellungswerkzeug abhängig. Die zweite Variante ist unabhängiger, bedeutet jedoch, dass jeder Cluster in der Lage sein muss, selbstständig mit Key Vault zu kommunizieren.
Die zweite Frage betraf die Art und Weise, wie Anwendungen den Wert nutzen. Die meisten unserer Anwendungen – insbesondere Produkte von Drittanbietern wie Redis, MongoDB, PostgreSQL oder Superset – werden mit Helm-Charts bereitgestellt, die ein Standard-Kubernetes-Secret erwarten. Eine Lösung, die Secrets auf andere Weise bereitstellt – beispielsweise als in einem Volume eingebundene Dateien –, könnte uns dazu zwingen, diese Charts anzupassen, und genau das wollten wir vermeiden, da dies zukünftige Upgrades erschwert.
Die dritte Frage lautete, was nach einer Rotation geschieht. Die Aktualisierung eines Werts in Key Vault ist einfach. Auch die Aktualisierung des Kubernetes-Secrets ist möglich. Viele Anwendungen lesen ihre Konfiguration jedoch nur beim Start des Containers ein, sodass ein bereits laufender Pod weiterhin das alte Passwort verwendet. Würde die Lösung dies nicht berücksichtigen, wäre bei jeder Rotation weiterhin ein manueller Neustart erforderlich.
Die letzte Frage betraf die Skalierbarkeit. Es geht hier nicht um eine einzige Anwendung in einem einzigen Cluster. Wir verfügen über mehrere AKS-Cluster, von denen jeder viele Namespaces und viele Anwendungen umfasst. Jeder Schritt, der für jeden Namespace oder Cluster manuell wiederholt werden muss, ist ein Schritt, der irgendwann vergessen wird.
Diese vier Fragen wurden zu unseren Kriterien für die Bewertung jedes Tools.
Das Geheimnis hinter den Geheimnissen
Es gab noch eine weitere Frage, und es dauerte eine Weile, bis uns klar wurde, wie wichtig sie war.
Um ein Geheimnis aus Azure Key Vault auszulesen, muss sich jede Komponente zunächst authentifizieren. In unserem Fall bedeutet dies Anmeldedaten, beispielsweise eine Dienstinstanz mit Berechtigungen für den Tresor. Und diese Anmeldedaten sind natürlich ebenfalls ein Geheimnis. Bevor der Cluster also die Geheimnisse abrufen kann, benötigt er ein Geheimnis, um die Geheimnisse abzurufen. Wir haben begonnen, dies als „Bootstrap-Problem“ zu bezeichnen.
Das erste Geheimnis kann nicht mit dem Mechanismus abgerufen werden, der davon abhängt. Wenn ein Tool Geheimnisse aus dem Key Vault synchronisiert, kann es keine eigenen Anmeldedaten für den Zugriff auf den Key Vault erstellen, da es diese benötigt, bevor es überhaupt etwas tun kann. Diese müssen von einer anderen Stelle bereitgestellt werden.
In einem einzelnen Cluster erscheint dies als nebensächliches Detail. Ein Administrator legt die Anmeldedaten einmalig manuell an, und ab diesem Zeitpunkt übernimmt die Synchronisierung. Kubernetes-Secrets sind jedoch auf den Namespace beschränkt: Ein in einem Namespace erstelltes Secret kann nicht von einem anderen Namespace aus referenziert werden. Wenn die Anmeldedaten also in zehn Namespaces benötigt werden, müssen sie zehnmal vorhanden sein. Bei mehreren Clustern vervielfacht sich diese Zahl erneut.
Und dann laufen die Zugangsdaten ab oder müssen aufgrund von Sicherheitsrichtlinien ausgetauscht werden. Jede Kopie muss in jedem Namensraum und in jedem Cluster manuell aktualisiert werden. Genau diese Art von Arbeit wollten wir beseitigen, als wir SealedSecrets verlassen haben – nur dass sie nun an einem Ort verborgen ist, an dem man sie leicht vergisst. Ein zentraler Speichersort für Geheimnisse sollte Doppelarbeit reduzieren und nicht eine neue Reihe von Kopien schaffen, die niemand verwaltet.
Beim Bootstrap-Problem geht es also nicht nur um den ersten Systemstart. Es geht um den gesamten Lebenszyklus der Zugangsdaten, die das gesamte System erst ermöglichen. Wir haben zudem akzeptiert, dass sich dieses Problem nicht vollständig beseitigen lässt. Irgendwann muss es eine erste Vertrauensbeziehung geben, die von einer privilegierten Person hergestellt wird. Unser Ziel war es, diesen manuellen Schritt auf ein Minimum zu reduzieren – im Idealfall auf einen einzigen Moment – und alles, was danach folgt, automatisch zu verwalten.

Was wir suchten
Am Ende dieser Analyse hatten wir ein viel klareres Bild von der Architektur, die wir uns wünschten, noch bevor wir uns für ein Tool entschieden hatten.
Git würde die Anwendungen definieren und festlegen, welche Geheimnisse die jeweilige Anwendung benötigt, jedoch niemals die Werte selbst. Azure Key Vault würde alle sensiblen Informationen speichern, einschließlich der für den Zugriff erforderlichen Anmeldedaten. Ein Bootstrap-Mechanismus würde diese Anmeldedaten in den Clustern bereitstellen und auf dem neuesten Stand halten. Ein Synchronisationsmechanismus würde die Anwendungsgeheimnisse aus dem Key Vault auslesen und standardmäßige Kubernetes-Secrets in den entsprechenden Namespaces erstellen. Und ein Mechanismus würde sicherstellen, dass laufende Anwendungen nach einer Rotation einen neuen Wert verwenden, ohne dass jemand sie manuell neu starten muss.
Was uns noch fehlte, waren die entsprechenden Werkzeuge. Und in einem Unternehmen wie dem unseren ist die Wahl nicht völlig frei. Sicherheitsrichtlinien, Support und die Erfahrungen anderer Teams haben einen starken Einfluss, und da eine gewisse Dringlichkeit bestand, bald eine Lösung zu finden, wollten wir nichts von Grund auf neu entwickeln. Innerhalb des Unternehmens gab es drei Optionen, die bereits genehmigt waren und in anderen Projekten zum Einsatz kamen: das ArgoCD-Vault-Plugin, den Secrets-Store-CSI-Treiber und den External-Secrets-Operator.
Im nächsten Kapitel werden wir erläutern, inwiefern die einzelnen Komponenten in dieses Gesamtbild passten – oder eben nicht passten – und welche Kombination wir schließlich in die Produktion übernommen haben.
Verfasst von
Contact














