EN EnglishDE Deutsch

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

Zwei Schweizergardisten in gestreiften Uniformen stehen an einem Torbogen in der Vatikanstadt
Foto von Rahul Chowdhury / Unsplash

Kafka in Produktion – wir bauen einen skalierbaren, sicheren und Enterprise-tauglichen Message Bus auf der Google Cloud Platform
opensight.ch - roman hüsler

Inhaltsverzeichnis

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.

SASL-Plaintext-Authentifizierung

Um in Kafka Authentifizierung zu konfigurieren, sind einige Anpassungen an der Konfigurationsdatei server.properties nötig. In diesem Setup setzen wir Simple Authentication and Security Layer (SASL) ein, um vor dem Kafka-Broker eine Client-Authentifizierung zu erzwingen.

SASL ist ein flexibles Authentifizierungs-Framework und unterstützt mehrere Mechanismen, wodurch sich unterschiedliche Authentifizierungsanbieter einbinden lassen. Weitere SASL-Modi schauen wir uns in späteren Beiträgen dieser Serie an.

Diagramm: Ein Client verbindet sich über SASL mit dem Kafka-Broker. SASL bietet die Mechanismen GSSAPI Kerberos, Plain mit Passwort-Server, SCRAM SHA mit in Zookeeper gespeichertem Passwort-Hash und OAUTHBEARER mit OAuth-Server; Plain ist hervorgehoben.
Abbildung – Kafka-Authentifizierung mit SASL Plaintext

Deployment

Bei SASL Plaintext werden Benutzer und Passwörter direkt in der Kafka-Konfiguration gespeichert.

Diagramm: Kafka-UI auf Port 8089 authentifiziert sich per SASL (Plaintext) mit dem Passwort changeit am Kafka-Broker auf Port 9095; der Broker ist an Zookeeper auf Port 2181 angebunden.
Abbildung – Kafka-Deployment mit SASL-Plaintext-Authentifizierung

Konfigurations-Properties

Schauen wir uns an, welche Konfigurations-Properties wir anpassen müssen, um die SASL-Plaintext-Authentifizierung zu aktivieren.

  • advertised.listeners:PLAINTEXT://kafka:9092,SASL_PLAINTEXT://kafka:9095
    (Doku) Zuerst aktivieren wir einen neuen Listener auf Port 9095, auf dem «Simple Authentication and Security Layer (SASL)» aktiv ist.
  • security.inter.broker.protocol:SASL_PLAINTEXT
    (Doku) Auch die Broker würden untereinander per SASL-Authentifizierung im Plaintext kommunizieren. Da wir aber nur einen Broker haben, bewirkt diese Einstellung hier nicht viel.
  • sasl.enabled.mechanisms:PLAIN
    (Doku) Aktiviert den Mechanismus Plain für die Passwort-Authentifizierung.
  • ZOOKEEPER_SASL_ENABLED:false
    Diese Umgebungsvariable ist spezifisch für das Confluent-Kafka-Image (cp-kafka). Ist sie gesetzt, wird sie beim Start von Kafka in den Kommandozeilenparameter -Dzookeeper.sasl.client=false übersetzt. So versucht Kafka nicht, bei der Kommunikation mit ZooKeeper SASL-Authentifizierung zu verwenden. In diesem Setup läuft ZooKeeper ohne Authentifizierung.
  • opts:-Djava.security.auth.login.config=/etc/kafka/configs/kafka_server_jaas.conf -Dzookeeper.sasl.client=false
    Die Startoptionen von Kafka. Ich habe zookeeper.sasl.client=false hier noch einmal gesetzt, falls Sie ein anderes Kafka-Docker-Image verwenden. Ausserdem übergeben wir die Konfigurationsdatei kafka_server_jaas.conf, in der die Zugangsdaten für die Authentifizierung hinterlegt sind. (Doku)
KafkaServer {
    org.apache.kafka.common.security.plain.PlainLoginModule required
    username="kafka"
    password="changeit"
    user_kafka="changeit";
};

Wie Sie sehen, ist ein Benutzer kafka mit dem Passwort changeit konfiguriert. Dieses Setup dient ausschliesslich der Authentifizierung am Kafka-Broker. Access Control Lists (ACLs) für die Autorisierung auf Topic-Ebene haben wir noch nicht umgesetzt; die Autorisierung konfigurieren wir in einem späteren Schritt.

KafkaUI
KafkaUI hat jetzt einige zusätzliche Properties, damit es sich am Kafka-Broker authentifizieren kann. Sehen Sie sich dazu den Abschnitt kafka-ui in der docker-compose.yml an.

