从kafka client端(producers、consumers、 其他的brokers、tools)到kafka broker端的连接,进行身份认证,使用SSL或者SASL。kafka所支持的SASL机制如下:

  • SASL/GSSAPI (Kerberos) - starting at version 0.9.0.0
  • SASL/PLAIN - starting at version 0.10.0.0
  • SASL/SCRAM-SHA-256
  • SASL/SCRAM-SHA-512 - starting at version 0.10.2.0
  • SASL/OAUTHBEARER - starting at version 2.0

本篇主要总结 SASL/PLAIN

0. perquisite

安装kafka:kafka_2.12-2.4.0,download

安装zookeeper: zookeeper-3.6.3, download

1. kafka  SASL/PLAIN配置方法和步骤

image

  

2. 为zookeeper开启SASL身份认证(可选,可以不配置)

如果希望连接zookeeper的时候 也启用对身份做一个基本认证,基本配置如下。

  • 1)为zookeeper启用SASL认证功能,添加如下内容到zoo.cfg
## add SASL support for zookeeper
authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
requireClientAuthScheme=sasl
jaasLoginRenew=3600000


  • 2) 添加 zk_server_jaas.conf, 为zookeeper添加账号信息

:~/zookeeper/apache-zookeeper-3.6.3-bin/conf$ cat zk_server_jaas.conf Server {

org.apache.kafka.common.security.plain.PlainLoginModule required

username="cluster" password="clusterpasswd" user_alice="passwdofzookeeperserver"; };

username password是zookeeper集群之间的密码,alice用户可以给zookeeper client端使用。

并且需要把以下账户信息添加到kafka_server_jaas.conf,kafka作为zookeeper client需要提供身份认证信息。

Client{

org.apache.kafka.common.security.plain.PlainLoginModule required username="alice" password="passwdofzookeeperserver"; };


  • 3)导入kafka依赖的包

新建目录zk_sasl_dependency,kafka/lib目录下复制以下几个jar包到该目录下。

zk_sasl_dependency$ ls
kafka-clients-2.4.0.jar  lz4-java-1.6.0.jar  slf4j-api-1.7.28.jar  slf4j-log4j12-1.7.28.jar  snappy-java-1.1.7.3.jar


  • 4)修改zkEnv.sh

在zkEnv.sh中添加:

