背景说明
在去年2021-12-09左右,那时候log4j 2的远程代码执行漏洞,Java程序员和运维在朋友圈疯狂转发,当初定义的CVE号为:CVE-2021-44228 ,然后影响版本只是Apache Log4j 2.x <= 2.14.1受该漏洞影响,Log4j1.x声明是不受到此漏洞影响范围内的,临时构建出来了2.15.0-rc2防护版本,但是后续不让人消停啊。
官方于又于2021.12.13号官方已经发布稳定修复漏洞版本 https://logging.apache.org/log4j/2.x/download.html 「2.16.0」,看了一下提交记录,删除了Messge Lookups的方法。
大家以为的终极版本2.16.0出来后,又爆出DOS拒绝服务攻击漏洞,所以官方在「2021.12.17」又推出了最新版本 「2.17.0」
继官方推出2.17.0版本修复DOS拒绝服务攻击后,又出现远程代码执行 (RCE) 攻击的特征漏洞,官方于2021.12.28推出了 「2.17.1」 版本,虽然此漏洞的利用条件极高,需要可以控制配置文件,但是官方评分还是6.6分(https://logging.apache.org/log4j/2.x/security.html#),属于「中危漏洞」。
最近看官方,2022-02-23又升级到2.17.2了,但是看官方安全说明是正常迭代,不涉及修复关联漏洞。
利用Java语言开发的程序其实好多都在用Log4j日志框架,当然也包括我们常用的开源服务kafka、zookeeper、Nacos等。
比如这些开源的服务就有一个特征,就是当你线上使用是较老一点点的版本,基本里面引用的都是Log4j 1版本,虽然log4j 2的远程代码执行漏洞没有连累1.x版本,但是1.x版本也有漏洞(CVE-2020-9488 CVE-2019-17571), 而且1.x版本是EOL状态的版本,并且最新1.x版本的包是没有解决漏洞问题的。
EOL代表不再维护迭代的版本, 所以一般对于安全性要求高的公司,这种情况是不允许的,所以理想的处理办法是在不修改源码的情况下能够修复这些漏洞,同时满足生产安全要求,如果实在不行,只能让有编程开发能力的同学拉取官方对应版本代码分支重新编译一个版本。从log4j2的官网https://logging.apache.org/log4j/2.x/了解到,log4j1.x和log4j2.x是存在一定的兼容性的,它们实现的都是SLF4J的API,理论上来说是可以通过替换Jar包切换的, 所以今天我这里介绍一下Kafka和Zookeepr替换修复的方法。
我这里还是以官方最新发布的2.17.2的版本为例子。
zookeeper从log4j1.x升级到log4j2.x
基本信息,kafka版本:3.4.14 操作系统:Centos7.9 Java版本:1.8.0_152 ,升级过程如下。
1、下载官方最新log4j 2.17.2版本包并解压
wget --no-check-certificate https://dlcdn.apache.org/logging/log4j/2.17.2/apache-log4j-2.17.2-bin.tar.gz
tar xf apache-log4j-2.17.2-bin.tar.gz
2、同时下载要替换的slf4j-api 1.7.32版本jar包
wget --no-check-certificate https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar
3、查询zookeeper引用信息
[root@labs zookeeper]# ls lib/ |grep -Ei 'log4j|slf'
log4j-1.2.17.jar
slf4j-api-1.7.25.jar
slf4j-log4j12-1.7.25.jar
然后分别删除slf4j-log4j12、slf4j-api和log4j三个包:
[root@labs kafka]# rm -f lib/{slf4j-log4j12-1.7.25.jar,slf4j-api-1.7.25.jar,log4j-1.2.17.jar}
4、替换新包
分别把apache-log4j-2.17.2-bin目录下的:
log4j-1.2-api-2.17.2.jar
log4j-api-2.17.2.jar
log4j-core-2.17.2.jar
log4j-slf4j-impl-2.17.2.jar
和slef4j-api-1.7.32.jar
同步到zookeeper安装目录下的lib目录下。
5、关闭jmx
如果不关闭jmx,zookeeper启动会报类(org.apache.log4j.jmx.HierarchyDynamicMBean
)找不到,详情如下:
[root@labs zookeeper]# cat /data/logs/zookeeper/zookeeper.out
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/jmx/HierarchyDynamicMBean
at org.apache.zookeeper.jmx.ManagedUtil.registerLog4jMBeans(ManagedUtil.java:51)
at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:77)
at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:55)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:119)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.jmx.HierarchyDynamicMBean
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 5 more
如上源码可知前置条件是JMX开启才才会加载此类实例化,并不是直接import的, 因为zookeeper启动可以设置参数来控制是否启用jmx,所以简单粗暴的方式就是,直接修改启动脚本bin/zkServer.sh
,然后启动。
ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY -Dzookeeper.jmx.log4j.disable=true org.apache.zookeeper.server.quorum.QuorumPeerMain"
但是这有个问题,就是你全局禁用了,so,可能影响jmx监控数据的采集,所以可以看看源代码尝试完全修复 or 临时跳过,最简单:
if (Boolean.getBoolean("zookeeper.jmx.log4j.disable") == true) {
return;
}
改成:
if (1 == 1) {
return;
}
5、测试可用性
启动zookeeper服务,看看是否可以正常运行;
然后利用zkCli.sh客户端连接,进行create,get等操作验证。
kafka从log4j1.x升级到log4j2.x
基本信息,kafka版本:2.2.2 操作系统:Centos7.9 Java版本:1.8.0_152 ,升级过程如下。
1、下载官方最新log4j 2.17.2版本包并解压
wget --no-check-certificate https://dlcdn.apache.org/logging/log4j/2.17.2/apache-log4j-2.17.2-bin.tar.gz
tar xf apache-log4j-2.17.2-bin.tar.gz
2、同时下载要替换的slf4j-api 1.7.32版本jar包
wget --no-check-certificate https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar
3、查询kafka引用信息
[root@labs kafka]# ls libs/ |grep -Ei 'slf|log4j'
kafka-log4j-appender-2.2.2.jar
log4j-1.2.17.jar
slf4j-api-1.7.25.jar
slf4j-log4j12-1.7.25.jar
然后分别删除slf4j-log4j12、slf4j-api和log4j三个包:
[root@labs kafka]# rm -f libs/{slf4j-log4j12-1.7.25.jar,slf4j-api-1.7.25.jar,log4j-1.2.17.jar}
4、替换新包
分别把apache-log4j-2.17.2-bin目录下的:
log4j-1.2-api-2.17.2.jar
log4j-api-2.17.2.jar
log4j-core-2.17.2.jar
log4j-slf4j-impl-2.17.2.jar
和slef4j-api-1.7.32.jar
同步到kafka安装目录下的libs目录下。
5、测试可用性
启动kafka服务看看可以正常启动吗;
利用自带生产者工具kafka-console-producer.sh和消费者工具kafka-console-consumer.sh测试功能。