Sayai头像
ZooKeeper 未授权访问漏洞修复实战:SASL 认证 + ACL 全树收权,Kafka/Hadoop/ClickHouse 多组件适配封面图

ZooKeeper 未授权访问漏洞修复实战:SASL 认证 + ACL 全树收权,Kafka/Hadoop/ClickHouse 多组件适配

ZooKeeper 未授权访问漏洞修复:SASL 双层认证、ACL 全树收权与 Kafka/Hadoop/ClickHouse 多组件适配

安全扫描甩来一份报告:ZooKeeper 未授权访问。拿四字命令一试,2181 端口果然裸奔——不用任何账号就能看到整套集群的节点信息和数据节点树。真正的麻烦不在改 ZooKeeper 本身,而在这套 ZK 上还挂着 Kafka、Hadoop、ClickHouse 三家:认证一开,它们全都会因为拿不出凭据被拒之门外。本篇记录这次修复的完整过程:ZK 双层 SASL 认证 → 三个组件逐一适配 → ACL 全树收权 → 全链路验证,配置在 ZooKeeper 3.8.4 + Kafka 3.9.1 生产集群验证通过。

系列导航

篇场景链接
一内置 ZooKeeper 的 Kafka 集群加 SASL 全套配置已发布,点击直达
二外置 ZooKeeper,只做 kafka↔zk 之间认证已发布,点击直达
三外置 ZooKeeper 完整版:quorum 认证 + zk ACL + 客户端认证已发布,点击直达
四漏洞修复实战:多组件适配 + ACL 全树收权(本篇)本篇

一、漏洞原理与复现验证

1.1 先确认漏洞真实存在

拿到报告先别急着改,四字命令(four-letter words)一发就知道是不是误报:

[root@hadoop-node1 ~]# echo srvr | nc 192.168.10.11 2181
Zookeeper version: 3.8.4--9d5ba68f2445b6c8ab67c97ec36c9a9ca4c9f74b, built on 2024-02-15 20:47
Latency min/avg/max: 0/2.1/182
Received: 1284753
Sent: 1284691
Connections: 12
Outstanding: 0
Zxid: 0x1a3
Mode: leader
Node count: 3482

[root@hadoop-node1 ~]# echo ruok | nc 192.168.10.11 2181
imok

没有账号、没有密码,直接吐出集群版本、角色、连接数和节点总数——这就是"未授权访问"的实锤。

1.2 危害比看起来大

2181 能裸连,意味着攻击者可以:

  • 用自带 zkCli 直接连上,ls / 枚举出 brokers、controller、consumers 等全部路径,摸清 Kafka 集群拓扑;
  • 读取 broker 注册信息、消费者位移,甚至直接改写;
  • 最坏情况一条 deleteall / 把元数据连根拔起,Kafka、Hadoop HA、ClickHouse 副本同步全部瘫痪。

之前写 Kafka SASL 系列(二) 时就强调过:zk 里存的是整个大数据平台的元数据底座,这条通道必须锁。

1.3 修复目标

  • 认证:quorum(zk 节点之间)与 server-client(组件连 zk)双层 SASL,裸客户端一律拒绝;
  • 授权:ACL 全树收权,认证通过后也只放开必要的权限;
  • 不破坏业务:Kafka、Hadoop、ClickHouse 三个组件全部完成适配,修复后照常运行。

二、方案总览与影响面分析

2.1 谁在连这套 ZK

修复前必须把客户端盘点清楚,漏掉一家,重启后那家就直接瘫痪:

组件为什么连 ZK需要适配什么
Kafka 3.9.1broker 注册、controller 选举、位移存储kafka.jaas 加 Client 段 + zookeeper.set.acl
Hadoop(HA 集群)NameNode ZKFC 主备选举、YARN 状态存储zookeeper-jaas.conf + HADOOP_OPTS 挂 JAAS
ClickHouseReplicatedMergeTree 副本同步、leader 选举config.xml 的 <zookeeper> 加 <identity>

2.2 修复路线

停 Kafka → 停 Hadoop → 停 ClickHouse → 改 ZK 配置(SASL)
→ 起 ZK → 改 Kafka 并逐台起 → 改 Hadoop 并逐台起
→ 改 ClickHouse 并逐台起 → ACL 全树收权 → 全链路验证

