学会使用Kafka(十二)Kafka认证与授权
Kafka的认证机制
概述
- GSSAPI:使用的Kerberos认证,可以集成目录服务,比如AD。Kafka最小版本 0.9
- PLAIN:使用简单用户名和密码形式,Kafka最小版本 0.10
- SCRAM:主要解决PLAIN动态更新问题以及安全机制,Kafka最小版本 0.10.2
- OAUTHBEARER:基于OAuth 2认证框架,Kafka最小版本 2.0
配置SASL\PLAIN
创建kafka_server_jaas.conf文件
这个文件是配置Broker服务端的JAAS,进入Kafka程序目录的config目录中,执行下面的命令:
touch kafka_server_jaas.conf
KafkaServer {
org.apache.kafka.common.security.plain.PlainLoginModule required
username="admin"
password="admin"
user_admin="admin"
user_rex="123456"
user_alice="123456";
user_lucy="123456";
};
Client {
org.apache.zookeeper.server.auth.DigestLoginModule required
username="admin"
password="admin";
};
上面的文件配置要顶格写。另外上面的
username="admin" password="admin"
只是定义了broker间通信使用的用户名和密码,但是并不是创建用户的函数以,所以后面要写user_admin="admin"
,来创建创建用,如果你不创建,那么broker启动将会报验证失败的错误。
KafkaServer
字段是用来配置broker间通信使用的用户名和密码以及客户端连接时需要的用户名和密码,其中username和password是broker用于初始化连接到其他的broker,kafka用户为broker间的通讯。在一个Kafka集群中,这个文件的内容要一样,集群中每个Borker去连接其他Broker的时候都使用这个文件中定义的username和password来让对方进行认证。
而user_NAME
语句,是用来配置客户端连接Broker时使用的用户名和密码,也就是新建用户的用户名都是以user_
开头的,等号后面是密码,密码可以是明文且没有复杂度要求。完整语句是user_USERNAME="PASSWORD"
。
Client
部分是用来设置与Zookeeper的连接的,它还允许broker设置 SASL ACL 到zookeeper 节点,锁定这些节点,只有broker可以修改它。如果Zookeeper与Broker之间不设置认证,那么就可以不配置。
创建kafka_client_jaas.conf文件
进入Kafka程序目录的config目录中,执行下面的命令:
touch kafka_client_jaas.conf
KafkaClient {
org.apache.kafka.common.security.plain.PlainLoginModule required
username="admin"
password="admin";
};
Client {
org.apache.zookeeper.server.auth.DigestLoginModule required
username="admin"
password="admin";
};
这个文件是客户端连接Broker使用的,username和password是用来配置客户端连接broker的用户,在上面配置中,客户端使用admin用户连接到broker。
这些客户端包括kafka的二进制包中bin目录下的客户端工具以及其他外部客户端,比如Java、Python程序等。
内部客户端工具
kafka自带的生产和消费客户端工具
比如下面的客户端工具:
- kafka-console-consumer.sh
- kafka-console-producer.sh
- kafka-consumer-groups.sh
针对kafka-console-consumer.sh
和kafka-console-producer.sh
,可以在consumer.properties
和producer.properties
,加入下面的内容:
security.protocol=SASL_PLAINTEXT
sasl.mechanism=PLAIN
在kafka-console-consumer.sh和kafka-console-producer.sh中添加:
export KAFKA_OPTS=" -Djava.security.auth.login.config=/data/kafka/kafka_2.11-1.1.0/config/kafka_client_jaas.conf"
目的是让客户端启动时带上身份信息,也就是它使用的用户名和密码
输入命令,启动生产者:
./kafka-console-producer.sh --broker-list x.x.x.x:9092 --topic test --producer.config ../config/producer.properties
输入命令,启动消费者:
./kafka-console-consumer.sh --bootstrap-server x.x.x.x:9092 --topic test --from-beginning --consumer.config ../config/consumer.properties
这两个加上
../config/consumer.properties
这个的目的是设置认证协议。这个东西要和Broker端设置的一样。
还有另外一种设置,我们在启动命令行生产者和消费者时不需要引入外部文件,只需要在consumer.properties
和producer.properties
中加入下面的内容,这样就不需要建立kafka_client_jaas.conf
文件:
security.protocol=SASL_PLAINTEXT
sasl.mechanism=PLAIN
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="admin" password="admin";
注意上面的
sasl.jaas.config
后面要加分号。
kafka-consumer-groups.sh工具
直接启动会拒绝。针对kafka-consumer-groups.sh
工具你可以单独写一个配置文件,包含上面的内容,在启动程序的时候加上一个参数引入这个文件即可,比如:
touch sasl.properties
security.protocol=SASL_PLAINTEXT
sasl.mechanism=PLAIN
./kafka-consumer-groups.sh --bootstrap-server X.X.X.X:9092 --list --command-config ../config/sasl.properties
外部客户端工具
Python
config._kwargs = {
"bootstrap_servers": "x.x.x.x:9092",
"client_id": "ClientId",
"group_id": "GroupID",
"enable_auto_commit": False,
"auto_offset_reset": "latest",
"key_deserializer": lambda m: json.loads(m.decode('utf-8')),
"value_deserializer": lambda m: json.loads(m.decode('utf-8')),
# 配置验证信息
"security_protocol": "SASL_PLAINTEXT",
"sasl_mechanism": "PLAIN",
"sasl_plain_username": "rex",
"sasl_plain_password": "123456",
}
try:
self._consumer = KafkaConsumer(**config._kwargs)
except Exception as err:
print("Consumer init failed, %s" % err)
security_protocol
:与Borker通信协议,默认是PLAINTEXT
,可选值为:PLAINTEXT
, SSL
,SASL_PLAINTEXT
。
sasl_mechanism
:当security_protocol
被配置为SASL_PLAINTEXT
或者SASL_SSL
时,该参数的可用值为PLAIN
、GSSAPI
、OAUTHBEARER
。
sasl_plain_usernam
和sasl_plain_password
,设置SASL PLAIN认证使用的用户名和密码,这个信息就是在Broker使用的kafka_server_jaas.conf
文件中配置的。如果sasl_mechanism
设置成PLAIN
的话,这两个必须配置。
生产者和消费者都是这么配置,至于这个用户有什么权限,比如读、写等需要在服务端设置ACL。如果遇到check_version错误,则在配置中需要制定版本
api_version=(0,10)
。
Java
方式一:
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, “node01:9092”);
props.put(“acks”, “all”);
props.put(“retries”, 0);
props.put(“batch.size”, 16384);
props.put(“linger.ms”, 1);
props.put(“buffer.memory”, 33554432);
props.put(“key.serializer”, “org.apache.kafka.common.serialization.StringSerializer”);
props.put(“value.serializer”, “org.apache.kafka.common.serialization.StringSerializer”);
// 这里设置SASL连接
props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, “SASL_PLAINTEXT”);
props.put(SaslConfigs.SASL_MECHANISM, “PLAIN”);
props.put(“sasl.jaas.config”,
“org.apache.kafka.common.security.plain.PlainLoginModule required username=\”alice\” password=\”123456\”;”);
方式二:
System.setProperty("java.security.auth.login.config", "/tmp/kafka_client_jaas.conf"); //配置文件路径
props.put("security.protocol", "SASL_PLAINTEXT");
props.put("sasl.mechanism", "PLAIN");
而上面那个文件的内容就是:
KafkaClient {
org.apache.kafka.common.security.plain.PlainLoginModule required
username="alice"
password="123456";
};
修改Broker配置文件
在server.properties
增加如下内容:
# 配置listener
listeners=SASL_PLAINTEXT://x-x-x-x:9092
advertised.listeners=SASL_PLAINTEXT://x-x-x-x:9092
# 认证方面
# 表示Broker间通信使用SASL
security.inter.broker.protocol=SASL_PLAINTEXT
# 表示开启PLAIN认证机制
sasl.enabled.mechanisms=PLAIN
# 表示Broker间通信也启用PLAIN机制
sasl.mechanism.inter.broker.protocol=PLAIN
# 授权方面
# 设置身份验证使用的类
authorizer.class.name=kafka.security.auth.SimpleAclAuthorizer
# 设置超级账号,如果是多个需要分号分割,例如:User:admin;User:root
super.users=User:admin
# 对所有用户topic可见,要禁用。
allow.everyone.if.no.acl.found=false
服务端的另外一种配置
Broker还可以使用sasl.jaas.config
配置JAAS。字段是:listener.name.{listenerName}.{saslMechanism}.sasl.jaas.config
修改server.propertis文件
listener.name.sasl_plaintext.plain.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
username="admin" \
password="admin" \
user_admin="admin" \
user_tom="123456" \
user_alice="123456";
listener.name
:固定格式
{listenerName}
:就是上面的sasl_plaintext
,这里和linsteners
中配置的协议要一致。
{saslMechanism}
:就是上面的plain
sasl.jaas.config
:固定格式
下面是另外一种listener.name,这种是使用sasl的scram,:
listener.name.sasl_plaintext.scram-sha-256.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \
username="admin" \
password="admin-secret";
所谓listener
就是:listeners=SASL_PLAINTEXT://x-x-x-x:9092
就是这个语句配置的,因为你可以看到地址前面配置了协议。换句话说Broker可以配置多个验证方式,用逗号分隔即可PLAINTEXT://localhost:9092,SSL://localhost:9091
。
注意:如果使用在
server.propertis
文件中设置JAAS的方式,那么就不用建立kafka_server_jaas.conf
文件。不过由于PLAIN形式是基于文件配置的用户名和密码,所以建议使用单独的kafka_server_jaas.conf
,毕竟用户多了会造成server.propertis
比较乱。对于SCRAM机制的话可以配置在server.propertis
里,毕竟它的用户是存放在Zookeeper中。
修改Broker启动脚本
编辑./kafka-server-start.sh
增加一个环节变量:
export SECURITY_OPTS="-Djava.security.auth.login.config=/opt/java/kafka/config/kafka_server_jaas.conf"
主要是让kafka-run-class.sh
启动脚本加载这个文件。在kafka-run-class.sh
增加下面的内容:
KAFKA_OPTS="$SECURITY_OPTS $KAFKA_OPTS"
默认kafka-run-class.sh
文件中的$KAFKA_OPTS
是空的,如下:
# Generic jvm settings you want to add
if [ -z "$KAFKA_OPTS" ]; then
KAFKA_OPTS=""
fi
所以你可以直接修改这个变量为-Djava.security.auth.login.config=/opt/java/kafka/config/kafka_server_jaas.conf"
,而不需要建立SECURITY_OPTS
环境变量。
说明:如果Broker的JAAS通过在
server.properties
文件中的sasl.jaas.config
参数配置,那么就不需要修改启动脚本。因为修改启动脚本是为了让它去加载外部的JAAS文件,如果这个文件的内容都在Broker启动使用的server.properties
文件中配置了,那么在启动的时候就无需加载外部文件了。
配置用户的ACL
列出ACL设置
列出所有主题的ACL设置
kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --list
列出指定主题的ACL设置
kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --list --topic TOPIC_NAME
授权用户
# 允许 alice 用户读取和写入test主题
./kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --add --allow-principal User:alice --operation Read --operation Write --topic test
# 允许GroupA组使用alice用户对主题test读写操作
./kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --add --allow-principal User:alice --operation Read --operation Write --topic test --group GroupA
# 允许所有用户对某个主题有读写操作
./kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --add --allow-principal User:* --consumer --topic test
# 拒绝某个主机使用alice账号读写,允许其他所有的主机使用任何账号进行读写某个主题
./kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --add --allow-principal User:* --allow-host * --deny-principal User:alice --deny-host 198.51.100.3 --consumer --topic test
如果是删除操作就把上面的 --add 改成 --remove。
--operation支持的操作有:READ、WRITE、DELETE、CREATE、ALTER、DESCRIBE、ALL,所以一般我们快捷方式授权:
- 授权给某个消费者组读取和订阅权限就直接使用
--consumer
参数而不使用--operation
来指定,前者就包含了允许消费者在主题上READ、DESCRIBE以及在消费者组在主题上READ。 - 授权给某个消费者组写和订阅权限就直接使用
--producer
参数,它包括了生产者对主题有写和订阅权限以及在集群上建立主题的权限。
# 快捷设置允许某个组使用某用户对某主题读操作
./kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --add --allow-principal User:alice --consumer --topic test --group GroupA
# 快捷设置允许某用户对某主题写操作
./kafka-acls.sh --authorizer-properties zookeeper.connect=x.x.x.x:2181/CHROOT --add --allow-principal User:alice --producer --topic test
如果授权用户同上读写那么就要手动设置
--operation
参数
配置SASL\SCRAM
SASL\SCRAM都属于SASL认证框架范围,它主要解决了PLAIN机制中密码铭文问题以及动态更新问题,解决动态更新用户的机制就是用户信息存放在Zookeeper中。
创建kafka_server_jaas.conf文件
KafkaServer {
org.apache.kafka.common.security.scram.ScramLoginModule required
username="admin"
password="admin";
};
这个文件的作用上面以及说过了,不过在这种SASL\SCRAM机制下,这个文件只需要配置这个部分,因为用户信息在Zookeeper中。
修改Broker配置文件
# 设置监听使用SASL而不是SSL
listeners=SASL_PLAINTEXT://172.16.247.100:9092
advertised.listeners=SASL_PLAINTEXT://172.16.247.100:9092
# Zk连接设置
zookeeper.connect=172.16.247.100:2181/kafka
# 认证部分
# 表示开启SCRAM认证机制,并启用SHA-256算法
sasl.enabled.mechanisms=SCRAM-SHA-256
# 表示Broker间通信也要开启SCRAM认证,同样适用SHA-256算法
sasl.mechanism.inter.broker.protocol=SCRAM-SHA-256
# 表示Broker间通信使用SASL
security.inter.broker.protocol=SASL_PLAINTEXT
#
listener.name.sasl_plaintext.scram-sha-256.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \
username="admin" \
password="admin";
# 授权方面
# 设置身份验证使用的类
authorizer.class.name=kafka.security.auth.SimpleAclAuthorizer
# 设置超级账号,如果是多个需要分号分割,例如:User:admin;User:root
super.users=User:admin
# 对所有用户topic可见,要禁用。
allow.everyone.if.no.acl.found=false
这里我通过sasl.jaas.config
这种方式来配置,那么上面的kafka_server_jaas.conf
这个文件就可以不用,之所以写主要是为了说明一下,如果你希望通过使用外部文件的方式那么你就需要这个文件,并且启动时修改启动脚本增加环境变量来引用这个文件。
请注意这里的ZK连接设置,我这里使用了chroot,172.16.247.100:2181/kafka
,指定了一个路径,这个路径要提前在ZK中创建,create /kafka kafka
。
说明:官网这里设置的
security.inter.broker.protocol
这个参数是SASL_SSL
,包括listeners
也是SASL_SSL
,需要说明一下如果你启用了SSL加密通信的话你就设置,如果你没有启用那么就设置SASL_PLAINTEXT
就好。
修改Broker启动脚本
如果使用外部kafka_server_jaas.conf
文件方式,则需要修改启动脚本,把下面的环境变量添加到kafka-server-start.sh
脚本中。如果采用配置方式则无需修改脚本。
export SECURITY_OPTS="-Djava.security.auth.login.config=/work/kafka_2.11-2.3.0/config/kafka_server_jaas.conf"
export KAFKA_OPTS="$SECURITY_OPTS $KAFKA_OPTS"
创建用户
添加三个用户
./kafka-configs.sh --zookeeper localhost:2181/kafka --alter --add-config 'SCRAM-SHA-256=[password=admin],SCRAM-SHA-512=[password=admin]' --entity-type users --entity-name admin
./kafka-configs.sh --zookeeper localhost:2181/kafka --alter --add-config 'SCRAM-SHA-256=[password=123456],SCRAM-SHA-512=[password=123456]' --entity-type users --entity-name tom
./kafka-configs.sh --zookeeper localhost:2181/kafka --alter --add-config 'SCRAM-SHA-256=[password=123456],SCRAM-SHA-512=[password=123456]' --entity-type users --entity-name lucy
创建用户时ZK的连接路径也要写上chroot,如果你使用了chroot的话。如果你kafka配置中写了,而这里创建用户没有写,那么你启动kafka集群就会报验证失败的错误。
另外再说一下为什么要创建admin
用户,在配置文件sasl.jaas.config
中或者是kafka_server_jaas.conf
文件中的KafkaServer
字段,定义了Broker间连接使用的用户名和密码,这个用户名和密码可以是你自己定义的,不一定是admin
,这只是一个名字。另外这里只定义了Broker连接使用的用户名和密码,但是不是创建,所以之后我们要创建这个用户,所以就是上面的语句为什么要创建admin
账号。即使是单台单实例的场景下,Broker启动会连接ZK,如果启用了SASL,那么它会使用配置的账号到ZK中去验证。/KAFKA_CHROOT/config/users/
节点下存放着用户。
查看所有用户
./kafka-configs.sh --zookeeper localhost:2181 --describe --entity-type users
创建主题
创建主题的时候有两种方式一个是使用--zookeeper
和--bootstrap-server
,前者不需要认证因为它不连接broker,但是后者需要。
创建文件kafka_client_jaas.conf
创建主题的命令也是客户端,你broker配置了认证,那么这个客户端工具连接broker需要携带认证信息,这里配置的就是客户端使用的用户名和密码,这里我们就使用之前创建的admin
KafkaClient {
org.apache.kafka.common.security.scram.ScramLoginModule required
username="admin"
password="admin";
};
创建文件adminclient-configs.conf
内容如下:这里的设置要和broker配置的认证方式一致
security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-256
修改kafka-topics.sh
脚本内容
添加如下内容:
export KAFKA_OPTS="-Djava.security.auth.login.config=/work/kafka_2.11-2.3.0/config/kafka_client_jaas.conf"
运行kafka-topics.sh命令
./kafka-topics.sh --list --bootstrap-server 172.16.42.100:9092 --command-config ../config/adminclient-configs.conf
命令行工具测试生产和消费
修改consumer.properties
配置文件
security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-256
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="lucy" password="123456";
修改producer.properties
配置文件
security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-256
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="tom" password="123456";
配置ACL
这个和之前是一致的,所以不再进行说明。
Kafka在什么时候会检测权限
kafka授权,如果使用**非管理账号*,程序启动会连接broker来获取元数据,从metadata返回的topic列表时,也是根据acl过滤过的,也就是这个账号没有读取某个主题的权限,那么返回的列表中将不会有这个主题。
我们工作中有一个kafka监控程序,用于监控消费者的偏移量,使用的是一个开源的go开发的exporter,它会读取broker的元数据以及消费__consumer_offsets
主题。我们使用的非管理账号,在授权的时候我们给这个账号授予所有主题的读取权限,因为通过读取__consumer_offsets
获取主题的消费者偏移量,也会受到你所使用账号是不是对某个主题有读取权限的限制。所以如果使用非管理账号那么最好的办法就是授权该账号对所有主题具有读权限。