Stackable

Die Stackable Security Story

stackable-blog-header-hellgrau

Für das Thema Security hatten sowohl Lars als auch ich während unserer gesamten Beraterkarriere immer einen besonderen Platz in unserem Herzen reserviert. Vielleicht ist das „typisch deutsch“, aber wahrscheinlich lag es einfach daran, dass wir zu oft damit konfrontiert wurden, dass redundante Einstellungen nicht übereinstimmten, keine gemeinsamen Chiffren gefunden wurden oder eines von tausend anderen möglichen Sicherheitsproblemen uns Stunden an Recherche gekostet hat.

Als wir also begannen, die ersten Entwürfe und Ideen für die Stackable Data Platform, unsere quelloffene Datendistribution, niederzuschreiben, war eines der ersten Prinzipien, auf das wir uns einigten:

Security is a first class citizen, not an afterthought!

Für sich genommen ist das natürlich nur ein Satz ohne wirkliche Bedeutung. In diesem Blogpost möchte ich daher versuchen, unseren Worten Taten folgen zu lassen und Dir einige Details darüber zu liefern, was wir uns ausgedacht haben, um diesen Satz mit Leben zu füllen.

Securitymaßnahmen in einer Big-Data-Umgebung zu implementieren ist eine ziemlich komplexe Aufgabe, selbst wenn sie von einem Management-Tool wie Apache Ambari oder dem Cloudera Manager unterstützt wird.

Du musst viele TLS-Schlüssel und Zertifikate mit den korrekten Hostnamen erstellen, Service Principals und Keytabs im AD anlegen, ein zentrales Tool zur Durchsetzung von ACLs wie Apache Ranger oder Apache Sentry einrichten, die Tools für die Verwendung konfigurieren sowie die Authentifizierung in den Tools konfigurieren und ACLs einrichten, um Benutzer:innen Zugriff zu gewähren.

Für viele dieser Aufgaben gibt es einfach keinen Ersatz – Verschlüsselung ohne Schlüssel, das geht nun mal nicht. Wir nehmen Dir den Aufwand ab, diese Schlüssel, Zertifikate und Keytabs zu generieren und auszurollen sowie die ACL-Verwaltung zu übernehmen.

Für wen ist dieser Artikel gedacht?

Du musst kein Sicherheitsexperte sein, der stundenlang über die Vor- und Nachteile einzelner Verschlüsselungsalgorithmen diskutieren kann. Aber Du solltest prinzipiell mit Sicherheitskonzepten wie Authentifizierung und Autorisierung vertraut sein, da dieser Artikel nicht alles von Grund auf behandeln wird.

Authentifizierung

Bei der Authentifizierung wird sichergestellt, dass ein:e Benutzer:in der-/diejenige ist, der/die er/sie vorgibt, zu sein. Dazu gibt es eine Vielzahl von Möglichkeiten:

  • Benutzername und Passwort
  • TLS-Zertifikate
  • Kerberos-Tickets
  • Single-Sign-On-Mechanismen

Die Liste ließe sich endlos fortsetzen.

Das Hauptproblem bei der Authentifizierung ist, dass keine zwei Tools aus einem typischen Data-Tool-Stack die gleichen Authentifizierungsformen auf die gleiche Weise unterstützen. Jedes Tool hat seine eigene Vorstellung davon, wie ein:e Benutzer:in identifiziert werden sollte. Und da diese Benutzeridentifikation auch von den Client-Tools unterstützt werden muss, ist sie in der Regel nicht erweiterbar, was bedeutet, dass es nicht einfach ist, benutzerdefinierte Implementierungen zu erstellen, die das Hinzufügen neuer Mechanismen ermöglichen.

Für die Authentifizierung müssen wir uns also auf das beschränken, was die Tools von Haus aus bieten. Aber das ist eigentlich kein großes Problem, denn das Wichtigste, was wir vom Authentifizierungsschritt erwarten, ist ein Benutzername, dem wir vertrauen können – und das wird meistens einfach ein String sein. Ich werde in einem zukünftigen Blog-Beitrag mehr darüber schreiben, wie wir dies implementiert haben.

Autorisierung

Gut, jetzt haben wir einen Benutzernamen, dem wir vertrauen können, weil er mit einer Methode nachgewiesen wurde, die wir für sicher halten – was nun? An dieser Stelle kommt die Autorisierung ins Spiel, die diesen Benutzernamen nimmt und eine Reihe von Regeln gegen ihn durchsetzt. In der einfachsten Form könnte das so aussehen

