一次CentOS7 glibc灾难的全链路修复

背景
有一台Centos 7的服务器执行系统命令提示GLIBC的错误,在后来排查中发现是有更新过系统,系统中同时存在centos7和centos8的包导致系统库损坏,这台服务器上安装了编译打包的环境,重装成本很高,还是尽力去修复吧,下面是修复过程

修复过程
首先挂载centos7的iso镜像进入救援模式, 使用 ISO 里的 rpm 包重新安装 glibc 。
进入救援模式后,CentOS 会提示是否挂载原系统分区到 /mnt/sysimage,输入1之后回车就可以了

切换到原系统环境,结果chroot命令都无法执行了
chroot /mnt/sysimage

当前只能在在救援环境里用 rpm –root 重装了。首先挂载并查找 ISO 里的 glibc 包
mount /dev/cdrom /mnt/cdrom
find /mnt/cdrom -name “glibc.rpm” 直接在原系统根目录安装,因为 chroot 已经坏掉,要用 –root 指定路径 rpm –root /mnt/sysimage -ivh –force –nodeps /mnt/cdrom/Packages/glibc-2.17-.rpm
rpm –root /mnt/sysimage -ivh –force –nodeps /mnt/cdrom/Packages/glibc-common-2.17-*.rpm
参数解释:
–force –nodeps → 强制覆盖,不检查依赖(因为依赖链也可能损坏了)
–root /mnt/sysimage → 指定修复目标系统
可以看到rpm –root 装进去了,但 %post/trigger 脚本在目标根里执行失败(iconvconfig、/bin/sh 都被坏掉的 glibc/ld 撞到了),所以还需要“不跑脚本,强制覆盖一遍”,再让系统能 chroot,最后在 chroot 里用 yum 正常收尾

先覆盖安装glibc 与 glibc-common,但禁止执行脚本,避免再次触发坏环境,
rpm –root /mnt/sysimage -Uvh –force –nodeps –nopre –nopost –notriggers \
/mnt/cdrom/Packages/glibc-2.17-.rpm \ /mnt/cdrom/Packages/glibc-common-2.17-.rpm
–nopre –nopost –notriggers = 不跑 %pre/%post/%trigger,避免上面截图中的 scriptlet failed, exit 127。
下面截图就是执行完成了

修复关键链接
ln -sf /lib64/ld-2.17.so /mnt/sysimage/lib64/ld-linux-x86-64.so.2
ln -sf /lib64/libc-2.17.so /mnt/sysimage/lib64/libc.so.6

绑定必要伪文件系统后再 chroot
mkdir -p /mnt/sysimage/mnt/cdrom

挂载 ISO 到目标系统的 /mnt/cdrom

mount /dev/sr0 /mnt/sysimage/mnt/cdrom

mount -o bind /dev /mnt/sysimage/dev
mount -o bind /proc /mnt/sysimage/proc
mount -o bind /sys /mnt/sysimage/sys

chroot /mnt/sysimage /bin/bash
ls /mnt/cdrom/Packages | grep glibc

在 chroot 里用 ISO 做一个临时 repo
cat >/etc/yum.repos.d/cdrom.repo <<‘EOF’
[cdrom]
name=CentOS7 ISO
baseurl=file:///mnt/cdrom
enabled=1
gpgcheck=0
EOF

让 glibc 及相关包重装一遍
yum reinstall -y glibc glibc-common
这里发现安装失败了,glibc的版本有冲突, centos7.9的系统里还残留了 glibc 的 el8 版本,这可能就是系统为什么坏掉的原因

检查下系统 有哪些 glibc 包并将其删除
rpm –root / -qa | grep glibc
把所有 el8 结尾的 glibc 包都删掉,只保留 el7 的
rpm -e –nodeps glibc-headers-2.28-164.el8.x86_64
rpm -e –nodeps glibc-langpack-zh-2.28-164.el8.x86_64
rpm -e –nodeps glibc-devel-2.28-164.el8.x86_64
rpm -e –nodeps glibc-locale-source-2.28-164.el8.x86_64
rpm -e –nodeps glibc-langpack-en-2.28-164.el8.x86_64

