CentOS 7 rpm依赖导致的疑难故障案例

前些日子遇到一个故障案例,现象是云主机上的机器网络丢失,通过命令无法正常启动网卡,导致各项业务中断,OPS反馈机器只能通过云控制台输入口令进入系统。故障的影响还是比较大的,和OPS一起解决故障并恢复业务后,回家用虚拟机模拟环境来找找根本原因。

故障处理

本文内容使用虚拟机复现故障场景,为了更好地记录,这里先描述故障现象和如何处理。

故障出现后,OPS同步的故障现象信息是机器启动后网络丢失,通过命令无法正常启动网卡:
1)通过ifconfig看不见业务使用的网卡,通过ip a可以看到业务网卡,但无网络配置:

1
2
3
4
5
6
7
8
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether 00:0c:29:1d:d1:f2 brd ff:ff:ff:ff:ff:ff

2)手工通过ifup启动网卡,但出现报错:

1
2
3
$ ifup ens33
Determining IP information for ens33.../sbin/dhclient: error while loading shared libraries: libdns-export.so.100: cannot open shared object file: No such file or directory
failed.

3)在/usr/lib64下未找到libdns-export.so.100:

1
2
$ ls -lrt /usr/lib64/libdns-export*
ls: cannot access /usr/lib64/libdns-export*: No such file or directory

到了这里可以看出,因为SO库缺失的原因,已经无法启动网卡。这个时候OPS提出了先尝试临时配置静态IP看看是否可用的方案:

1
2
3
4
5
6
7
8
9
10
11
# 方式一
$ ifconfig ens33 192.168.126.122 netmask 255.255.255.0
$ route add default gw 192.168.126.1 dev ens33
$ ifconfig
$ route -n

# 方式二
$ ip a a 192.168.126.122/24 dev ens33
$ ip r a default via 192.168.126.1 dev ens33
$ ip a
$ route -n

配置完以后,ping网关和同网段内主机均正常,此前中断的SSH恢复。但还需要将缺失的SO从其他机器拷贝一份,让网卡正常启动。

经过一番查找,我们在CentOS 7.5.1804、7.6.1810都找到了libdns-export.so.100

1
2
3
$ ls -lrt /usr/lib64/libdns-export*
-rwxr-xr-x. 1 root root 1344880 Apr 12 2018 /usr/lib64/libdns-export.so.100.1.1
lrwxrwxrwx. 1 root root 24 Jun 3 10:24 /usr/lib64/libdns-export.so.100 -> libdns-export.so.100.1.1

我们将libdns-export.so.100.1.1拷贝到故障机器上,并创建软链:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 正常CentOS 7.5
$ scp /usr/lib64/libdns-export.so.100.1.1 develop@192.168.126.122:/home/develop

# 故障机器
$ cp libdns-export.so.100.1.1 /usr/lib64
$ cd /usr/lib64/
$ ls -lrt libdns-export.so.100.1.1
-rwxr-xr-x. 1 root root 1344880 Jun 3 21:05 libdns-export.so.100.1.1

$ ln -s libdns-export.so.100.1.1 libdns-export.so.100
$ ls -lrt libdns-export.so.100*
-rwxr-xr-x. 1 root root 1344880 Jun 3 21:05 libdns-export.so.100.1.1
lrwxrwxrwx. 1 root root 24 Jun 3 21:06 libdns-export.so.100 -> libdns-export.so.100.1.1

完成上述步骤后,通过ifup ens33尝试启动网卡时出现了新的SO库缺失问题:

1
2
3
$ ifup ens33
Determining IP information for ens33.../sbin/dhclient: error while loading shared libraries: libisc-export.so.95: cannot open shared object file: No such file or directory
failed.

同理,在CentOS 7.5.1804、7.6.1810也都找到了libisc-export.so.95

1
2
3
$ ls -lrt /usr/lib64/libisc-export*
-rwxr-xr-x. 1 root root 369744 Oct 31 2018 /usr/lib64/libisc-export.so.95.2.1
lrwxrwxrwx. 1 root root 23 Apr 16 07:10 /usr/lib64/libisc-export.so.95 -> libisc-export.so.95.2.1

与处理libdns-export.so.100.1.1的方式相同,对libisc-export.so.95.2.1进行同样的处理。

1
2
3
4
5
6
7
8
9
10
11
12
# 正常CentOS 7.5
scp /usr/lib64/libisc-export.so.95.2.1 develop@192.168.126.122:/home/develop
# 故障机器
$ cp libisc-export.so.95.2.1 /usr/lib64
$ cd /usr/lib64/
$ ls -lrt libisc-export.so.95.2.1
-rwxr-xr-x. 1 root root 369648 Jun 3 21:13 libisc-export.so.95.2.1