ACL 收权一定放在最后:等所有组件都能带凭据正常连上之后再执行,否则 setAcl -R 一生效,没适配完的组件立刻 NoAuth。

环境约定:ZK 集群两节点 hadoop-node1/2(192.168.10.11/12),Kafka 在 kafka01(192.168.10.31),安装根目录 /opt/bigdata。本文密码用 ZkAdmin@123(super/quorum)、ZkUser@123(组件客户端)演示,上线前务必替换。

三、ZooKeeper 侧:开启双层 SASL 认证

这部分的配置与系列二、三完全一致,这里快速过一遍,每条参数的来龙去脉见系列(二)与系列(三)。

3.1 zk_server.jaas(服务端身份 + 客户端账号库)

集群每个节点都要做。进入配置目录:

[root@hadoop-node1 ~]# cd /opt/bigdata/zookeeper/apache-zookeeper-3.8.4-bin/conf

新建 zk_server.jaas,三段各管一件事:

Server {
    org.apache.zookeeper.server.auth.DigestLoginModule required
    user_super="ZkAdmin@123"
    user_zkuser="ZkUser@123";
};
QuorumServer {
    org.apache.zookeeper.server.auth.DigestLoginModule required
    user_quorum="ZkAdmin@123";
};
QuorumLearner {
    org.apache.zookeeper.server.auth.DigestLoginModule required
    username="quorum"
    password="ZkAdmin@123";
};
  • Server 段是账号库:user_xxx="密码" 定义了客户端可用的账号,后面 Kafka/Hadoop/ClickHouse 全部用 zkuser 连接;
  • QuorumServer/QuorumLearner 两段服务 quorum 认证:leader 与 follower 之间也要互相验明正身,防止伪造节点混进选举。

3.2 zk_client.jaas(zk 自带命令行工具用)

Client {
    org.apache.zookeeper.server.auth.DigestLoginModule required
    username="zkuser"
    password="ZkUser@123";
};

3.3 zoo.cfg 追加认证参数

# ===== Server-Server (quorum) 互相认证 =====
quorum.auth.enableSasl=true
quorum.auth.learnerRequireSasl=true
quorum.auth.serverRequireSasl=true
quorum.auth.learner.saslLoginContext=QuorumLearner
quorum.auth.server.saslLoginContext=QuorumServer
# 建议设为集群节点数 n*2(两节点配 4,三节点配 6)
quorum.cnxn.threads.size=4

# ===== Server-Client 之间认证 =====
authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
# 强制客户端必须 SASL 认证,裸连直接拒绝(版本要求 zk >= 3.6.0)
sessionRequireClientSASLAuth=true

3.4 启动脚本挂 JAAS

编辑 bin/zkServer.sh,开头加两行环境变量:

export SERVER_JVMFLAGS="${SERVER_JVMFLAGS} -Djava.security.auth.login.config=/opt/bigdata/zookeeper/apache-zookeeper-3.8.4-bin/conf/zk_server.jaas"
export CLIENT_JVMFLAGS="${CLIENT_JVMFLAGS} -Djava.security.auth.login.config=/opt/bigdata/zookeeper/apache-zookeeper-3.8.4-bin/conf/zk_client.jaas"

逐台重启 ZK:

/opt/bigdata/zookeeper/apache-zookeeper-3.8.4-bin/bin/zkServer.sh restart

zkServer.sh status 确认 leader/follower 角色正常,再进行下一步。

四、Kafka 适配

4.1 kafka.jaas 加 Client 段

在 Kafka 的 config 目录(/opt/bigdata/kafka/kafka_2.13-3.9.1/config)新建或编辑 kafka.jaas:

Client {
    org.apache.zookeeper.server.auth.DigestLoginModule required
    username="zkuser"
    password="ZkUser@123";
};

4.2 server.properties 开启 ACL

zookeeper.set.acl=true

4.3 逐台重启

cd /opt/bigdata/kafka/kafka_2.13-3.9.1
bin/kafka-server-start.sh -daemon config/server.properties

broker 在 /brokers/ids 正常注册、kafka-topics.sh --describe 不报错,说明 kafka→zk 认证通了。与本文环境同源的集群升级记录见 Kafka 2.8→3.9.1 跨大版本升级实战。

五、Hadoop 适配(ZKFC 与 YARN 的凭据)

