我们先把话说在前面:Kafka 跑起来很容易,但生产环境的 Kafka 集群从来不是“能收发消息”就完事了。搞大数据平台的人迟早都会遇到这么一个问题:集群内网明明做了隔离,但某天安全审计说你所有 Broker 端口裸奔,没有鉴权,随便来个人只要网络通就能消费生产数据。我之前陪着某团队做平台整改,三个集群、几十个业务方全部对接安全认证,前前后后踩了小一个月的坑,今天把这些经验整理出来,希望能让后来的人少走弯路。
先说清楚,这篇内容围绕 Kafka 的安全认证机制展开,核心包括三块:链路加密(SSL/TLS)、身份认证(SASL)、权限管控(ACL)。适合正在做大数据平台运维、消息中间件统一管控、或者被安全合规压着必须给 Kafka 上锁的工程师阅读。本文不会停留在罗列概念的层面,而是直接给出可落地的配置、参数、坑点和排障思路。
1. 先理清楚 Kafka 安全认证到底护住了什么
1.1 从三个维度看待 Kafka 安全:传输、身份、权限
安全审计来的时候,不会只问“你有没有密码”,他们会拆成三件事来问。
第一件事:网络上传输的数据有没有可能被别人抓包看到?这个靠SSL/TLS 链路加密解决,也就是配置SSL://协议。Kafka 客户端与 Broker 之间传输的内容如果走明文,那就是裸奔,内网抓包直接能看到消息体。
第二件事:连接 Kafka 的一方到底是什么身份?这个靠SASL 认证解决。Kafka 支持多种认证机制,最常用的是 PLAIN、SCRAM、GSSAPI(Kerberos)、OAUTHBEARER。认证是“你和 Broker 之间确认彼此身份”的过程,必须发生在客户端发消息之前。
第三件事:即使知道你是谁,你到底能动哪些数据?这个靠ACL 授权解决。Kafka 的 ACL 定义了“谁(Principal)可以对哪个资源(Topic/Consumer Group)做什么操作(Read/Write/Describe)”。很多人以为配好账号密码就安全了,实际上不配 ACL,光认证通过的用户默认有集群超级权限,这比不认证更可怕——等于门卫问你要了工牌,但不管进去之后能拿什么。
这三层需要放在一起理解:SSL 解决“通信过程怕被偷听”,SASL 解决“你是谁”,ACL 解决“你能干什么”。如果时间紧,认证和授权必须一块上,传输加密可以后补,但千万不能只做用户密码不设 ACL。
1.2 四种监听器协议组合该怎么选
Kafka 的listeners是个多协议配置,常见组合包括PLAINTEXT、SSL、SASL_PLAINTEXT、SASL_SSL四种。刚接触的人容易被这些枚举名绕晕,其实拆开来看就明白了:下划线前面是“认证方式”,下划线后面是“传输方式”。
| 监听器协议 | 认证方式 | 传输方式 | 适用场景 |
|---|---|---|---|
| PLAINTEXT | 无认证 | 明文传输 | 仅本机调试,单机演示 |
| SSL | 无认证(互相认证除外) | TLS加密 | 只要求传输加密,不需要登录身份 |
| SASL_PLAINTEXT | SASL认证 | 明文传输 | 内网可信环境,主要做身份管控 |
| SASL_SSL | SASL认证 | TLS加密 | 生产最推荐,跨安全域、跨机房必选 |
我自己的倾向是,任何有多业务方接入的测试环境以上规模,直接用SASL_PLAINTEXT先把账号体系建起来,后续有条件再升级到SASL_SSL。如果一上来就SASL_SSL,证书签发和管理工作会和业务适配纠缠在一起,排障成本瞬间翻倍。
这里有一点容易忽略:advertised.listeners必须和实际内网访问地址保持同协议。曾经有团队把listeners配成 SASL_SSL,但advertised.listeners忘了同步改,客户端连上后拿到的是“内网普通 PLAINTEXT 地址”,导致一堆诡异的握手错乱。
1.3 为什么生产环境优先选 SCRAM 而不是 PLAIN 或 Kerberos
PLAIN 是很多离线教程里用过的机制,只把用户名密码通过 SASL 握手发给 Broker,实现简单,但它的缺点非常致命:PLAIN 的密码必须明文存储在 Kafka 的 JAAS 文件里,Broker 重启、多节点同步、密码轮换都非常痛苦。而且 PLAIN 的接口不能动态创建用户,只能改配置重启,这在多业务方动态接入的场景下不可接受。
Kerberos 在传统大数据集群(Hadoop/Hive 体系)里地位很高,但它依赖额外的 KDC,票据过期策略、跨域名 realm 映射、客户端 keytab 分发,每一件都够折腾一阵子。如果公司没有已经运维良好的 KDC 基础设施,不要强行上 Kerberos,维护成本会吃掉所有安全收益。
SCRAM(Salted Challenge Response Authentication Mechanism)是目前平衡安全和运维的最佳实践。它基于挑战-响应机制,密码以摘要形式存在服务端,不会在网络中明文传输,同时支持通过kafka-configs.sh动态创建用户和修改密码。也就是说,加一个业务方接入不需要重启 Broker,只要发给他账号和密码即可。对生产环境来说,这个“免重启新增用户”是刚需。
2. 动手之前要捋清的版本和部署前缀
2.1 版本差异直接决定配置文件长什么样
Kafka 版本演进中,配置项的变化很大。如果是 0.11 以前的老版本,安全配置是另一套写法。目前主流是 2.x 以及 3.x。2.x 阶段authorizer.class.name是常规配置,3.x 之后 KRaft 逐步替代 ZooKeeper,Server 端配置结构有所调整。
在开始之前,我强烈建议先用kafka-broker-api-versions.sh确认集群的精确版本,再查官方文档里对应版本的“Security”章节。网上搜到的配置,如果不问版本直接抄,大概率会踩到listener.name.{listenerName}.{mechanism}.sasl.jaas.config新旧格式不兼容的问题。
举个例子:2.6 之前,SASL/SCRAM 的 JAAS 可以写在config/server_jaas.conf里,再用KAFKA_OPTS=-Djava.security.auth.login.config=/path/server_jaas.conf引入。2.7 之后,官方更推荐在server.properties里直接写listener.name.sasl_ssl.scram-sha-256.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required;这种独立配置块。你只有搞清楚版本,才能判断该采用哪种方式。
2.2 KRaft 模式下的安全认证部署方式不同
Kafka 3.3 以后,KRaft 模式(不再依赖 ZooKeeper)已经可以用于生产。KRaft 模式下,Controller 和 Broker 本身就是两个独立监听器,如果不把安全机制覆盖到 Controller 的监听器上,就等于你给数据面加了一把锁,但控制面大门敞开。控制面被入侵,集群元数据、分区副本分配、Leader 选举全部能被操纵。
我的建议是,在 KRaft 模式下,要么全部监听器统一切到SASL_SSL+ SCRAM,要么至少给CONTROLLER监听器配上单独的存储认证机制。Controller 的认证和 Data Plane 的认证要分清,否则你改了 Broker 的认证,controller 互相通信还是老配置,日志里会反复出现 cluster authorization failed。
另外,KRaft 模式创建账号的方式也有变化。ZooKeeper 模式可以走kafka-configs.sh --zookeeper ...来写用户,KRaft 模式必须用--bootstrap-server指定 controller 或 broker,而且需要先配置一个超级用户才能初始化。这个顺序是:先在 server.properties 里设置一个超级用户(super.users),启动 controller,再用超级用户创建后续的普通 SCRAM 用户。
2.3 证书规划千万别一把梭
如果决定上 SSL 传输加密,“证书”这一块要提前规划,否则后面给几十个客户端分发证书会非常痛苦。
第一种方案是自己建根 CA(自签 CA),为每个 Broker、每个客户端签发独立证书,并把根 CA 证书放到各端的 truststore。完整程度高,但签名和分发工作量也大。
第二种做法是用统一证书。团队内部网络环境,所有 Broker 共用一份证书,域名/IP 通过SAN扩展写进去。客户端只需要配一个 truststore 加上客户端自己的 keystore,复杂度大大降低。代价是一旦证书泄漏,整个集群的所有节点都受影响。
我的经验是:中小规模集群先统一证书,配合内网 IAM 体系用短期证书,半年轮换一次。如果被审计要求严格,再上独立证书并配合自动化签发工具。
等价的说法是:如果你还不打算做自动化证书轮换,就不要一步到位玩“每个节点不同证书、不同 hostname”的形态。布鲁诺前车之鉴是,某团队第一周就把几十个证书签好了,第二周因为机房 IP 段调整全部作废重签,浪费的时间足够再搭一整套认证。
3. 从零把 Kafka 上线 SASL/SCRAM + ACL 的完整实操
3.1 第一步:Broker 端配置认证
我们以 3.4 版本、部署在/data/kafka目录下为例。先编辑config/server.properties,以下给出核心配置块。
# 监听器与安全协议配置 listeners=SASL_PLAINTEXT://0.0.0.0:9092 advertised.listeners=SASL_PLAINTEXT://kafka-001.example.internal:9092 # 这里按业务网段自行替换 # 允许所有机器都经过 SASL 认证 listener.security.protocol.map=SASL_PLAINTEXT:SASL_PLAINTEXT # 指定认证机制 sasl.enabled.mechanisms=SCRAM-SHA-256 listener.name.sasl_plaintext.scram-sha-256.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required; # 授权相关 authorizer.class.name=kafka.security.authorizer.AclAuthorizer super.users=User:admin有几个点我需要刻意提醒:第一,listener.name.sasl_plaintext.scram-sha-256.sasl.jaas.config中listener.name后面的值要与你实际配置的监听器名严格对应。很多同志把sasl_PLAINTEXT大写小写混淆,或者没注意到监听器名全部要转换成小写再点分割,结果 Broker 启动后外部客户端始终报“Unsupported SASL mechanism”。第二,如果同时启用多套认证机制,比如 SCRAM 与 PLAIN 共存,要在sasl.enabled.mechanisms里同时列出来,但建议业务初期不要混用,多机制会成倍增加排障难度。
重启完 Broker,正常情况下日志里应该看到类似Registered broker 0 at path ... with epoch ...的输出,并出现与sasl相关的一堆初始化日志。此时用kafka-configs.sh --bootstrap-server还不能直接创建用户,因为 admin 账号还没建,需要管理员权限。
所以提前在server.properties里配置super.users=User:admin是有意为之——先给集群做一个“人肉超级管理账号”,等启动完成后,用这个账号去创建运维或者业务账号。后面 SLF4J 日志会出现“User admin has been successfully logged in”这类握手日志,看到这类信息就说明认证已经被启用。
3.2 第二步:创建 SCRAM 用户与凭据
通过kafka-configs.sh动态创建用户。在 3.x 版本中推荐使用--bootstrap-server操作,不要再用旧版--zookeeper方式。
# 先配置管理用户环境变量 export KAFKA_OPTS="-Djava.security.auth.login.config=/data/kafka/config/kafka_client_jaas.conf" # 创建业务账号 kafka-configs.sh \ --bootstrap-server kafka-001.example.internal:9092 \ --alter \ --add-config 'SCRAM-SHA-256=[password=my-secure-pass]' \ --entity-type users \ --entity-name app_ data_consumer注意看上面这段,entity-name就是登录 Kafka 时使用的用户名,最终在 ACL 授权里看到的 Principal 为User:app_data_consumer。
创建完可以验证:
kafka-configs.sh \ --bootstrap-server kafka-001.example.internal:9092 \ --describe \ --entity-type users \ --entity-name app_data_consumer输出中应该显示SCRAM-SHA-256的摘要信息。SCRAM 机制的本质是“服务端只存盐值和迭代替换后的摘要”,客户端每次握手时带着用户名发起挑战,因此即使配置文件泄漏,密码原文也不会直接暴露。
这里注意一点:SCRAM 用户列表存在 ZooKeeper 或 KRaft 元数据中,不是存在本地文件。如果集群有多个节点,创建的用户会被所有节点识别,不需要逐台添加。
3.3 第三步:给业务账号配上足够但不越权的 ACL
这一步是安全策略的临门一脚。不做 ACL,相当于能登录就拥有一切权限。做了 ACL 之后,即便密码泄漏,攻击者也只能在有限的授权范围内操作,这是纵深防御的关键。
Kafka 的 ACL 是基于“资源 + 操作 + 用户”三元组授权的,常用到的是 Topic 的读写,Consumer Group 的读和 Group 描述等。以授权app_data_consumer用户消费order_event主题为例:
kafka-acls.sh \ --bootstrap-server kafka-001.example.internal:9092 \ --add \ --allow-principal User:app_data_consumer \ --operation Read \ --operation Describe \ --topic order_event \ --group order_event_group这意味着这个用户能够消费order_event的数据,同时能查看这个消费组的 Offset、Lag 状态。注意,如果这里没有授权 Describe 操作,客户端在启动消费时即使能连接 Broker,也会在获取 group 元数据或提交 offset 时收到 TOPIC_AUTHORIZATION_FAILED 或 GROUP_AUTHORIZATION_FAILED。
生产端授权类似:
kafka-acls.sh \ --bootstrap-server kafka-001.example.internal:9092 \ --add \ --allow-principal User:app_data_producer \ --operation Write \ --operation Describe \ --topic order_event如果业务方需要同时生产多个主题,可以一次性传多个--topic参数,避免重复执行脚本。这里有个细节:Describe操作对于生产客户端也不是废话,没有它,客户端连“这个主题是否存在、分区 leader 是谁”都无法感知,直接连不上。
还有一类内网平台常见的权限:查主题列表、查看消费组列表。很多平台型业务经常要向运维要这权限,建议默认不给,统一收口到运维账号,否则 ACL 形同虚设。
提示:不要一上来就敲一堆授权脚本,先给业务方最小权限,配合业务上线过程逐步放开。前期半步不放,后期全量放开,都会让你被业务方反复拉群“下一步还报错”,效率极低。
3.4 第四步:客户端侧配置与接入适配
服务端改了协议,所有现存客户端基本无法直接使用,这是必然的,没人能绕过这一步。以 Java 客户端为例,生产者和消费者都要在Properties里补上认证相关配置:
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_PLAINTEXT"); properties.put(SaslConfigs.SASL_MECHANISM, "SCRAM-SHA-256"); properties.put(SaslConfigs.SASL_JAAS_CONFIG, "org.apache.kafka.common.security.scram.ScramLoginModule required " + "username=\"app_data_producer\" password=\"my-secure-pass\";");如果用的是 Spring Kafka,则对应地配置:
spring: kafka: bootstrap-servers: kafka-001.example.internal:9092 properties: security.protocol: SASL_PLAINTEXT sasl.mechanism: SCRAM-SHA-256 sasl.jaas.config: org.apache.kafka.common.security.scram.ScramLoginModule required username="app_data_producer" password="my-secure-pass";常见的新人疑问是:JAAS 配置中的SaslConfigs.SASL_JAAS_CONFIG是不是所有客户端版本都支持?其实这是 2.x 版本加入的,如果客户端版本太老,必须退回到-Djava.security.auth.login.config指向的 JAAS 文件。生产上强制客户端依赖版本在 2.3 以上,可以省掉一堆兼容性排障。
Python 客户端(基于 confluent-kafka)的配置示例也一并给了,方便多语言团队参考:
conf = { 'bootstrap.servers': 'kafka-001.example.internal:9092', 'security.protocol': 'SASL_PLAINTEXT', 'sasl.mechanism': 'SCRAM-SHA-256', 'sasl.username': 'app_data_producer', 'sasl.password': 'my-secure-pass' }3.5 第五步:验证配置是否真正生效
不要只看“客户端能连上、能收发消息”就认为认证没问题。我见过太多例子:客户端自己配置了错误的security.protocol,但因为 Kafka 服务端仍然同时监听了明文端口,结果客户端以明文方式连上去了,像裸奔一样运行了数月。
用 CLI 工具做一次针对性验证:
kafka-console-producer.sh \ --bootstrap-server kafka-001.example.internal:9092 \ --topic order_event \ --producer.config /path/client_sasl.properties其中client_sasl.properties必须包含与生产环境一致的认证配置。执行成功,再查看 Broker 的服务端日志,名称匹配的监听器下会出现用户登录记录和 ACL 校验日志。
我还习惯在服务端开启审计日志来观察 ACL 行为,可以灵活设置log4j.properties中与kafka.authorizer.logger相关的日志级别为DEBUG,只在排障时开启,用完再关掉,避免长期刷大量日志影响磁盘。
4. 现场踩坑实录:明明认证通过了,授权又卡住
4.1 坑一:所有客户端都报 Principal 无法解析
现象:客户端输入了正确的用户名密码,但连接时提示Server failed to authenticate: User:xxx not found,或者Authentication failed。
排查时先查 Broker 端server.log,确认该用户是否在USER中存在;确认是不是走了--bootstrap-server对Kraft模式创建到了错误的存储分区。还有一个容易被忽视的点:用户名不要带空格、不要包含中划线等特殊字符,Kafka 对 Principal 的解析会处理异常字符。
4.2 坑二:明明能生产,消费组一直在 REBALANCE
现象:生产端已经能正常发消息,但消费端每隔几十秒就重新平衡。
通常原因有两种。第一种是消费组相关的 ACL 没有配全,尤其遗漏了Describe权限导致的 group 元数据读权限;第二种是网络抖动,但如果你引入了认证,首先要怀疑是否在 ACL 中遗漏了--group授权。用kafka-consumer-groups.sh --describe手动查一下,如果报错GROUP_AUTHORIZATION_FAILED,基本就实锤了。
4.3 坑三:Broker 节点间互相认证不过
现象:KRaft 模式下,Controller 与 Broker 节点之间出现周期性的Connection to node -1 (host/kafka-002...) terminated authentication failed。
这个坑很经典——你给数据面配了SASL_PLAINTEXT认证,然后忘了给CONTROLLER监听器加独立认证配置。不同节点上的 controller 和 broker 使用不同 JAAS 配置,导致互相不认。
如果你不打算为 controller 维护额外账号体系,有一个巧方案:把 controller 和 broker 的监听器认证都放到同一个 SCRAM 用户上,虽然不符合最小权限原则,但能保证内聚快速跑通。等业务稳定后在拆分,别一上来就和多账号体系死磕。
4.4 坑四:ACL 中看到允许条目,但客户端依然报 TOPIC_AUTHORIZATION_FAILED
对于 Kafka ACL,它的判定逻辑不是“没有显式拒绝则放行”,而是“没有显式允许则拒绝”,默认白名单原则。所以一条消费报错,必须做三检查。
- 检查用户本身是否为
super.users成员; - 检查 topic 授权包含 Read 和 Describe;
- 检查 group 授权包含 Read 和 Describe。
另外,client ID 信息也会出现在某些授权错误日志里,但 ACL 默认不针对 client ID,别被误导。
4.5 坑五:客户端能访问所有 topic 的元数据
现象:某个用户只是授权了一个 topic,但用kafka-topics.sh --list却把整个集群的主题列表拉了出来。
这通常说明super.users里配置了该用户,或者该用户被误配置成超级用户。超级用户会绕过所有 ACL 检查,这是安全审计里最敏感的配置之一。我记得有次处理某平台整改,排查到最后居然发现为了图省事,把一批测试账号都塞进了super.users,整个集群的权限体系等于摆设。这个配置只保留运维管理员账号,业务账号一律走 ACL。
4.6 给新接入 Kafka 团队的一个排障顺序建议
遇到安全认证问题,我建议按这个顺序排查,能节省大量时间:
- 确认客户端和服务端版本,再查配置语法;
- 看 Broker 端
server.log,尤其是Authentication和Authorization相关行; - 用同一个用户,在能通到 Broker 的机器上手动敲 CLI 命令,区分是配置问题还是网络到 Broker 链路问题;
- 用
--describe确认 ACL ,并确认用户是否存在、是否在super.users中; - 最后才考虑是不是代码或框架封装的锅,这一步排查优先级最低,因为框架问题往往在日志里会有明显的异常堆栈。
这套顺序我给过好几拨人,每次都能让他们从“瞎试”状态拉回来。最怕的是一上来就翻代码改代码,然后越改越乱,最后连原本能跑通的配置也被改坏。
结尾一点体会
我在实际运维 Kafka 的过程中体会到:安全认证不是一次性的配置动作,而是要当作一项基础设施能力持续建设。它和你的业务接入流程、账号生命周期管理、证书或密钥轮换机制是深度绑定的。今天你说“先让业务跑通,安全后补”,过三个月再回头往往要推倒重来,还得同时安抚几十个被断连的业务方,那种场面非常消耗信任。
我的建议是:即便初期业务很小,也至少把 SCRAM + ACL 跑通,用最小权限模型对接业务方。传输加密可以等证书体系成熟后再升级,但身份和权限的骨架一定要从第一天就立起来。等以后安全审计主动来找你,你这边只需要拿出配置清单和授权策略就能从容应对,而不是临时补课。
如果你现在正准备给 Kafka 集群开启认证,优先把文中的 Broker 端配置跑一遍,再拿一个真实业务主题做端到端验证。脚本敲完、消息通掉的那一刻,你会发现这个过程的难点其实不在于机制原理,而在于每个环节的细节契合度,而细节恰恰是这篇文章里花了最大篇幅去抠的部分。