tom darf aus Tabelle-Transaktionen lesen

Oder ein bisschen komplexer

tom darf die Spalten Betrag und Datum aus der Tabelle Transaktionen in den Zeilen lesen, in denen das Feld customer_id den Wert 123 hat

Sieht im Prinzip recht einfach aus und wenn es sich nur um ein einzelnes Produkt handeln würde, wäre es in der Tat auch nicht sonderlich schwer. Alle Tools aus unserem Stack unterstützen zumindest eine einfache ACL-Implementierung, einige von ihnen verfügen sogar über recht komplexe Systeme dafür.

Aber das ist genau das Problem, das wir mit dem Sicherheitsdesign für die Stackable Data Platform lösen wollen: Es sollten nicht 12 verschiedene Autorisierungssysteme sein, die sich alle unterschiedlich anfühlen, sondern ein System, das sich auf der gesamten Plattform gleich anfühlt und funktioniert. Tatsächlich war dies bei der Entwicklung unserer gesamten Plattform immer unser zentraler Grundsatz, etwas zu entwickeln, das sich über mehrere Tools hinweg so „gleich“ wie möglich anfühlt.

Um dieses Ziel zu erreichen, verwenden wir den Open Policy Agent (OPA) als zentrales Puzzlestück. OPA ist ein Tool, das die Implementierung komplexer Autorisierungsregeln auf der Grundlage nahezu beliebiger Eingabedaten ermöglicht. Die Sprache, die zur Definition dieser Regeln verwendet wird, heißt rego, die ihrerseits auf Datalog basiert.

Der Hauptvorteil von OPA gegenüber bestehenden Lösungen wie Ranger besteht darin, dass die gesamte Logik in den Regeln selbst gehalten werden kann, was bedeutet, dass es sehr einfach ist, Regelsätze zu erweitern, ohne dass Code geschrieben und veröffentlicht werden muss.

Gruppen-Suche

Erinnerst Du dich an die Beispielrichtlinien, die wir uns oben angesehen haben? In der Praxis wirst Du nur selten tatsächliche Benutzernamen in ACLs finden – was Du normalerweise tun möchtest, ist etwas wie das Folgende:

Benutzer, die Mitglied der Gruppe reporting sind, dürfen die Spalten amount und date aus der Tabelle transactions in den Zeilen lesen, in denen das Feld customer_id gleich 123 ist.

Das bedeutet, dass wir einen Weg finden müssen, um Gruppen für Benutzer:innen aus einer Art von Verzeichnisdienst zu suchen, meistens LDAP oder AD.

Häufig erfolgt die Suche nach Gruppen für eine:n Benutzer:in während des Authentifizierungsschritts. Die abgerufenen Informationen werden dann zusammen mit dem Benutzerprinzipal weitergegeben. Dies hat den großen Nachteil, dass der Code zur Durchführung dieser Suche auf mehrere Tools verteilt wird.

Idealerweise wollten wir, dass die Gruppensuche als Teil der Regelauswertung in OPA durchgeführt wird, was uns erlauben würde, dies einmal von einer zentralen Komponente aus zu tun und dieselben Gruppen in Regeln für alle Produkte, die im Rahmen unserer Plattform verwaltet werden, wiederzuverwenden – was auch eine effiziente Zwischenspeicherung ermöglicht.

OPA ermöglicht so etwas mit dem Konzept der externen Daten. Die Idee ist, dass Du entweder externe Daten, die für die Autorisierung erforderlich sind, in die Bündel einbetten kannst, die einen OPA-Regelsatz bilden. Oder, dass Du externe Daten während der Auswertung der Regeln abrufen kannst.

Beides hat offensichtliche Vor- und Nachteile: Die Einbettung von Benutzer-Rollen-Zuweisungen in die Regeln wird umständlich, wenn es viele Benutzer:innen und viele Gruppen gibt und sich die Zuweisungen häufig ändern. Andererseits ist es auch keine gute Option, ein externes System für Zugriffsanfragen aufzurufen. Die gute Nachricht an dieser Stelle ist, dass dieser Teil des Entwurfs nicht festgeschrieben werden muss. Mehrere Implementierungen können bei Bedarf leicht erstellt werden und in unserem Code nebeneinander bestehen – der/die Kund:in muss nur die beste Lösung für die Umgebung konfigurieren.

Das folgende Diagramm zeigt den ursprünglichen Fluss, den wir für die gruppenbasierte Autorisierung implementieren:

Für unsere anfänglichen Implementierungen würden wir also etwas erhalten, das in etwa der folgenden Darstellung entspricht:

Der Stackable Policy Server ist in diesem Fall die zentrale Komponente, die für die Zwischenspeicherung von Benutzer-Rollen-Zuordnungen und anderen relevanten Daten aus einem zentralen Verzeichnisdienst zuständig ist.

Autorisierungsregeln

Bei allem, was wir bisher betrachtet haben, sind wir immer davon ausgegangen, dass die Berechtigungsregeln einfach da sind und auf Benutzeranfragen angewendet werden können. Diese Regeln entstehen jedoch normalerweise nicht aus dem Nichts, sondern müssen von jemandem definiert und – was noch wichtiger ist – dem System zur Durchsetzung übergeben werden.

Wie wir in der OPA-Einführung gesehen haben, können Regeln sehr komplex werden und erlauben es, den größten Teil der Autorisierungslogik auf diese Regeln zu verlagern. Dies hat den Vorteil, dass der Code, der zum Zeitpunkt der Durchsetzung ausgeführt werden muss, vereinfacht wird – mit dem Nachteil, dass die Regeln dadurch komplexer werden.

Da dies nicht im Einklang mit unserem Ziel steht, die Plattform benutzerfreundlich zu gestalten, werden wir produktspezifische Abstraktionen schaffen, die es unseren Benutzer:innen ermöglichen, ihre Autorisierungsanforderungen auf eine viel einfachere Weise auszudrücken. Natürlich handelt es sich hierbei wie üblich um eine optionale Abstraktionsschicht. Wenn die Benutzer:innen das Gefühl haben, dass sie mehr Kontrolle benötigen, kann der gesamte Regelsatz auch manuell geschrieben werden.

Wir werden die Abstraktionsschicht als benutzerdefinierte Ressourcen in Kubernetes darstellen, genau wie die Definition der Plattform selbst. Das hat den großen Vorteil, dass wir die Definition der Autorisierungsregeln in der Versionskontrolle zusammen mit den yaml-Dateien der Architekturdefinition dort aufbewahren können, wo sie unserer Meinung nach hingehören.

Ein zusätzlicher Vorteil dieses Ansatzes besteht darin, dass wir Regelsätze sehr einfach kombinierbar machen können, was zum Beispiel die Wiederverwendung von Regeln in verschiedenen Umgebungen ermöglicht.

Stell Dir vor, Du definierst eine Regel in der Entwicklungsumgebung und kannst dieselbe Regel nach dem Testen in die Test- und Produktivumgebung migrieren. Du musst keine Tippfehler mehr in ACLs in der Entwicklungsumgebung finden, nachdem in der Testumgebung alles einwandfrei funktioniert hat … nicht, dass mir das jemals passiert wäre, natürlich!

Das folgende Diagramm soll eine Vorstellung davon vermitteln, wie wir dies im Prinzip umsetzen wollen. Viele Details sind noch in der Schwebe, da wir dies gerade implementieren, aber die prinzipielle Idee sollte klar werden.

Zusammenfassung

Als aufmerksame:r  Leser:in hast Du es vielleicht schon bemerkt: Nichts von dem, was ich in diesem Blog-Beitrag geschrieben habe, ist radikal neu oder unterscheidet sich grundlegend von dem, was es bisher bei der Sicherung von Clustern gegeben hat.

Was wir mit diesem Entwurf tun, ist, den gesamten Prozess integrierter und konsistenter zu gestalten, was wiederum die Nutzung für alle einfacher macht. Und Systeme, die einfacher zu bedienen und zu überprüfen sind, sind in der Regel auch sicherer. Durch die Integration von ACL-Definitionen in die eigentliche Infrastrukturdefinition und die Anwendung des „as-code“-Prinzips wird die konsistente Verwaltung des Zugriffs über mehrere Umgebungen hinweg wesentlich einfacher. Durch die Zentralisierung des Prozesses zur Anreicherung der Benutzeridentität verringert sich die Anzahl der Anwendungen, die in zentralisierte Verzeichnisse integriert werden müssen.

Wenn Du irgendwelche Kommentare oder Gedanken zu diesem Thema hast, dann melde Dich bitte, wir diskutieren gerne und sind immer offen für neue Ideen!

Und wenn Du daran interessiert bist, bei der Entwicklung von all dem mitzuhelfen: Wir suchen noch Leute für unser Team!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Copy link
Powered by Social Snap