背景
有一台Centos 7的服务器执行系统命令提示GLIBC的错误,在后来排查中发现是有更新过系统,系统中同时存在centos7和centos8的包导致系统库损坏,这台服务器上安装了编译打包的环境,重装成本很高,还是尽力去修复吧,下面是修复过程。
图片
修复过程
首先挂载centos7的iso镜像进入救援模式, 使用 ISO 里的 rpm 包重新安装 glibc 。
进入救援模式后,CentOS 会提示是否挂载原系统分区到 /mnt/sysimage,输入1之后回车就可以了。
图片
切换到原系统环境,结果chroot命令都无法执行了。

当前只能在在救援环境里用 rpm --root 重装了。首先挂载并查找 ISO 里的 glibc 包。
复制
mount /dev/cdrom /mnt/cdrom
find /mnt/cdrom -name "glibc*.rpm"1.2.
直接在原系统根目录安装,因为 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-*.rpm1.2.
参数解释:
--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-*.rpm1.2.3.
--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.61.2.
图片
绑定必要伪文件系统后再 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 glibc1.2.3.4.5.6.7.8.9.10.
图片
在 chroot 里用 ISO 做一个临时 repo
复制
cat >/etc/yum.repos.d/cdrom.repo <<EOF
[cdrom]
name=CentOS7 ISO
baseurl=file:///mnt/cdrom
enabled=1
gpgcheck=0
EOF1.2.3.4.5.6.7.
图片
让 glibc 及相关包重装一遍
复制
yum reinstall -y glibc glibc-common1.
这里发现安装失败了,glibc的版本有冲突, centos7.9的系统里还残留了 glibc 的 el8 版本,这可能就是系统为什么坏掉的原因。
图片
检查下系统 有哪些 glibc 包并将其删除。
复制
rpm --root / -qa | grep glibc1.
把所有 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_641.2.3.4.5.
图片
在 chroot 里清理并重装 glibc和其他可能损坏的包。
复制
yum reinstall -y glibc glibc-common
yum reinstall -y nss-softokn-freebl nss nscd libgcc libstdc++ bash coreutils1.2.
可以看到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|glibc1.
卸载 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_641.2.
图片
纠正 libcrypt 的实际文件与链接(el7 应该存在 libcrypt-2.17.so)。
复制
ls -l /mnt/sysimage/lib64/libcrypt*
ln -sf /lib64/libcrypt-2.17.so /mnt/sysimage/lib64/libcrypt.so.11.2.
图片
再次执行就不会报错了。
复制
yum reinstall -y glibc glibc-common
yum reinstall -y nss-softokn-freebl nss nss-util nspr libgcc libstdc++ bash coreutils1.2.
图片
刷新缓存
复制
/sbin/ldconfig
/usr/sbin/iconvconfig || true1.2.
回到救援模式外层,解除绑定并卸载cdrom,之后重启
复制
umount /mnt/sysimage/{dev,proc,sys}
umount /mnt/sysimage/mnt/cdrom
reboot1.2.3.
重启后登录系统发现刚输入用户名立刻提示 “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}
reboot1.2.3.4.5.6.7.8.9.10.11.
图片
重装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-auth1.2.3.4.
当我使用authconfig 重建system-auth/ password-auth等默认规则时发现问题了。
复制
authconfig --enableshadow --passalgo=sha512 --update1.
这里明显是 /sbin/authconfig 被换成了 Python3/EL8 的版本,CentOS7 的 authconfig 是 shell+perl 脚本,不会报这种 Python 错,说明el8的包还是没清理干净,登录认证的包也被污染了。
图片
确认util-linux的包是否正确,centos7的包正常应该是2.23版本。
复制
rpm -qpi /mnt/cdrom/Packages/util-linux*.rpm | grep Version1.
因为 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.rpm1.2.
覆盖安装 authconfig。
复制
rpm -ivh --force --nodeps /mnt/cdrom/Packages/authconfig-6.2.8-30.el7.x86_64.rpm1.
重新生成 PAM 配置,结果发现还是报错。
复制
authconfig --enableshadow --passalgo=sha512 --update1.
这里他提示的是缩进错误,** **应该还是Python3 解释器在运行(Python3 对混用缩进更严格),而 CentOS 7 应该用 Python2.7, 这说明系统里还残留了 EL8 的 python3或者**/usr/bin/python****指到了python3。 **
图片
查看当前python确实指到了python3上。
安装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 -V1.2.3.
重新生成 PAM 模板并刷新链接,这次发现没有报错了。
复制
/usr/bin/python2.7 /sbin/authconfig --enableshadow --passalgo=sha512 --update1.
刷新动态库链接。
图片
修改完之后再次重启。
图片
重启发现还是无法登录系统,并且我在进救援模式使用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.rpm1.2.3.4.
图片
上步执行成功之后再把几个关键软链接校正并刷新缓存。
复制
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/sysimage1.2.3.4.5.6.7.
安装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/ldconfig1.2.3.4.5.6.7.
上步执行完成后进行验证。
复制
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:... 这一行1.2.3.4.
都返回正常后重启登录,可以看到已经修复完成可以正常登录系统。
复制
exit
umount /mnt/sysimage/{dev,proc,sys} 2>/dev/null
reboot1.2.3.
图片
总结
这次事故的本质是el8包误混入el7引发glibc系列错配,libc/libcrypt/libnsl 与 PAM/NSS、util-linux、authconfig 等系统组件都被损坏,由于系统包被污染的很严重,这次的修复过程可谓是一波三折。
修复过程是在救援环境用 同版本的ISO 将 glibc 家族强制回滚,修复ld-linux、libc、libcrypt、libnsl等关键软链,恢复 PAM/登录链路并将默认 Python 指回 2.7版本。
最后还是要说一句:操作不规范,运维两行泪!