Kafka in Produktion: von null zum Profi (Teil 3)

Kafka in Produktion – wir bauen einen skalierbaren, sicheren und Enterprise-tauglichen Message Bus auf der Google Cloud Platform
opensight.ch - roman hüsler
Inhaltsverzeichnis
- Teil 1 – Kafka-Einzelinstanz mit Docker
- Teil 2 – Kafka-Einzelinstanz mit Authentifizierung (SASL)
- Teil 3 – Kafka-Cluster mit TLS-Verschlüsselung, Authentifizierung und ACLs
- Teil 4 – Enterprise-Authentifizierung und Zugriffskontrolle (OAuth 2.0)
- Teil 5 – Produktiver Kafka-Cluster auf Google Cloud mit Strimzi
- Teil 6 – Observability und Data Governance mit Strimzi (folgt)
TL;DR
Ich empfehle zwar, die Blogposts zu lesen, aber ich weiss, dass manche direkt zur «Single Source of Truth» springen: zum Code. Das ist ungefähr so, wie an einem Freitag ungetestet zu deployen.
Bitte sehr:
Einführung
Apache Kafka ist eine leistungsfähige Event-Streaming-Plattform, doch der Betrieb in Produktion ist eine ganz andere Herausforderung. Wir beginnen mit den Grundlagen, aber das hier ist kein Spielplatz: Schritt für Schritt bauen wir einen Message Bus in Enterprise-Qualität, der Hochverfügbarkeit, Sicherheit und Skalierbarkeit für echte Workloads bietet.
Ich bin selbst kein Kafka-Spezialist, ich lerne unterwegs. Wie so oft bei den opensight-Blogs können Sie mich auf meinem Weg von null zum Profi begleiten.
In dieser Blogserie gehen wir mit Kafka über die Grundlagen hinaus. Wir entwerfen ein produktionsreifes Kafka-Cluster-Deployment, das folgende Eigenschaften hat:
- Skalierbar – bewältigt wachsende Last ohne Performance-Engpässe.
- Sicher – setzt Authentifizierung (Plain, SCRAM, OAUTH), Autorisierung (ACL, OAUTH) und Verschlüsselung (TLS) durch.
- Sauber strukturiert – inklusive Schema Registry für eine ordentliche Data Governance.
- Enterprise-tauglich – mit den Funktionen, die eine ereignisgesteuerte Architektur in einer grossen Umgebung braucht.
Kafka-Cluster mit TLS-Verschlüsselung, Authentifizierung und ACLs
In diesem Teil der Blogserie bauen wir einen Kafka-Cluster in einer Laborumgebung auf. Der Einfachheit halber läuft der ganze Cluster auf einem einzigen Docker-Host. In einem produktiven Setup sollten Sie die einzelnen Komponenten jedoch auf mehrere virtuelle Maschinen (VMs) verteilen, um Skalierbarkeit, Redundanz und Ausfallsicherheit zu gewährleisten. In einem der nächsten Blogs setze ich einen echten Kubernetes-Cluster auf GKE auf.
Die Broker-Ports wirken vielleicht ungewöhnlich: 19093, 29093 und 39093. Das ist aber Absicht. So können alle Broker ohne Portkonflikte auf einem einzigen Testhost laufen. Mit getrennten Portbereichen arbeitet jeder Broker unabhängig, ohne mit Standardports oder anderen Diensten auf dem System zu kollidieren. Natürlich ist dieses Setup nur zum Testen gedacht: In Produktion verwenden Sie mehrere VMs, auf denen der Kafka-Broker jeweils auf demselben Port läuft.