version: "3.8"
services:
  zookeeper:
    image: "${ZOOKEEPER_IMAGE}"
    container_name: zookeeper
    restart: always
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
      ZOOKEEPER_MAX_CLIENT_CNXNS: 60
      KAFKA_HEAP_OPTS: "-Xms512M -Xmx1024M"
    ports:
      - "2181:2181"
    networks:
      - kafka

  kafka:
    image: "${KAFKA_IMAGE}"
    container_name: kafka
    restart: always
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 100
      KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka:9092,SASL_PLAINTEXT://kafka:9095"
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT,SASL_PLAINTEXT:SASL_PLAINTEXT"
      KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SASL_PLAINTEXT
      KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL: PLAIN
      KAFKA_SASL_ENABLED_MECHANISMS: PLAIN
      KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/configs/kafka_server_jaas.conf -Dzookeeper.sasl.client=false"
      KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: 30000
      ZOOKEEPER_SASL_ENABLED: "false"
    ports:
      - "9092:9092"
      - "9095:9095"
    volumes:
      - "./kafka_config:/etc/kafka/configs"
    networks:
      - kafka

  kafka-ui:
    image: "${KAFKA_UI_IMAGE}"
    container_name: kafka-ui
    restart: always
    depends_on:
      - kafka
    ports:
      - "8089:8080"
    environment:
      KAFKA_CLUSTERS_0_NAME: local
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9095
      KAFKA_CLUSTERS_0_PROPERTIES_SECURITY_PROTOCOL: SASL_PLAINTEXT
      KAFKA_CLUSTERS_0_PROPERTIES_SASL_MECHANISM: PLAIN
      KAFKA_CLUSTERS_0_PROPERTIES_SASL_JAAS_CONFIG: 'org.apache.kafka.common.security.plain.PlainLoginModule required username="kafka" password="changeit";'
    networks:
      - kafka

networks:
  kafka:
    driver: bridge

Kafka-Deployment mit SASL-Plaintext-Authentifizierung

Testen

Wie schon in Teil 1 empfohlen, lohnt es sich sehr, mit der Kafka UI zu experimentieren. Der Verbindungsstatus wird grün angezeigt, das Setup funktioniert also.

Zudem kann sich unsere Test-Applikation jetzt auch per SASL/PLAIN authentifizieren. In diesem Setup ist Kafka SASL auf Port 9095 erreichbar, und die Test-Applikation hat erfolgreich eine Verbindung aufgebaut. Das bestätigt unsere Authentifizierungskonfiguration zusätzlich.

Screenshot der Test-App mit Broker kafka:9095, SASL/PLAIN, Benutzer kafka und Status «Connected»; eine Testnachricht wurde gesendet und empfangen.
Abbildung – Kafka-Test-App erfolgreich per SASL/PLAIN-Authentifizierung verbunden

Eine gute Methode, um zu prüfen, ob SASL/PLAIN korrekt konfiguriert ist, ist ein Test mit falschen Zugangsdaten. Bei einem falschen Passwort verweigert Kafka die Verbindung zu Recht. Ebenso wird die Verbindung abgelehnt, wenn die Authentifizierung auf "none" gesetzt und damit deaktiviert wird.

Screenshot der Test-App, deren Log nach einem falschen Passwort die Fehlermeldung «SASL PLAIN authentication failed: Invalid username or password» zeigt.
Abbildung – Verbindung der Kafka-Test-App schlägt mit falschem Passwort fehl

Werfen Sie während eines Verbindungsversuchs auch einen Blick in die Logs des Kafka-Containers:

docker logs kafka -f

Logs des Kafka-Containers prüfen

Terminal-Screenshot der Kafka-Logs mit einer hervorgehobenen Zeile «Failed authentication … Invalid username or password».
Abbildung – Kafka-Logs mit fehlgeschlagener SASL-PLAIN-Authentifizierung

Damit ist bestätigt, dass die SASL/PLAIN-Authentifizierung wie erwartet durchgesetzt wird: Unser Setup funktioniert!

Damit schliessen wir diesen Teil der Blogserie ab. In Teil 3 verlassen wir das Einzelinstanz-Deployment und bauen einen Kafka-Cluster mit drei Nodes auf. Ausserdem nutzen wir SSL (TLS) für die Authentifizierung und führen Zugriffskontrolle (ACL) ein, um die Sicherheit zu erhöhen. Bleiben Sie dran!