for i in /home/ehunjng/zk_sasl_dependency/*.jar;
do
    CLASSPATH="$i:$CLASSPATH"
done
SERVER_JVMFLAGS=" -Djava.security.auth.login.config=/home/ehunjng/zookeeper/apache-zookeeper-3.6.3-bin/conf/zk_server_jaas.conf "


3. 验证配置是否成功

  • 1)启动 zookeeper
ehunjng@CN-00005131:~$ /home/ehunjng/zookeeper/apache-zookeeper-3.6.3-bin/bin/zkServer.sh start
/usr/bin/java
ZooKeeper JMX enabled by default
 Using config: /home/ehunjng/zookeeper/apache-zookeeper-3.6.3-bin/bin/../conf/zoo.cfg
 Starting zookeeper ... STARTED


  • 2) 启动kafka 如果开启了zookeeper的身份认证,此时会用到连接zookeeper的用户密码, 否则用不到。
cd /home/ehunjng/kafka_2.12-2.4.0
 ./bin/kafka-server-start.sh  config/server.properties


通过修改 kafka_2.12-2.4.0/config$ vim log4j.properties的文件,来修改kafka的loglevel。


如果kafka broker端设置了正确的连接zookeeper的密码,打开zookeeper的log可以看到:

cd zookeeper/apache-zookeeper-3.6.3-bin/logs

cat zookeeper-ehunjng-server-CN-00005131.out

2021-10-25 17:54:40,340 [myid:] - INFO  [NIOWorkerThread-5:SaslServerCallbackHandler@119] - Successfully authenticated client: authenticationID=alice;  authorizationID=alice.
 2021-10-25 17:54:40,353 [myid:] - INFO  [NIOWorkerThread-5:SaslServerCallbackHandler@135] - Setting authorizedID: alice
 2021-10-25 17:54:40,353 [myid:] - INFO  [NIOWorkerThread-5:ZooKeeperServer@1680] - adding SASL authorization for authorizationID: alice


如果kafka broker端设置了错误的连接zookeeper的密码,启动kafka的时候,控制台会有错误log:

[2021-10-25 18:07:45,068] ERROR Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer)
 org.apache.zookeeper.KeeperException$AuthFailedException: KeeperErrorCode = AuthFailed for /consumers
         at org.apache.zookeeper.KeeperException.create(KeeperException.java:130)
         at org.apache.zookeeper.KeeperException.create(KeeperException.java:54)
         at kafka.zookeeper.AsyncResponse.maybeThrow(ZooKeeperClient.scala:561)
         at kafka.zk.KafkaZkClient.createRecursive(KafkaZkClient.scala:1640)
         at kafka.zk.KafkaZkClient.makeSurePersistentPathExists(KafkaZkClient.scala:1562)
         at kafka.zk.KafkaZkClient.$anonfun$createTopLevelPaths$1(KafkaZkClient.scala:1554)
         at kafka.zk.KafkaZkClient.$anonfun$createTopLevelPaths$1$adapted(KafkaZkClient.scala:1554)
         at scala.collection.immutable.List.foreach(List.scala:392)
         at kafka.zk.KafkaZkClient.createTopLevelPaths(KafkaZkClient.scala:1554)
         at kafka.server.KafkaServer.initZkClient(KafkaServer.scala:400)
         at kafka.server.KafkaServer.startup(KafkaServer.scala:207)
         at kafka.server.KafkaServerStartable.startup(KafkaServerStartable.scala:44)
         at kafka.Kafka$.main(Kafka.scala:84)
         at kafka.Kafka.main(Kafka.scala)
 [2021-10-25 18:07:45,094] INFO shutting down (kafka.server.KafkaServer)
 [2021-10-25 18:07:45,104] INFO [ZooKeeperClient Kafka server] Closing. (kafka.zookeeper.ZooKeeperClient)
 [2021-10-25 18:07:45,105] DEBUG Shutting down task scheduler. (kafka.utils.KafkaScheduler)
 [2021-10-25 18:07:45,106] DEBUG Close called on already closed client (org.apache.zookeeper.ZooKeeper)
 [2021-10-25 18:07:45,116] INFO [ZooKeeperClient Kafka server] Closed. (kafka.zookeeper.ZooKeeperClient)
 [2021-10-25 18:07:45,123] INFO shut down completed (kafka.server.KafkaServer)
 [2021-10-25 18:07:45,125] ERROR Exiting Kafka. (kafka.server.KafkaServerStartable)
 [2021-10-25 18:07:45,130] INFO shutting down (kafka.server.KafkaServer)


同时,zookeeper的log也有以下错误提醒:

image


  • 3)验证kafka consumers/producers 到kafka broker的连接

从控制台验证:

1) 当kafka_client_jaas.conf中的用户名密码不为username=“kafka"和password=“kafkapasswd";

cat kafka_client_jaas.conf
KafkaClient {
  org.apache.kafka.common.security.plain.PlainLoginModule required
  username="kfktest"
  password="kfktest";
};

启动生产者和消费者会报错:

image

2)当kafka_client_jaas.conf中的用户名密码为username=“kafka"和password=“kafkapasswd",和kafka_server_jaas.conf匹配时

① 启动生产者和消费者,观察二者之间的通信。

@CN-00005131:~/kafka_2.12-2.4.0$ bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test1025 --producer.config config/producer.properties


SLF4J: Class path contains multiple SLF4J bindings.
 SLF4J: Found binding in [jar:file:/home/ehunjng/zk_sasl_dependency/slf4j-log4j12-1.7.28.jar!/org/slf4j/impl/StaticLoggerBinder.class]
 SLF4J: Found binding in [jar:file:/home/ehunjng/kafka_2.12-2.4.0/libs/slf4j-log4j12-1.7.28.jar!/org/slf4j/impl/StaticLoggerBinder.class]
 SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
 SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
 >test1025-1
 >test1025-2
 >test1025-3
 >


ehunjng@CN-00005131:~/kafka_2.12-2.4.0$ /home/ehunjng/kafka_2.12-2.4.0/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test1025 --from-beginning --consumer.config config/consumer.properties
test1025-1
test1025-2
test1025-3


从代码验证:

① consumer端代码:

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Collections;
import java.util.Properties;

public class KafkaConsumerTest{
        public static void main(String [] args) throws Exception{
          testConsumer();
        }
    public static  void testConsumer() throws Exception {
        System.setProperty("java.security.auth.login.config", "/home/ehunjng/kafka_2.12-2.4.0/kafka_client_jaas.conf");
        Properties props = new Properties();
        props.put("bootstrap.servers", "127.0.0.1:9092");
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        props.put("group.id", "kafka_test_group");
        props.put("session.timeout.ms", "6000");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("security.protocol", "SASL_PLAINTEXT");
        props.put("sasl.mechanism", "PLAIN");

        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Collections.singletonList("kafkatest0715"));
                while(true){
                        long startTime = System.currentTimeMillis();
                        ConsumerRecords<String, String> records = consumer.poll(5000);
                        System.out.println(System.currentTimeMillis() - startTime);
                        System.out.println("recieve message number is " + records.count());
                        for (ConsumerRecord<String, String> record : records) {
                                System.out.printf("offset = %d, key = %s, value = %s, partition = %d %n",
                                                record.offset(),
                                                record.key(),
                                                record.value(),
                                                record.partition());
                        }
        }
    }
}
② producer端代码:
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Collections;
import java.util.Properties;

public class KafkaProducerTest{
        public static void main(String [] args) throws Exception{
          testProduct();
        }

        public static void testProduct() throws Exception{
                System.setProperty("java.security.auth.login.config", "/home/ehunjng/kafka_2.12-2.4.0/kafka_client_jaas.conf");
                Properties props = new Properties();
                props.put("bootstrap.servers", "127.0.0.1:9092");
                props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
                props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
                props.put("security.protocol", "SASL_PLAINTEXT");
                props.put("sasl.mechanism", "PLAIN");

                Producer<String, String> producer = new KafkaProducer<>(props);

                long startTime = System.currentTimeMillis();
                for (int i = 0; i < 10; i++) {
                        producer.send(new ProducerRecord<>("kafkatest0715", Integer.toString(i), Integer.toString(i))).get();
                        System.out.println("Send message key = " + Integer.toString(i) + ", value = " + Integer.toString(i));
                        Thread.sleep(2000);
                }
                System.out.println(System.currentTimeMillis()-startTime);

        }
}

③ 设置必要的classpath:

export CLASSPATH="/usr/lib/jvm/java-1.8.0-openjdk-amd64/lib:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib:/home/ehunjng/kafka_2.12-2.4.0:/home/ehunjng/kafka_2.12-2.4.0/libs:/home/ehunjng/zk_sasl_dependency/kafka-clients-2.4.0.jar:/home/ehunjng/zk_sasl_dependency/slf4j-api-1.7.28.jar:/home/ehunjng/zk_sasl_dependency/slf4j-log4j12-1.7.28.jar:/home/ehunjng/zk_sasl_dependency/snappy-java-1.1.7.3.jar:/home/ehunjng/zk_sasl_dependency/lz4-java-1.6.0.jar:/home/ehunjng/kafka_2.12-2.4.0/libs/log4j-1.2.17.jar"

④ 编译代码

javac -cp $CLASSPATH KafkaConsumerTest.java KafkaProducerTest.java

⑤启动consumer

java KafkaConsumerTest

⑥启动producer

java KafkaProducerTest


kafka_client_jaas.conf持有正确的密码,如下:

image


image


kafka_client_jaas.conf持有错误的密码,如下:

image


image


参照:

kafka 2.4 guide:
https://kafka.apache.org/24/documentation.html#security_sasl
https://kafka.apache.org/24/documentation.html#security_sasl_plain_production

https://www.cnblogs.com/ilovena/p/10123516.html

https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer

posted on 2021-10-25 21:42  维也纳下午茶  阅读(3173)  评论(0编辑  收藏  举报