Authentifizierung mit Client-Zertifikaten (mTLS)
Für die Authentifizierung verwenden wir Mutual TLS (mTLS): Um eine Verbindung zu den Kafka-Brokern aufzubauen, braucht ein Client ein Client-Zertifikat, das von unserer Certificate Authority (CA) ausgestellt wurde. Anders als bei Kafkas SASL-Authentifizierungsschicht stützt sich dieser Ansatz ausschliesslich auf die Validierung von SSL-(TLS-)Zertifikaten. Stammt das Zertifikat eines Clients nicht von unserer CA, schlägt der TLS-Handshake fehl und der Client kann nicht mit den Brokern kommunizieren. SASL ist in diesem Setup nicht beteiligt.
Unsere Kafka-Broker-Konfiguration umfasst:
- SSL-(TLS-)Verschlüsselung für eine sichere Kommunikation.
- Mutual-TLS-(mTLS-)Authentifizierung, erzwungen über
ssl.client.auth: required. - Kommunikation zwischen den Brokern über SSL, sodass der gesamte Verkehr zwischen den Kafka-Nodes verschlüsselt bleibt.
Späteres Upgrade: SASL_SSL mit OAuth
In einem späteren Deployment wechseln wir auf SASL_SSL mit OAuth. Damit kommt eine tokenbasierte Authentifizierung hinzu, während die SSL-Verschlüsselung erhalten bleibt. So lässt sich ein OAuth-Identity-Provider (z. B. Keycloak oder Okta) für Authentifizierung und Autorisierung anbinden, was das System in einer Produktionsumgebung besser skalierbar und einfacher verwaltbar macht.
Zertifikate erzeugen

Zuerst erzeugen wir ein CA.crt mit privatem Schlüssel, mit dem wir alle weiteren Zertifikate ausstellen.
- kafka.crt
Zertifikat der Kafka-Broker, um die Kommunikation zu verschlüsseln und sich gegenseitig zu authentifizieren - kafka-ui.crt
Zertifikat, mit dem sich KafkaUI mit einem Kafka-Broker verbindet - client-1.crt
Zertifikat für einen beliebigen «client-1», der sich mit Kafka verbindet - client-2.crt
Zertifikat für einen beliebigen «client-2», der sich mit Kafka verbindet
# generate all required certificates
./generate_certs.sh changeit Befehl – alle benötigten Zertifikate per Skript erzeugen
Alle Schlüssel erzeugt dieses Skript im Git-Repository automatisch. Java wird benötigt, weil wir mit keytool einen Java-Keystore erstellen. Nachdem alle Zertifikate erzeugt sind, sieht das so aus:

Deployment
Um das ganze Setup zu starten, führen Sie Folgendes aus:
# start the kafka cluster on docker (and generate certs)
make run

Das sind die wichtigsten Konfigurationsparameter für dieses Setup:
advertised.listeners:SSL://kafka-1:19093 advertised.listeners:SSL://kafka-2:29093 advertised.listeners:SSL://kafka-3:39093
(Doku) Die Broker laufen jetzt mit SSL-(TLS-)Verschlüsselung auf Port 9093.security.inter.broker.protocol:SSL
(Doku) Für die Kommunikation mit einem Kafka-Broker ist jetzt SSL-(TLS-)Verschlüsselung Pflicht.ssl.keystore.filename:server.keystore.jks
(Doku) Alternativ lässt sichssl.keystore.locationverwenden, dann muss aber meines Wissens der vollständige Dateipfad angegeben werden. Als Keystore dient der zuvor erzeugte Server-Keystore, der das ca.crt sowie den privaten Schlüssel und das Zertifikat (kafka.crt) für den Betrieb der Kafka-Broker enthält. Dieser Keystore ist zentral, um die Kommunikation zwischen den Kafka-Brokern per SSL/TLS abzusichern. Übrigens:ssl.keystore.credentialssind die Zugangsdaten für den Keystore; sie liegen in der Datei certs/credentials und werden in den Container gemountet.ssl.client.auth:required
(Doku) Clients müssen sich per SSL (TLS) mit einem Client-Zertifikat authentifizieren.authorizer.class.name:kafka.security.authorizer.AclAuthorizer
(Doku) In diesem Abschnitt schauen wir uns Access Control Lists (ACLs) in Apache Kafka an. Mit ihnen legen Sie fest, welche Clients auf welche Topics zugreifen dürfen. Über ACLs setzen Sie feingranulare Sicherheitsrichtlinien durch, sodass nur berechtigte Benutzer oder Applikationen aus Kafka-Topics lesen oder in sie schreiben können.principal.builder.class:org.apache.kafka.common.security.authenticator.DefaultKafkaPrincipalBuilder
(Doku) Diesen Principal Builder habe ich zuerst verwendet. Er konnte aber den eigentlichen Benutzernamen nicht aus dem Zertifikat (CN=kafka) extrahieren, sondern lieferteCN=kafkastattkafka. Deshalb habe ich schliesslichssl.principal.mapping.rules:RULE:^CN=(.*?)$$/$$1/Lergänzt. Damit wird der Benutzername korrekt extrahiert.super.users:your_super_users
(Doku) Sie können im Setup auch Superuser definieren, die keine ACLs benötigen.
Zugriffskontrolle
Ein Blick in KafkaUI zeigt, dass es kein neues Topic im Cluster anlegen kann. Das ist zu erwarten: KafkaUI verbindet sich mit dem erzeugten kafka-ui.crt, aber der Benutzer kafka-ui (CN=kafka-ui) hat in den Access Control Lists (ACL) noch nicht die nötigen Berechtigungen.