5.1 为什么要单独适配

Hadoop HA 集群里连 ZK 的不止一个角色:NameNode 的 ZKFC 靠 ZK 做主备选举锁,YARN 的 ResourceManager 也可能用 ZK 存状态。sessionRequireClientSASLAuth=true 生效后,这些 JVM 拿不出 SASL 凭据会直接被 ZK 拒绝——表现就是 ZKFC 起不来、HA 沦为单点,比 Kafka 挂掉更隐蔽。

5.2 新增 zookeeper-jaas.conf

切换到 hadoop 用户,在 Hadoop 配置目录创建 JAAS 文件:

su - hadoop
cd $HADOOP_HOME/etc/hadoop
vi zookeeper-jaas.conf
Client {
    org.apache.zookeeper.server.auth.DigestLoginModule required
    username="zkuser"
    password="ZkUser@123";
};

5.3 挂进 HADOOP_OPTS

修改 yarn-env.sh 和 hadoop-env.sh,追加:

export HADOOP_OPTS="$HADOOP_OPTS -Djava.security.auth.login.config=$HADOOP_CONF_DIR/zookeeper-jaas.conf"

走全局 HADOOP_OPTS 的好处是 NameNode/ZKFC/ResourceManager 各守护进程统一继承,不用逐个角色单独配。

5.4 滚动重启

顺序原则:停和起都从同一台开始,逐台操作,确认上一台状态正常再动下一台。我们的做法是先停第三台(备角色所在节点),再停另外两台;启动时同样先起第三台,再起另外两台:

# 停:先第三台,后另外两台
$HADOOP_HOME/sbin/hadoop-daemon.sh stop zkfc
$HADOOP_HOME/sbin/hadoop-daemon.sh stop namenode
# 起时反过来:先第三台,后另外两台
$HADOOP_HOME/sbin/hadoop-daemon.sh start namenode
$HADOOP_HOME/sbin/hadoop-daemon.sh start zkfc

hdfs haadmin -getAllServiceState 确认一个 active 一个 standby、ZKFC 进程存活,YARN 的 yarn node -list 正常返回,适配完成。

六、ClickHouse 适配(identity 一行配置)

6.1 ClickHouse 与 ZK 的关系

ReplicatedMergeTree 系列表的副本同步、merge 协调、leader 选举全靠 ZK 上的副本日志。ZK 开认证后 CK 连不上,典型症状是写入变慢、system.replicas 里 is_alive=0、副本队列积压。建表侧的 ZooKeeper 用法可以回看 ClickHouse 可观测数据建模实战。

6.2 config.xml 加 identity

ZK 侧不需要为 CK 做任何额外配置,只在 CK 每个节点的 config.xml 的 <zookeeper> 标签内增加一行口令:

cd /etc/clickhouse-server
vi config.xml
<zookeeper>
    <!-- 新增:digest 认证账号,格式 user:password -->
    <identity>zkuser:ZkUser@123</identity>
    <node index="1">
        <host>hadoop-node1</host>
        <port>2181</port>
    </node>
    <node index="2">
        <host>hadoop-node2</host>
        <port>2181</port>
    </node>
</zookeeper>

6.3 重启顺序

先逐台重启 ZK(第三节的操作已完成),再逐台重启 ClickHouse:

systemctl restart clickhouse-server

SELECT hostName(), is_alive, queue_size FROM system.replicas 逐台核对副本健康。

七、ACL 全树收权

认证解决"你是谁",ACL 解决"你能干什么"。前面组件全部适配完成后,最后一步把整棵节点树的权限收给 zkuser。

7.1 登录 zkCli 并认证

在任意一个 ZK 节点执行(CLIENT_JVMFLAGS 把客户端 JAAS 挂进去):

cd /opt/bigdata/zookeeper/apache-zookeeper-3.8.4-bin/bin
export CLIENT_JVMFLAGS="${CLIENT_JVMFLAGS} -Djava.security.auth.login.config=/opt/bigdata/zookeeper/apache-zookeeper-3.8.4-bin/conf/zk_client.jaas"
./zkCli.sh -server 192.168.10.11:2181

7.2 addauth 再 setAcl

[zk: 192.168.10.11:2181(CONNECTED) 0] ls /
[admin, brokers, cluster, config, consumers, controller, controller_epoch, feature, isr_change_notification, kafka-acl, kafka-acl-changes, latest_producer_id_block, log_dir_event_notification, zookeeper]