$ ln -s libisc-export.so.95.2.1 libisc-export.so.95
$ ls -lrt libisc-export.so.95*
-rwxr-xr-x. 1 root root 369648 Jun 3 21:13 libisc-export.so.95.2.1
lrwxrwxrwx. 1 root root 23 Jun 3 21:14 libisc-export.so.95 -> libisc-export.so.95.2.1

再次通过ifup ens33尝试启动网卡,已经能够正常执行,未有错误抛出。systemctl restart network也启动正常。

另外,如果机器的dhclient在运行中,这一步可能会遇到失败的现象,抛出类似dhclient(22421) is already running - exiting.的错误,同时systemctl restart network也无法正常启动。遇到该问题的话,可以通过kill掉dhclient相关进程后再次尝试重启网络解决。

故障复盘

在故障处理完,恢复业务后,还是需要分析下到底什么操作导致了故障出现。

复现环境: CentOS 7.5.1804、CentOS 7.6.1810、CentOS 7.9.2009各一台虚拟机。

已知信息:某项业务部署在CentOS 7.9.2009出现故障,部署在CentOS 7.5.1804、CentOS 7.6.1810状态正常。

出现问题的SO库libdns-exportlibisc-export经过简单的查询可以确认和DHCP、BIND依赖有关。在业务部署脚本中也确认通过rpm安装了dhcp的几个依赖。

1
2
3
4
dhcp-common-4.2.5-68.el7.centos.x86_64
dhcp-libs-4.2.5-68.el7.centos.x86_64
dhclient-4.2.5-68.el7.centos.x86_64
dnsmasq-2.76-5.el7.x86_64

经过对比,这几个RPM版本是CentOS 7.5.1804、CentOS 7.6.1810中的默认版本。之所以安装这几个RPM的原因是为了自动在低于CentOS 7.5.1804的版本上修复漏洞CVE-2018-1111,该漏洞公开时恰好CentOS 7.5.1804刚发布不久。由于部署脚本日积月累,低于CentOS 7.5.1804的系统目前已经基本不再使用,但这一步骤仍保持着。

系统对比

通过rpm2cpio可以确认,上面的三个rpm并未包含libdns-exportlibisc-export。于是将目标放在了bind相关的rpm上。

到这里,我们还需要确认libdns-exportlibisc-export在不同系统上是哪个依赖包释放的。

首先看下CentOS 7.5.1804正常运行的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ rpm -qa | grep bind
bind-license-9.9.4-61.el7.noarch
bind-libs-lite-9.9.4-61.el7.x86_64
keybinder3-0.3.0-1.el7.x86_64
bind-libs-9.9.4-61.el7.x86_64
bind-utils-9.9.4-61.el7.x86_64
rpcbind-0.2.0-44.el7.x86_64

$ ls -lrt /usr/lib64/libdns-export*
-rwxr-xr-x. 1 root root 1344880 Apr 12 2018 /usr/lib64/libdns-export.so.100.1.1
lrwxrwxrwx. 1 root root 24 Jun 3 10:24 /usr/lib64/libdns-export.so.100 -> libdns-export.so.100.1.1

$ ls -lrt /usr/lib64/libisc-export*
-rwxr-xr-x. 1 root root 369648 Apr 12 2018 /usr/lib64/libisc-export.so.95.2.1
lrwxrwxrwx. 1 root root 23 Jun 3 10:24 /usr/lib64/libisc-export.so.95 -> libisc-export.so.95.2.1

再看下CentOS 7.9.2009出现故障的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ rpm -qa | grep bind
keybinder3-0.3.0-1.el7.x86_64
bind-license-9.11.4-26.P2.el7.noarch
bind-export-libs-9.11.4-26.P2.el7.x86_64
bind-libs-lite-9.11.4-26.P2.el7.x86_64
bind-utils-9.11.4-26.P2.el7.x86_64
rpcbind-0.2.0-49.el7.x86_64
bind-libs-9.11.4-26.P2.el7.x86_64

$ ls -lrt /usr/lib64/libdns-export*
ls: cannot access /usr/lib64/libdns-export*: No such file or directory
$ ls -lrt /usr/lib64/libisc-export*
ls: cannot access /usr/lib64/libisc-export*: No such file or directory