Jetzt passen wir die Access Control List (ACL) an und geben Kafka-UI die nötigen Berechtigungen, um den Kafka-Cluster zu verwalten.
# Give Kafka-UI full access to all topics
docker exec -it kafka-1 kafka-acls --authorizer-properties zookeeper.connect=zookeeper-1:2181 \
--add --allow-principal "User:kafka-ui" --operation All --topic '*'
# Allow Kafka-UI full access to the Kafka cluster
docker exec -it kafka-1 kafka-acls --authorizer-properties zookeeper.connect=zookeeper-1:2181 \
--add --allow-principal "User:kafka-ui" --operation All --cluster
# restart kafka ui
docker restart kafka-uiBefehl – dem Benutzer kafka-ui die nötigen Berechtigungen erteilen
Legen wir auch die nötigen Berechtigungen für die Test-App an. Sie verwendet das erzeugte client-1.crt mit dem Common Name CN=client-1.
# grant client-1 permissions to create test-topic
docker exec -it kafka-1 kafka-acls --authorizer-properties zookeeper.connect=zookeeper-1:2181 \
--add --allow-principal "User:client-1" --operation Create --topic test-topic
# grant client-1 permissions to read/write test-topic
docker exec -it kafka-1 kafka-acls --authorizer-properties zookeeper.connect=zookeeper-1:2181 \
--add --allow-principal "User:client-1" --operation Read --operation Write --topic test-topic
# grant client-1 permissions to read the test group
docker exec -it kafka-1 kafka-acls --authorizer-properties zookeeper.connect=zookeeper-1:2181 \
--add --allow-principal "User:client-1" --operation Read --group test-group
# grant client-1 permissions to describe the test group
docker exec -it kafka-1 kafka-acls --authorizer-properties zookeeper.connect=zookeeper-1:2181 \
--add --allow-principal "User:client-1" --operation Describe --group test-groupBefehl – client-1 die nötigen Berechtigungen erteilen
Testen
Das Deployment ist abgeschlossen, also Zeit für einige Tests mit der Test-Applikation. Sie finden die Test-App im GitHub-Repository.
Alles scheint korrekt zu funktionieren. Jetzt können Sie mit den Access Control Lists (ACLs) experimentieren und prüfen, dass Benutzer ohne die nötigen Berechtigungen nicht mehr auf den Kafka-Cluster zugreifen können. So stellen Sie sicher, dass die Zugriffsbeschränkungen tatsächlich greifen.

Fazit
Wir kommen gut voran! Jetzt haben wir einen Kafka-Cluster mit Verschlüsselung, Authentifizierung, Zugriffskontrolle und Skalierbarkeit.
Im kommenden Teil 4 dieser Serie konzentriere ich mich auf Funktionen für den Enterprise-Einsatz, konkret auf OAuth-Authentifizierung und OAuth-basierte Berechtigungen. Diese Erweiterungen stärken die Sicherheit weiter und vereinfachen die Verwaltung der Zugriffe von Benutzern und Applikationen. Bleiben Sie dran!