在 chroot 里清理并重装 glibc和其他可能损坏的包
yum reinstall -y glibc glibc-common
yum reinstall -y nss-softokn-freebl nss nscd libgcc libstdc++ bash coreutils
可以看到glibc安装成功了,但是安装其他包的时候报错了
说明系统里还残留了 EL8 的 libcrypt(来自 libxcrypt 包)。
CentOS 7 的 libcrypt.so.1 是 glibc 自带的(libcrypt-2.17.so),而 EL8 的 libcrypt.so.1 来自 libxcrypt,需要 GLIBC_2.25。 CentOS 7 的 glibc 只有 2.17,所以所有调用 libcrypt.so.1 的程序都会报错。

直接把 libxcrypt(EL8)* 移除,并把 libcrypt 换回 el7 的版本即可。
不要在 chroot 里跑,全部在外层用 –root /mnt/sysimage 执行,避免被坏的 libcrypt 影响。
找出系统里与 crypt 相关的 EL8 包
rpm –root /mnt/sysimage -qa | egrep -i ‘libxcrypt|libcrypt|glibc’
卸载 el8 的 libxcrypt 系列
rpm –root /mnt/sysimage -e –nodeps libxcrypt-4.1.1-6.el8.x86_64
rpm –root /mnt/sysimage -e –nodeps libxcrypt-devel-4.1.1-6.el8.x86_64

纠正 libcrypt 的实际文件与链接(el7 应该存在 libcrypt-2.17.so)
ls -l /mnt/sysimage/lib64/libcrypt*
ln -sf /lib64/libcrypt-2.17.so /mnt/sysimage/lib64/libcrypt.so.1

再次执行就不会报错了
yum reinstall -y glibc glibc-common
yum reinstall -y nss-softokn-freebl nss nss-util nspr libgcc libstdc++ bash coreutils

刷新缓存
/sbin/ldconfig
/usr/sbin/iconvconfig || true
回到救援模式外层,解除绑定并卸载cdrom,之后重启
umount /mnt/sysimage/{dev,proc,sys}
umount /mnt/sysimage/mnt/cdrom
reboot
重启后登录系统发现刚输入用户名立刻提示 “Login incorrect”,可能还是PAM认证这块有损坏了,进入救援环境继续排查,在进救援模式的过程中发现了下面的报错。systemd 会调用 /usr/sbin/sulogin 来提供 root 登录,系统里sulogin 不见了导致现在无法登录 。

sulogin 属于 util-linux 包,还是进入救援模式重装
mount -o bind /dev /mnt/sysimage/dev
mount -o bind /proc /mnt/sysimage/proc
mount -o bind /sys /mnt/sysimage/sys
chroot /mnt/sysimage /bin/bash
mount /dev/sr0 /mnt/cdrom
find /mnt/cdrom/Packages -name “util-linux*.rpm”
rpm -ivh –force –nodeps /mnt/cdrom/Packages/util-linux-2.23.2-65.el7.x86_64.rpm
ls -l /usr/sbin/sulogin
exit
umount /mnt/sysimage/{dev,proc,sys}
reboot

重装util-linux后发现还是不行,于是又继续排查
ls -l /etc/nsswitch.conf*
ls -l /etc/pam.d/login
ls -l /etc/pam.d/system-auth
ls -l/etc/pam.d/password-auth
当我使用authconfig 重建system-auth/ password-auth等默认规则时发现问题了
authconfig –enableshadow –passalgo=sha512 –update
这里明显是 /sbin/authconfig 被换成了 Python3/EL8 的版本,CentOS7 的 authconfig 是 shell+perl 脚本,不会报这种 Python 错,说明el8的包还是没清理干净,登录认证的包也被污染了

确认util-linux的包是否正确,centos7的包正常应该是2.23版本
rpm -qpi /mnt/cdrom/Packages/util-linux.rpm | grep Version 因为 login 流程靠 PAM + sulogin,所以需要覆盖安装一下 PAM & shadow-utils rpm -ivh –force –nodeps /mnt/cdrom/Packages/pam-1.1.8-23.el7.x86_64.rpm rpm -ivh –force –nodeps /mnt/cdrom/Packages/shadow-utils-4.6.5.el7.x86_64.rpm 覆盖安装 authconfig rpm -ivh –force –nodeps /mnt/cdrom/Packages/authconfig-6.2.8-30.el7.x86_64.rpm 重新生成 PAM 配置,结果发现还是报错 authconfig –enableshadow –passalgo=sha512 –update 这里他提示的是缩进错误,* 应该还是Python3 解释器在运行(Python3 对混用缩进更严格),而 CentOS 7 应该用 Python2.7, 这说明系统里还残留了 EL8 的 python3或者/usr/bin/python****指到了python3。 **