$ ls -lrt /usr/lib64/bind9-export/*
-rwxr-xr-x. 1 root root 446112 Oct 13 2020 /usr/lib64/bind9-export/libisc-export.so.169.0.3
-rwxr-xr-x. 1 root root 182576 Oct 13 2020 /usr/lib64/bind9-export/libisccfg-export.so.160.2.1
-rwxr-xr-x. 1 root root 45248 Oct 13 2020 /usr/lib64/bind9-export/libirs-export.so.160.0.5
-rwxr-xr-x. 1 root root 2304120 Oct 13 2020 /usr/lib64/bind9-export/libdns-export.so.1102.1.2
lrwxrwxrwx. 1 root root 25 Jun 3 09:17 /usr/lib64/bind9-export/libdns-export.so.1102 -> libdns-export.so.1102.1.2
lrwxrwxrwx. 1 root root 24 Jun 3 09:17 /usr/lib64/bind9-export/libirs-export.so.160 -> libirs-export.so.160.0.5
lrwxrwxrwx. 1 root root 24 Jun 3 09:17 /usr/lib64/bind9-export/libisc-export.so.169 -> libisc-export.so.169.0.3
lrwxrwxrwx. 1 root root 27 Jun 3 09:17 /usr/lib64/bind9-export/libisccfg-export.so.160 -> libisccfg-export.so.160.2.1

可以看出,CentOS 7.9.2009和CentOS 7.5.1804的bind依赖包相比,主要有几处不同:

  • bind*系列的版本,CentOS 7.9.2009较CentOS 7.5.1804高2个版本
  • CentOS 7.9.2009多出一个bind-export-libs
  • CentOS 7.9.2009下libdns-exportlibisc-export单独存在于/usr/lib64/bind9-export/

到此,CentOS 7.9.2009出现故障的原因可以确认是由于部署脚本中安装了修复CVE-2018-1111的几个rpm,导致相关的依赖版本降级后又缺失对应的SO库,造成网卡服务异常。

这里还需要确认一些问题,各个版本的libdns-exportlibisc-export是由哪个RPM释放的。通过查找对应版本的RPM,利用rpm2cpio,可以看到下面的结果:

  • 在CentOS 7.5.1804中分别由bind-libs-lite和bind-libs释放:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    $ rpm2cpio bind-libs-lite-9.9.4-61.el7.x86_64.rpm | cpio -idv
    ./usr/lib64/libdns-export.so.100
    ./usr/lib64/libdns-export.so.100.1.1
    ./usr/lib64/libirs-export.so.90
    ./usr/lib64/libirs-export.so.90.0.1
    ./usr/lib64/libisc-export.so.95
    ./usr/lib64/libisc-export.so.95.2.1
    ./usr/lib64/libisccfg-export.so.90
    ./usr/lib64/libisccfg-export.so.90.0.7
    3550 blocks

    $ rpm2cpio bind-libs-9.9.4-61.el7.x86_64.rpm | cpio -idv
    ./usr/lib64/libbind9.so.90
    ./usr/lib64/libbind9.so.90.0.8
    ./usr/lib64/libdns.so.100
    ./usr/lib64/libdns.so.100.1.1
    ./usr/lib64/libisc.so.95
    ./usr/lib64/libisc.so.95.2.1
    ./usr/lib64/libisccc.so.90
    ./usr/lib64/libisccc.so.90.0.4
    ./usr/lib64/libisccfg.so.90
    ./usr/lib64/libisccfg.so.90.0.7
    ./usr/lib64/liblwres.so.90
    ./usr/lib64/liblwres.so.90.0.5
    5294 blocks
  • 而在CentOS 7.9.2009中,bind-libs-lite和bind-libs的作用有调整,export相关移动到bind-export-libs释放:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    $ rpm2cpio bind-libs-lite-9.11.4-26.P2.el7.x86_64.rpm | cpio -idv
    ./usr/lib64/libdns.so.1102
    ./usr/lib64/libdns.so.1102.1.2
    ./usr/lib64/libirs.so.160
    ./usr/lib64/libirs.so.160.0.5
    ./usr/lib64/libisc.so.169
    ./usr/lib64/libisc.so.169.0.3
    ./usr/lib64/libisccfg.so.160
    ./usr/lib64/libisccfg.so.160.2.1
    6049 blocks

    $ rpm2cpio bind-libs-9.11.4-26.P2.el7.x86_64.rpm | cpio -idv
    ./usr/lib64/libbind9.so.160
    ./usr/lib64/libbind9.so.160.0.8
    ./usr/lib64/libisccc.so.160
    ./usr/lib64/libisccc.so.160.0.3
    ./usr/lib64/liblwres.so.160
    ./usr/lib64/liblwres.so.160.0.2
    372 blocks

    $ rpm2cpio bind-export-libs-9.11.4-26.P2.el7.x86_64.rpm | cpio -idv
    ./etc/ld.so.conf.d/bind-export-x86_64.conf
    ./usr/lib64/bind9-export
    ./usr/lib64/bind9-export/libdns-export.so.1102
    ./usr/lib64/bind9-export/libdns-export.so.1102.1.2
    ./usr/lib64/bind9-export/libirs-export.so.160
    ./usr/lib64/bind9-export/libirs-export.so.160.0.5
    ./usr/lib64/bind9-export/libisc-export.so.169
    ./usr/lib64/bind9-export/libisc-export.so.169.0.3
    ./usr/lib64/bind9-export/libisccfg-export.so.160
    ./usr/lib64/bind9-export/libisccfg-export.so.160.2.1
    ./usr/share/licenses/bind-export-libs-9.11.4
    ./usr/share/licenses/bind-export-libs-9.11.4/COPYRIGHT
    5879 blocks
故障复现

1、首先在CentOS 7.9.2009上复现这个故障非常的简单:
1)拷贝以下几个rpm并强制安装:

1
2
3
4
5
6
7
dhcp-common-4.2.5-68.el7.centos.x86_64
dhcp-libs-4.2.5-68.el7.centos.x86_64
dhclient-4.2.5-68.el7.centos.x86_64
dnsmasq-2.76-5.el7.x86_64

$ rpm -Uvh *.rpm --nodeps --force
$ ldconfig

2)禁用NetworkManager

1
2
$ systemctl stop NetworkManager
$ systemctl disable NetworkManager

3)systemctl restart network无法正常重启,journalctl -xe可以看到SO库缺失

2、另外这里进行了思考,是不是说只要系统版本是CentOS 7.5.1804或者CentOS 7.6.1810就一定没问题呢?经过测试,其实不是的。用刚装好的CentOS 7.5.1804或CentOS 7.6.1810执行完yum update后,按相同的步骤即可复现。以CentOS 7.5.1804举例:
1)更新前:

1
2
3
4
5
6
7
$ rpm -qa | grep bind
bind-license-9.9.4-61.el7.noarch
bind-libs-lite-9.9.4-61.el7.x86_64
keybinder3-0.3.0-1.el7.x86_64
bind-libs-9.9.4-61.el7.x86_64
bind-utils-9.9.4-61.el7.x86_64
rpcbind-0.2.0-44.el7.x86_64

2)执行yum update,在列表中可以看到如下更新:

1
2
3
4
5
6
7
8
9
10
11
# Updating:
bind-libs x86_64 32:9.11.4-26.P2.el7_9.13 updates 158 k
bind-libs-lite x86_64 32:9.11.4-26.P2.el7_9.13 updates 1.1 M
bind-license noarch 32:9.11.4-26.P2.el7_9.13 updates 92 k
bind-utils x86_64 32:9.11.4-26.P2.el7_9.13 updates 262 k
dhclient x86_64 12:4.2.5-83.el7.centos.1 updates 286 k
dhcp-common x86_64 12:4.2.5-83.el7.centos.1 updates 177 k
dhcp-libs x86_64 12:4.2.5-83.el7.centos.1 updates 133 k

# Installing for dependencies:
bind-export-libs x86_64 32:9.11.4-26.P2.el7_9.13 updates 1.1 M

可以看出,这里升级完的版本和CentOS 7.9.2009上自带的版本是一致的。在升级完成后执行reboot,让系统重启一次。再通过上面CentOS 7.9.2009复现的步骤,即可复现出同样的故障。

结束

写到这里故障复盘就结束了,本意是多年前为了修复漏洞的一个操作,在多年以后却成了一个隐患。带来的一些警示:

  • 在部署脚本中对于漏洞修复这种敏感场景要标明一个操作是为什么要做,CVE编号是什么,影响的范围是哪里
  • 定期审视一些老旧代码是否仍具备合理性
  • 定位问题时除了报错和日志,要尽可能的多的收集环境信息,比如系统版本、可怀疑的组件版本等

参考

1、https://zh.wikipedia.org/wiki/CentOS
2、https://access.redhat.com/security/vulnerabilities/3442151
3、https://mobile.mirrors.ustc.edu.cn/centos-vault/centos/7.5.1804/os/x86_64/Packages/

0%