Homelab: 一次运维事故

2023年5月初,我的Homelab发生了一次严重的运维事故。撰文以回忆和记述这次事故的前因后果同经验教训。

Homelab: 硬件概述

我的homelab在2023年5月时仅包含一台充当服务器的电脑和若干网络设备。这台电脑采用了许多生产和购买于不同时代的硬件。其主板已经使用了长达11年。1

老旧的硬件带来无尽的问题,主要集中在这块主板。

2018年时许,这块主板的用于存储BIOS(UEFI固件)的EEPROM损坏,在经过临时解决方案后在2020年通过更换芯片最终解决。2

更严重的是,这个主板的许多连接器严重地氧化了,包括DRAM插槽和PCIe插槽。3这些不能正常工作的连接器,让事故雪上加霜。(后文详述)

此外,事故发生时我并不在家,一般通过回家的VPN访问家中Homelab所托管的服务。然而这部电脑没有配备任何形式的带外管理(OOB Management) ——这台电脑的主板是一般台式机的主板,而非服务器主板,不具备BMC,故无法提供IPMI;它也不是商用机的主板,并不支持Intel vPro的主动管理技术(AMT);我也没有购买以PiKVM为代表的IP-KVM实现。没有带外管理,也堵死了另一条应对本次事故的可能方案。

事发

2023年5月4日,我例行更新Homelab中的服务器(运转Arch Linux)上的软件。pacman提示道一些软件包有新的配置文件,然而我本地的对应的配置文件有修改过,不是发行版提供的旧版本,故未进行直接覆盖,而是以.pacnew后缀保存。形如:

warning: /etc/sudoers installed as /etc/sudoers.pacnew

这种问题我时常遇到,我一般的处置方法是,用diff(或图形化的meld,如果有桌面环境)比较其差异并合并之。然而,这次我误删除了一个.pacnew文件,这个文件属于PAM, (Pluggable Authentication Modules) 是系统的关键组件。在现时(2024年5月)用pacman检视此包,可见我的服务器上许多关键组件(systemd, polkit, sudo, openssh)依赖它。

leo@Nebulas:~
$ pacman -Qi pam
Name            : pam
Version         : 1.6.1-2
Description     : PAM (Pluggable Authentication Modules) library
Architecture    : x86_64
URL             : http://linux-pam.org
Licenses        : GPL-2.0-only
Groups          : None
Provides        : libpam.so=0-64  libpamc.so=0-64  libpam_misc.so=0-64
Depends On      : glibc  libtirpc  pambase  audit  libaudit.so=1-64  libxcrypt
                  libcrypt.so=2-64  libnsl
Optional Deps   : None
Required By     : kbd  libcap  libpwquality  openssh  polkit  qemu-img  qemu-system-aarch64
                  qemu-system-riscv  qemu-system-x86  screen  shadow  sudo  systemd
                  util-linux
Optional For    : None
Conflicts With  : None
Replaces        : None
Installed Size  : 3.20 MiB
Packager        : Tobias Powalowski <tpowa@archlinux.org>
Build Date      : Thu 11 Apr 2024 08:47:57 AM UTC
Install Date    : Thu 11 Apr 2024 10:26:02 AM UTC
Install Reason  : Installed as a dependency for another package
Install Script  : No
Validated By    : Signature

理论上我有更高明的办法找到这个配置文件的新版本,比如将pacman cache里的这个包的新版本解压缩并提取对应文件。但我不知怎么,想到将这个包完全卸载再重新安装来使这个配置文件重新被创建。

我起初尝试用pacman -R pam来卸载,但由于依赖问题,pacman不允许我这样做。于是我又尝试pacman -Rdd pam来让pacman忽略任何依赖关系而强行卸载。

这次卸载成功了,但问题也很快出现了——当我再运转pacman -S --asdeps pam时,sudo却无法使用了,提示缺少一些库——正是pam包所提供的。我又尝试另起一个SSH会话连接到服务器,发现无法连接,出现鉴权失败相关的错误——这意味着,如果我现有的这个SSH会话断开,我将甚至无法连接到这个服务器了。

