使用 JAAS 設定在 Kafka 中實現 SASL 身份驗證
1. 概述
身份驗證是設計任何訊息傳遞系統(例如 Kafka)的基本面向。我們可以使用基於使用者的憑證、SSL 憑證或基於令牌等方法來實現身份驗證。
在本教程中,我們將學習如何在 Kafka 服務中實作稱為簡單身份驗證和套接字層(SASL) 的身份驗證機制。我們也將使用 Spring Kafka 提供的機制實作客戶端身份驗證。
2.Kafka 身份驗證簡介
Kafka 支援各種身份驗證和授權機制,以確保網路通訊的安全。它支援 SSL、SASL 或委託令牌。身份驗證可以在客戶端與代理之間、代理與 Zookeeper 之間或代理之間進行。
根據系統需求和其他基礎設施因素,我們可以使用任何相關方法。 SSL 驗證使用X.509
憑證來驗證用戶端和代理,並提供單向或相互驗證。
SASL 身份驗證是一個支援不同身份驗證機制的安全框架:
- SASL/GSSAPI – SASL/GSSAPI(通用安全服務應用程式介面)是一種標準 API,它透過標準 API 抽象化安全機制,並且可以輕鬆地與現有的 Kerberos 服務整合。 SASL/GSSAPI 驗證使用金鑰分發中心透過網路提供身份驗證,通常在現有基礎設施可用的地方使用,例如 Active Directory 或 Kerberos 伺服器。
- SASL/PLAIN – SASL/PLAIN 使用基於使用者的憑證進行身份驗證,它主要用於非生產環境,因為它在網路上不安全。
- SASL/SCRAM –使用 SASL/SCRAM 身份驗證,透過對密碼進行雜湊處理並添加鹽來創建加鹽的質詢-回應,從而提供比純文字機制更好的安全性。 SCRAM 支援不同的雜湊演算法,如 SHA-256、SHA-512 或 SHA-1(較弱)。
- SASL/OAUTHBEARER – SASL/OAUTHBEARER 使用 OAUTH 2.0 承載令牌進行身份驗證,當我們擁有現有的身份提供者(如 Keycloak 或 OKTA)時很有用。
我們也可以將 SASL 與 SSL 認證結合起來,提供傳輸層加密。
對於 Kafka 中的授權,我們可以使用內建的基於 ACL 或 OAUTH/OIDC(OpenID Connect)或自訂授權器。
在本教程中,我們將重點介紹 GSSAPI 身份驗證實現,因為它被廣泛使用並且也保持其簡單性。
3.使用 SASL/GSSAPI 驗證實作 Kafka 服務
假設我們需要在 Docker 環境中建置一個支援 GSSAPI 驗證的 Kafka 服務。
為此,我們可以利用 Kerberos 運行時來提供票證授予票證 (TGT) 服務並充當身份驗證伺服器。
3.1.設定 Kerberos
為了在 Docker 環境中實作 Kerberos 服務,我們需要自訂Kerberos設定。
首先,讓我們包含一個krb5.conf
檔案來設定領域BAELDUNG.COM
一些設定:
[libdefaults]
default_realm = BAELDUNG.COM
dns_lookup_realm = false
dns_lookup_kdc = false
forwardable = true
rdns = true
[realms]
BAELDUNG.COM = {
kdc = kdc
admin_server = kdc
}
領域是 Kafka 服務的邏輯名稱或網域。
我們需要實作一個腳本,使用kdb5_util
初始化 Kerberos db,然後使用Kadmin.local
指令為 Kafka、Zookeeper 和客戶端應用程式建立主體及其關聯的金鑰表檔案。最後,我們將使用krb5kdc
和kadmind
指令來啟動 Kerberos 服務。
然後,讓我們實作腳本kdc_setup.sh
來新增主體,建立keytab
文件,並執行 Kerberos 服務:
kadmin.local -q "addprinc -randkey kafka/[email protected]"
kadmin.local -q "addprinc -randkey zookeeper/[email protected]"
kadmin.local -q "addprinc -randkey [email protected]"
kadmin.local -q "ktadd -k /etc/krb5kdc/keytabs/kafka.keytab kafka/[email protected]"
kadmin.local -q "ktadd -k /etc/krb5kdc/keytabs/zookeeper.keytab zookeeper/[email protected]"
kadmin.local -q "ktadd -k /etc/krb5kdc/keytabs/client.keytab [email protected]"
krb5kdc
kadmind -nofork
任何主體的格式通常為<service-name>/<host/domain>@REALM
。主機名稱部分是可選的,並且REALM
通常為大寫。
我們也應該注意,校長應該正確設定。否則,身份驗證將由於服務名稱或完全限定網域不符而失敗。
最後,我們來實作一個Dockerfile
來準備Kerberos環境:
FROM debian:bullseye
RUN apt-get update && \
apt-get install -y krb5-kdc krb5-admin-server krb5-user && \
rm -rf /var/lib/apt/lists/*
COPY config/krb5.conf /etc/krb5.conf
COPY setup_kdc.sh /setup_kdc.sh
RUN chmod +x /setup_kdc.sh
上述Dockerfile
使用先前建立的krb5.conf
和setup_kdc.sh
檔案來初始化並執行Kerberos服務。
我們還將添加一個kadm5.acl
文件,以授予管理員主體完全權限:
*/[email protected] *
3.2. Kafka 和 Zookeeper 的配置
為了在 Kafka 中設定 GSSAPI 驗證,我們將使用JAAS
(Java 驗證和授權服務)來指定 Kafka 或用戶端應如何使用 Kerberos 的金鑰分發中心 (KDC) 進行驗證。
我們將在 Kafka 伺服器、Zookeeper 的單獨檔案中建立 JAAS 相關設定。
首先,我們將實作zookeeper_jaas.conf
檔案並設定先前建立的zookeeper.keytab
檔案和principal
參數:
Server {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="/etc/kafka/keytabs/zookeeper.keytab"
principal="zookeeper/[email protected]";
};
該主體必須與動物園管理員的 Kerberos 主體相同。透過將useKeyTab
設為 true,我們強制身份驗證使用 keytab 檔案。
然後我們在kafka_server_jaas.conf
檔案中設定Kafka伺服器和客戶端JAAS相關屬性:
KafkaServer {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="/etc/kafka/keytabs/kafka.keytab"
principal="kafka/[email protected]"
serviceName="kafka";
};
Client {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="/etc/kafka/keytabs/client.keytab"
principal="[email protected]"
serviceName="kafka";
};
3.3.將 Kafka 與 Zookeeper 和 Kerberos 集成
透過Docker服務,可以輕鬆整合Kafka、Zookeeper和自訂Kerberos服務。
首先,我們將使用早期的Dockerfile
實作自訂 Kerberos 服務:
services:
kdc:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./config:/etc/krb5kdc
- ./keytabs:/etc/krb5kdc/keytabs
- ./config/krb5.conf:/etc/krb5.conf
ports:
- "88:88/udp"
上述服務將在內部和主機環境的典型UDP
88 連接埠上可用。
然後,讓我們使用confluentinc:cp-zookeeper
基礎映像設定 Zookeeper 服務:
zookeeper:
image: confluentinc/cp-zookeeper:latest
container_name: zookeeper
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/zookeeper_jaas.conf"
volumes:
- ./config/zookeeper_jaas.conf:/etc/kafka/zookeeper_jaas.conf
- ./keytabs:/etc/kafka/keytabs
- ./config/krb5.conf:/etc/krb5.conf
ports:
- "2181:2181"
上述 Zookeeper 服務也使用zookeeper_jaas.conf
配置了 GSSAPI 驗證。
最後,我們將使用與 GSSAPI 相關的環境屬性來設定 Kafka 服務:
kafka:
image: confluentinc/cp-kafka:latest
container_name: kafka
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL: GSSAPI
KAFKA_SASL_ENABLED_MECHANISMS: GSSAPI
KAFKA_LISTENERS: SASL_PLAINTEXT://:9092
KAFKA_ADVERTISED_LISTENERS: SASL_PLAINTEXT://localhost:9092
KAFKA_INTER_BROKER_LISTENER_NAME: SASL_PLAINTEXT
KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/kafka_server_jaas.conf"
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
volumes:
- ./config/kafka_server_jaas.conf:/etc/kafka/kafka_server_jaas.conf
- ./keytabs:/etc/kafka/keytabs
- ./config/krb5.conf:/etc/krb5.conf
depends_on:
- zookeeper
- kdc
ports:
- 9092:9092
在上面的 Kafka 服務中,我們在 Kafka 的 Broker 和用戶端上都啟用了 GSSAPI 驗證。
Kafka 服務將使用先前建立的kafka_server_jaas.conf
檔案進行 GSSAPI 配置,例如主體和金鑰表檔案。
我們應該注意, KAFKA_ADVERTISED_LISTENERS
屬性是 Kafka 用戶端將監聽的端點。
現在,我們將使用docker compose
命令運行整個 Docker 設定:
$ docker compose up --build
kafka | [2025-02-03 18:09:10,147] INFO Successfully authenticated client: authenticationID=kafka/[email protected]; authorizationID=kafka/[email protected]. (org.apache.kafka.common.security.authenticator.SaslServerCallbackHandler)
kafka | [2025-02-03 18:09:10,148] INFO [RequestSendThread controllerId=1001] Controller 1001 connected to localhost:9092 (id: 1001 rack: null) for sending state change requests (kafka.controller.RequestSendThread)
從以上日誌中我們確認Kafka,Zookeeper,Kerberos服務都整合成功,沒有錯誤。
4.使用Spring實現Kafka客戶端
我們將使用 Spring Kafka 實作來實作 Kafka 監聽器應用程式。
4.1. Maven 依賴項
首先,我們將包含spring-kafka
依賴項:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>3.1.2</version>
</dependency>
4.2.實作 Kafka 監聽器
我們將使用 Spring Kafka 的KafkaListener
和ConsumerRecord
類別來實作監聽器。
讓我們用@KafkaListener
註解實作Kafka監聽器並加入所需的topic
:
@KafkaListener(topics = test-topic)
public void receive(ConsumerRecord<String, String> consumerRecord) {
log.info("Received payload: '{}'", consumerRecord.toString());
messages.add(consumerRecord.value());
}
另外,我們將在application-sasl.yml
檔案中設定與 Spring 偵聽器相關的設定:
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: test
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
現在,讓我們運行 Spring 應用程式並驗證設定:
kafka | [2025-02-01 03:08:01,532] INFO [SocketServer listenerType=ZK_BROKER, nodeId=1001] Failed authentication with /172.21.0.1 (channelId=172.21.0.4:9092-172.21.0.1:59840-16) (Unexpected Kafka request of type METADATA during SASL handshake.) (org.apache.kafka.common.network.Selector)
上述日誌確認用戶端應用程式無法如預期向 Kafka 伺服器進行身份驗證。
為了解決這個問題,我們還需要在應用程式中包含 Spring Kafka JAAS
配置。
5.使用JAAS
Config 設定 Kafka 用戶端
我們將使用spring.kafka.properties
配置來提供 SASL/GSSAPI 設定。
現在,我們將包括一些與客戶端principal
、 keytab
檔案和sasl.mechanism
作為GSSAPI
相關的附加設定:
spring:
kafka:
bootstrap-servers: localhost:9092
properties:
sasl.mechanism: GSSAPI
sasl.jaas.config: >
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="./src/test/resources/sasl/keytabs/client.keytab"
principal="[email protected]"
serviceName="kafka";
我們應該注意,上面的**serviceName
配置應該與 Kafka 主體的****serviceName
**完全匹配。
我們再次驗證Kafka消費者應用程式.
6. 在應用程式中測試 Kafka 監聽器
為了快速驗證監聽器,我們將使用 Kafka 提供的實用程式kafka-console-producer.sh,
向主題發送訊息。
我們將運行以下命令向主題發送訊息:
$ kafka-console-producer.sh --broker-list localhost:9092 \
--topic test-topic \
--producer-property security.protocol=SASL_PLAINTEXT \
--producer-property sasl.mechanism=GSSAPI \
--producer-property sasl.kerberos.service.name=kafka \
--producer-property sasl.jaas.config="com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true keyTab=\"/<path>/client.keytab\"
storeKey=true principal=\"[email protected]\";"
> hello
在上面的命令中,我們透過client.keytab
檔案傳遞類似的與身份驗證相關的配置,如security.protocol
、 sasl.mechanism, and
sasl.jaas.config
。
現在,讓我們驗證一下收到的訊息的監聽器日誌:
08:52:13.663 INFO cbsKafkaConsumer - Received payload: 'ConsumerRecord(topic = test-topic, .... key = null, value = hello)'
我們應該注意,任何投入生產的應用程式可能還需要一些配置,例如配置 SSL 憑證或 DNS。
7. 結論
在本文中,我們學習如何在 docker 環境中設定 Kafka 服務並使用自訂 Kerberos 設定在 docker 環境中啟用 SASL/GSSAPI 驗證。
我們還實作了客戶端監聽器應用程序,並使用 JAAS 配置配置了 GSSAPI 身份驗證。最後,我們透過發送訊息並在偵聽器中接收該訊息來測試整個設定。
與往常一樣,範例程式碼可以在 GitHub 上找到。