查看当前python确实指到了python3上
python -V
安装centos7的python(2.7.5)并修改python的默认解释器
rpm -ivh –force –nodeps /mnt/cdrom/Packages/python-2.7*.el7.x86_64.rpm
ln -sf /usr/bin/python2.7 /usr/bin/python
python -V
重新生成 PAM 模板并刷新链接,这次发现没有报错了
/usr/bin/python2.7 /sbin/authconfig –enableshadow –passalgo=sha512 –update
刷新动态库链接
/sbin/ldconfig

修改完之后再次重启

重启发现还是无法登录系统,并且我在进救援模式使用vim编辑器时又发现了报错,libnsl.so.1 和 glibc 版本不匹配,说明系统里还是残留了 el8的libnsl,需要清理el8的包并重装一次glibc核心库

再次重新覆盖安装glibc,需要跳过签名和摘要校验不然会报错
mount /dev/sr0 /mnt/sysimage/mnt/cdrom
rpm –root /mnt/sysimage -Uvh –force –nodeps –nopre –nopost –notriggers –nosignature –nodigest \
/mnt/sysimage/mnt/cdrom/Packages/glibc-2.17-317.el7.x86_64.rpm \
/mnt/sysimage/mnt/cdrom/Packages/glibc-common-2.17-317.el7.x86_64.rpm

上步执行成功之后再把几个关键软链接校正并刷新缓存
ln -sf /lib64/ld-2.17.so /mnt/sysimage/lib64/ld-linux-x86-64.so.2
ln -sf /lib64/libc-2.17.so /mnt/sysimage/lib64/libc.so.6
ln -sf /lib64/libcrypt-2.17.so /mnt/sysimage/lib64/libcrypt.so.1
ln -sf /usr/lib64/libnsl-2.17.so /mnt/sysimage/lib64/libnsl.so.1

为目标根刷新 ld 缓存(不用 chroot)

/sbin/ldconfig -r /mnt/sysimage
安装glibc核心包及相关软件重装一遍
mount -o bind /dev /mnt/sysimage/dev
mount -o bind /proc /mnt/sysimage/proc
mount -o bind /sys /mnt/sysimage/sys
chroot /mnt/sysimage /bin/bash
mount /dev/sr0 /mnt/cdrom
yum reinstall -y glibc glibc-common pam shadow-utils util-linux nss nss-softokn-freebl nspr nss-util || true
/sbin/ldconfig
上步执行完成后进行验证
readlink -f /lib64/libnsl.so.1 # 现在应为 /usr/lib64/libnsl-2.17.so
readlink -f /lib64/libcrypt.so.1 # 现在应为 /usr/lib64/libcrypt-2.17.so
ldd /bin/login | grep ‘not found’ || echo “login deps OK”
getent passwd root # 应能打印出 root:… 这一行
都返回正常后重启登录,可以看到已经修复完成可以正常登录系统
exit
umount /mnt/sysimage/{dev,proc,sys} 2>/dev/null
reboot

总结
这次事故的本质是el8包误混入el7引发glibc系列错配,libc/libcrypt/libnsl 与 PAM/NSS、util-linux、authconfig 等系统组件都被损坏,由于系统包被污染的很严重,这次的修复过程可谓是一波三折。
修复过程是在救援环境用 同版本的ISO 将 glibc 家族强制回滚,修复ld-linux、libc、libcrypt、libnsl等关键软链,恢复 PAM/登录链路并将默认 Python 指回 2.7版本。
最后还是要说一句:操作不规范,运维两行泪!

最后,求关注。如果你还想看更多优质原创文章,欢迎关注我们的公众号「运维开发故事」。

如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

你还可以把我的公众号设为「星标」,这样当公众号文章更新时,你会在第一时间收到推送消息,避免错过我的文章更新。

我是 wanger,《运维开发故事》公众号团队中的一员,一线运维农民工,云原生实践者,这里不仅有硬核的技术干货,还有我们对技术的思考和感悟,欢迎关注我们的公众号,期待和你一起成长!

作者:wanger
公众号:运维开发故事
博客:https://devopstory.cn

声明:来自运维开发故事,仅代表创作者观点。链接:https://eyangzhen.com/2386.html

运维开发故事的头像运维开发故事

相关推荐

关注我们
关注我们
购买服务
购买服务
返回顶部