Dieser Artikel ist eine bearbeitete Fassung meines Vortrags auf der Kafka Utrecht Meetup 2026. Danke an Axual für das Hosting des Meetups und die Aufnahme.
Kafka ist fantastisch. Es ist schnell, skalierbar und zuverlässig. Aber Kafka hat auch zwei Eigenschaften, die dir das Leben schwer machen können: Kafka ist faul und Kafka ist alles egal.
Kafka betrachtet Nachrichten als reine Byte-Arrays. Es kümmert sich nicht darum, was in diesen Bytes steht. Das ist gut für die Performance, aber schlecht für die Datenqualität. Wenn ein Producer Müll sendet, bekommen deine Consumer diesen Müll auch ausgeliefert. Das Ergebnis sind abstürzende Consumer und lange Debugging-Sessions.
Und dann ist da noch das Thema Sicherheit. Wenn du Kafka in einer Public Cloud betreibst oder sensible Daten verarbeitest, reicht die Transportverschlüsselung oft nicht aus. Oft musst du sicherstellen, dass die Daten sauber verschlüsselt sind – und zwar so, dass nicht einmal der Cloud-Provider die Daten lesen kann.
Hier kommt Kroxylicious ins Spiel. Kroxylicious ist ein Open-Source Kafka-Proxy, der sich zwischen deine Clients und den Kafka-Cluster klemmt. Er versteht das Kafka-Protokoll und kann Nachrichten analysieren, validieren und verändern.
In diesem Artikel schauen wir uns zwei konkrete Use Cases an:
Record Validation: Wie du verhinderst, dass ungültige Daten überhaupt erst in deinen Cluster gelangen.
Record Encryption: Wie du Daten transparent verschlüsselst, ohne dass deine Anwendungen komplexes Key Management implementieren müssen.
Bevor wir tief eintauchen, setzen wir uns eine kleine Testumgebung auf. Wenn du noch keine Kafka-Umgebung hast, lies unseren Kafka-Testumgebung einrichten Artikel.
Zuerst laden wir Kroxylicious herunter (wir verwenden hier Version 0.18.0, aber schau gerne auf auf der Projektseite nach der neuesten Version):
wget https://github.com/kroxylicious/kroxylicious/releases/download/v0.18.0/kroxylicious-app-0.18.0-bin.tar.gz
tar -xzf kroxylicious-app-0.18.0-bin.tar.gz
mv kroxylicious-app-0.18.0 kroxylicious
cd kroxylicious
Als Nächstes erstellen wir eine Konfigurationsdatei. Wir sagen Kroxylicious, dass es auf Port 9292 lauschen und alle Anfragen an unseren lokalen Kafka-Broker auf 9092 weiterleiten soll.
config.yamlvirtualClusters:
- name: demo
targetCluster:
bootstrapServers: localhost:9092
gateways:
- name: mygateway
portIdentifiesNode:
bootstrapAddress: localhost:9292
nodeIdRanges:
- name: brokers
start: 0
end: 3
filterDefinitions: []
Achte darauf, dass deine nodeIdRanges zu deiner Kafka-Konfiguration passen. Wenn deine Broker-IDs bei 1 anfangen, passe start entsprechend an.
Jetzt starten wir den Proxy:
./bin/kroxylicious-start.sh --config config.yaml
Wenn alles geklappt hat, hast du jetzt einen Proxy laufen. Deine Kafka-Clients können sich nun entscheiden: Verbinden sie sich direkt mit Kafka (localhost:9092) oder gehen sie über Kroxylicious (localhost:9292).
Stell dir vor, du hast einen Producer, der JSON-Daten sendet. Ein anderes Team (Du ja nicht 😉) macht einen Fehler und der Producer sendet plötzlich invalides JSON.
Was passiert ohne Validierung?
Kafka nimmt die Nachricht an (ist ja nur ein Byte-Array).
Dein Consumer liest die Nachricht.
Der JSON-Parser im Consumer wirft eine Exception.
Der Consumer stürzt ab und bleibt in einer Retry-Schleife hängen.
Schema Registries sind eigentlich dazu da, dieses Problem zu lösen. Aber die Prüfungen passieren lediglich auf der Client-Seite. Ein fehlkonfigurierter Producer kann immer noch Müll produzieren.
Mit Kroxylicious können wir das verhindern. Wir konfigurieren einen Record Validation Filter, der sicherstellt, dass nur syntaktisch korrektes JSON im Topic wind-turbine-data landet.
Dazu passen wir die config.yaml an und fügen die Filter-Definition hinzu:
config.yamlfilterDefinitions:
- name: record-validation
type: RecordValidation
config:
rules:
- topicNames:
- wind-turbine-data
valueRule:
syntacticallyCorrectJson:
validateObjectKeysUnique: true
allowNulls: true
allowEmpty: false
defaultFilters:
- record-validation
Um das Beispiel einfach zu halten, prüfen wir lediglich ob das JSON syntaktisch korrekt ist. Natürlich unterstützt Kroxylicious auch Schema Validation. Aber dafür müssten wir eine Schema Registry einrichten und das wäre zu komplex für dieses Beispiel.
Nach einem Neustart von Kroxylicious passiert Folgendes, wenn ein Producer versucht, fehlerhaftes JSON zu senden:
echo "I am not a valid JSON" | kafka-console-producer.sh \
--bootstrap-server localhost:9292 --topic wind-turbine-data
Ergebnis:
org.apache.kafka.common.InvalidRecordException:
Value was invalid: value was not syntactically correct JSON:
Unrecognized token 'I': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
Spannend: Das Kafka Protokoll kennt diese Fehlermeldung nicht. Aber es unterstützt das Zurückgeben von Text-Fehlermeldungen. Kroxylicious nutzt diese Möglichkeit.
Der Producer bekommt sofort eine Fehlermeldung (InvalidRecordException). Die Nachricht landet niemals im Kafka-Log. Dein Consumer läuft friedlich weiter, weil er nur saubere Daten bekommt.
Verschlüsselung ist oft ein Albtraum. Client-seitige Verschlüsselung bedeutet, dass du dich um Key-Management, Rotation und die Verteilung von Schlüsseln an alle Teams kümmern musst. Wenn du das falsch machst, sind deine Daten entweder unsicher oder für immer verloren.
Kroxylicious kann als Encryption-Gateway fungieren. Es nutzt Envelope Encryption und integriert sich mit Key Management Systemen wie HashiCorp Vault, AWS KMS oder Azure Key Vault.
Für dieses Beispiel nutzen wir HashiCorp Vault im Dev-Modus (bitte nicht in Produktion so machen!).
wget https://releases.hashicorp.com/vault/1.18.0/vault_1.18.0_linux_amd64.zip
unzip vault_1.18.0_linux_amd64.zip
./vault server -dev
Wir müssen Vault so konfigurieren, dass Kroxylicious Schlüssel erstellen und nutzen darf. (Die genauen Policies und Befehle findest du in der Kroxylicious-Dokumentation, aber im Wesentlichen erlauben wir Operationen auf dem Pfad transit/keys/KEK_*).
export VAULT_ADDR='http://127.0.0.1:8200'
./vault secrets enable transit
Dann erstellen wir eine Policy (Berechtigungen) für Kroxylicious:
kroxylicious_encryption_filter_policy.hclpath "transit/keys/KEK_*" {
capabilities = ["read"]
}
path "/transit/datakey/plaintext/KEK_*" {
capabilities = ["update"]
}
path "transit/decrypt/KEK_*" {
capabilities = ["update"]
}
Und wenden wir die Policy an:
./vault policy write kroxylicious_encryption_filter_policy kroxylicious_encryption_filter_policy.hcl
Und schlussendlich erstellen wir einen Token für Kroxylicious:
./vault token create -display-name "kroxylicious record encryption" \
-policy=kroxylicious_encryption_filter_policy \
-period=768h \
-no-default-policy \
-orphan \
-format=json | jq -r .auth.client_token > kroxylicious-encryption-token
Für jedes Topic, das wir verschlüsseln wollen, müssen wir einen Key Encryption Key (KEK) erstellen:
./vault write -f transit/keys/KEK_my_encrypted_topic type=aes256-gcm96 auto_rotate_period=90d
Wir fügen den RecordEncryption Filter hinzu.
config.yamlfilterDefinitions:
- type: RecordEncryption
name: record-encryption
config:
kms: VaultKmsService
kmsConfig:
vaultTransitEngineUrl: http://localhost:8200/v1/transit
vaultToken:
passwordFile: /path/to/kroxylicious-encryption-token
selector: TemplateKekSelector
selectorConfig:
template: "KEK_${topicName}" # Wenn das Topic `my_encrypted_topic` heißt, wollen wir den KEK `KEK_my_encrypted_topic` benennen.
defaultFilters:
- record-encryption
Was passiert jetzt?
Dein Producer sendet Klartext an Kroxylicious (localhost:9292).
Kroxylicious verschlüsselt den Inhalt der Nachricht (Value), bevor er sie an Kafka (localhost:9092) weiterleitet.
Dein Consumer, der über Kroxylicious liest, bekommt die Daten automatisch entschlüsselt zurück.
Der Clou: Wenn du (oder ein Angreifer) direkt auf dem Kafka-Broker (localhost:9092) lauschst, siehst du nur Datensalat:
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic my_encrypted_topic --from-beginning
CAC_my_encrypted_topic......
Damit sind deine Daten At Rest im Kafka-Cluster sicher verschlüsselt. Der Kafka-Broker selbst sieht nie den Klartext.
Ist das nun Ende-zu-Ende Verschlüsselung oder nicht?
Das kommt drauf an. Wenn Kroxylicious unter der Kontrolle des Producers steht, würde ich es als Ende-zu-Ende Verschlüsselung bezeichnen. Wenn Kroxylicious vom Kafka Team betrieben wird, ist das lediglich at-rest Verschlüsselung, denn das Kafka-Team könnte die Daten entschlüsseln, wenn es wöllte, aber wenn Kafka in der Cloud betrieben würde, könnte der Cloud-Provider die Daten nicht lesen.
Kroxylicious erlaubt uns serverseitige Funktionen zu benutzen und zu implementieren, die Apache Kafka selbst nicht unterstützt. Der Preis dafür ist ein zusätzlicher Hop im Netzwerk und ein potenzieller Single Point of Failure.
Insbesondere bei erhöhten Sicherheitsanforderungen ist es sinnvoll, Verschlüsselung nicht selbst zu implementieren, sondern diese Funktionalität an eine getestete Software auszulagern.
Auch für die Kommunikation mit externen Partnern kann Kroxylicious eine sinnvolle Lösung sein. So können wir sicherstellen, dass die Daten immer im korrekten Format bei uns ankommen.
Tipp: Bevor du Kroxylicious einsetzt, prüfe bitte, ob du es wirklich benötigst. Es ist keine Lösung für alle Probleme.
Du willst sicherstellen, dass keine einzige Kafka-Nachricht verloren geht? In diesem Guide zeige ich dir, wie du mit den richtigen Konfigurationen eine maximale Zuverlässigkeit beim Produzieren erreichst und was du im Fehlerfall tun musst.
Mehr lesen
In diesem Post erfährst du, wie explizite Schemas, dir dabei helfen, ein mögliches Chaos in Kafka zu vermeiden und wie die Schema Registries dabei unterstützen.
Mehr lesen