作者:wanger
公众号:运维开发故事
博客:https://devopstory.cn
背景
有一台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,《运维开发故事》公众号团队中的一员,一线运维农民工,云原生实践者,这里不仅有硬核的技术干货,还有我们对技术的思考和感悟,欢迎关注我们的公众号,期待和你一起成长!