后来我又做了多种尝试,包括解开pam包提取相关.so文件并LD_PRELOAD等,皆未有成功修复。而在将服务器关机后,我仍不死心,电话联系母亲请求帮忙再开机后,却发现主机甚至无法ping通了——大概是因为systemd也依赖PAM,因而systemdinit无法运转起来吧... (回家后我排除故障时证实了这个猜想,后文详述)

事态恶化

到此,我仍没有意识到事态严重——这个故障大概用archiso启动后,挂载主机的rootfs并用pacstrap重新安装pam包便可解决了。正巧我有一个自己修改的archiso,其默认启用了sshd.service并包含我的SSH公钥,一旦这个修改的archiso能成功启动,我便能透过SSH连接上去,完成上述操作。

我于是又请求我母亲帮助将一个U盘插入我的另一个可以用SSH连接的Raspberry Pi 4以烧写archiso,再请求她将主机连接到这个U盘,键盘和家里的电视,以启动archiso。但不曾想,电视无论如何都无法识别到HDMI信号,甚至我又请求母亲为该主机安装一块"亮机显卡"并将电视接到该显卡上,仍不能看到任何画面。这大概时因为这块主板上的HDMI接口和PCIe接口都氧化严重导致的。

到这时我才有些慌了,不过幸运的是我将在不久的五月中旬回家,到时便可亲自排除故障。

解决

回家后我反复插拔"亮机显卡"多次,终于得以看到其输出的显示画面——开机时,systemd init无法初始化并进入rescue shell,原因正是缺失的PAM库。随后我便用U盘启动archiso并按上一章节所述方法完成修复,系统重新正常运转;经检验,文件未有损毁。算是不幸中的万幸了。

原因与教训

这起运维事故涉及多重原因,其中一些制造了事故,另一些让状况更糟

  • 我不应该进行"忽略依赖关系而卸载" (pacman -Rdd)这样的危险操作——更糟糕的是,尽管我知道这是一个危险的和应被避免的操作,我还是曾多次这样做——也许先前几次卸载的不是什么关键的软件包,便没有造成严重后果,我也因此更加松懈安全意识这是事故发生的直接原因和首要原因
  • 其次地,没有带外管理机制迫使我麻烦母亲帮忙实施带内操作。
  • 再次地,严重老化的硬件和氧化腐蚀的连接器令带内操作变得难以实现。

经以上分析,我采取相关应对措施如下:

  • 强化安全与合规操作之意识,杜绝冒险操作和反模式。(anti-pattern)
  • 活用drop-in configuration4以尽可能避免修改发行版提供的配置文件
  • 更换老旧硬件,以带有BMC的服务器主板等替代。BMC可提供IPMI供带外管理用。

后记

2024年1月某日夜,我的两台服务器(2023年5月底,新购硬件以替代旧有的一台,2023年12月,新加购一台)中有一台因不正确地修改systemd-boot配置(与GVT-g相关的kernel cmdline)而无法引导启动。现时,两台服务器皆有IPMI可用,我便用IPMI加载archiso在约10分钟里完成了修复。

  1. 该系统的机箱,电源,散热器,硬盘和阵列卡(RAID Controller)系近年购买。但主板和内存则购买于11年前的2012年3月,分别是华硕P8Z77-V LK,2条三星的4GiB DDR3 1600MHz (non-ECC)和2条十铨的4GiB DDR3 1600MHz (non-ECC),共计16GiB内存。 ↩︎
  2. EEPROM损坏造成了Intel ME固件损毁,iGPU不可用,后来使用AMIBCP强行烧写暂时解决了这个问题。在我的技术博客(现已关闭)中有一篇文章记录了这个事件。后来相同问题再次发生,尝试用编程器烧写固件,烧写后无法通过校验。更换EEPROM芯片并重新烧写后问题解决。 ↩︎
  3. 2022年9月这台电脑的内存接触不良,突然死机,强制重启后无法运转。反复插拔内存数次后侥幸恢复正常(可能是反复插拔磨去了插槽的触点和/或金手指上的氧化层),感谢坚强的日志式文件系统,数据安然无恙,没有因为异常卸载而损毁。 ↩︎
  4. 例如,当需要修改SSH服务器的设置时,不要直接修改/etc/ssh/sshd_config,而是将修改置于/etc/ssh/sshd_config/01-security-hardening.conf ↩︎

2条评论

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注