[zk: 192.168.10.11:2181(CONNECTED) 1] addauth digest zkuser:ZkUser@123
[zk: 192.168.10.11:2181(CONNECTED) 2] setAcl -R / auth:zkuser:ZkUser@123:cdrwa
KeeperErrorCode = NoAuth for /zookeeper/config

-R 表示递归整棵树,cdrwa 是全部五种权限(创建/删除/读/写/管理)。auth:... 语法的含义:凡是已通过 digest 认证且身份匹配的会话都持有这套权限。

7.3 NoAuth 报错是预期行为

setAcl -R / 扫到 /zookeeper/config 时会报 KeeperErrorCode = NoAuth——这个节点由 ZooKeeper 自身管理(存放运行期配置),任何会话都不允许改它的 ACL,属正常现象,不影响其余节点收权。用 getAcl 验证:

[zk: 192.168.10.11:2181(CONNECTED) 3] getAcl /
'sasl,'zkuser
: cdrwa
'digest,'zkuser:xU8wKq2zQmVbN3cLdR7tYg==
: cdrwa

根节点已经同时挂上 SASL 与 digest 两套身份的权限,收权完成。

八、全链路验证清单

验证项命令/方法预期
裸客户端被拒不带 JAAS 直接 zkCli.sh -server 192.168.10.11:2181连接被 sessionRequireClientSASLAuth 拒绝
带凭据连接第三/五/六节方式挂 JAAS/identity 后连接正常执行 ls/get
四字命令echo ruok | nc 192.168.10.11 2181按白名单配置响应(默认白名单外不再吐信息)
Kafkakafka-topics.sh --describe + 生产消费试跑注册正常、消息收发正常
Hadoophdfs haadmin -getAllServiceState、yarn node -list一 active 一 standby、节点正常
ClickHouseSELECT is_alive, queue_size FROM system.replicas副本存活、队列不积压

九、踩坑清单

坑现象解法
ACL 收权做早了某组件未适配完,setAcl -R 后立刻 NoAuth 大面积报错收权永远放最后一步;出问题可用 super 账号登录救回
setAcl -R / 报 NoAuth扫到 /zookeeper/config 报 KeeperErrorCode = NoAuth该节点 ZK 自管,属预期,忽略即可
ZK 版本低于 3.6sessionRequireClientSASLAuth 不生效或不识别先升级 ZK,参考Kafka/ZK 平滑升级
quorum 线程数没调大流量下 quorum 认证握手排队、选举变慢quorum.cnxn.threads.size 设为节点数 ×2
JAAS 文件权限过松密码明文可被其他用户读取chmod 600 *.jaas,属最小加固动作
只改了单个 ZK 节点集群选举分裂所有 ZK 节点同步修改后再逐台重启
Hadoop 忘配 zookeeper-jaas.confZKFC 反复重启、HA 降级按第五节补齐 HADOOP_OPTS 再滚动重启
CK 只改了一个节点部分副本 is_alive=0config.xml 逐节点都加 <identity>

总结

这次漏洞修复的完整链路:四字命令复现漏洞 → ZK 双层 SASL(Server/QuorumServer/QuorumLearner + zoo.cfg)→ Kafka(kafka.jaas + zookeeper.set.acl)→ Hadoop(zookeeper-jaas.conf + HADOOP_OPTS)→ ClickHouse(config.xml identity)→ addauth + setAcl -R 全树收权 → 六项验证。核心经验就一句:修 ZooKeeper 的漏洞,功夫在 ZooKeeper 之外——把挂在它上面的每一个客户端都盘点到、适配完,认证和收权才能安全落地。配合系列一至三食用,Kafka+ZK 的认证体系就完整闭环了;同期的 Hadoop 集群安全加固 也是同一轮漏扫的产物,ZK session 调优可以看 YARN 双 Standby 排查。

你们平台的 ZK 是不是也在裸奔?漏扫报告里还见过哪些组件的未授权访问,评论区聊聊处理思路。


系列文章配置在 ZooKeeper 3.8.4 + Kafka 3.9.1 + Hadoop HA + ClickHouse 生产集群验证;示例密码请替换后使用。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/Sayai/article/details/169168214

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--