一次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版本。
最后還是要說一句:操作不規范,運維兩行淚!


































