apt 安装任意软件产生 `libc6:amd64 package post-installation` 异常问题
零、问题描述
直接见如下内容
apt-get install lrzsz
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Suggested packages:minicom
The following NEW packages will be installed:lrzsz
0 upgraded, 1 newly installed, 0 to remove and 246 not upgraded.
8 not fully installed or removed.
Need to get 0 B/74.8 kB of archives.
After this operation, 531 kB of additional disk space will be used.
Setting up libc6:amd64 (2.35-0ubuntu3.11) ...
dpkg: error processing package libc6:amd64 (--configure):installed libc6:amd64 package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:libc6:amd64
E: Sub-process /usr/bin/dpkg returned an error code (1)
让我们来分析这个报错,但首先需要有这样 3 个补充知识
一是 apt
在安装软件包之前,会检测系统是否有未处理好的历史安装和配置情况,如果存在会先尝试处理这些问题(这样设计的原因是这样使得整体系统变得更加稳定)。
这对应了上面内容中 8 not fully installed or removed
这一部分,而这里面就包括 libc6:amd64
,所以 apt
在安装其他软件前需要先来处理 libc6:amd64
的遗留问题。
二是 apt-get
是依赖于 dpkg
来工作的:
dpkg
是 Debian/Ubuntu 系统中最底层的包管理工具,负责直接处理.deb
包的安装、卸载、配置等核心操作(例如解压包文件、执行维护脚本、更新包状态数据库等)
apt-get
是更高层次的工具,它的主要作用是解决包依赖关系、从软件源下载.deb
包,但其最终执行 “安装 / 配置包” 的实际操作时,必须调用dpkg
来完成
所以调用 apt-get
的时候,才会显示 dpkg: error...
。
三是 dpkg
提供了 postinst
脚本机制,用来在安装或升级软件包时触发的 “后置操作”。而这里的问题,正是后置操作返回了状态 1 返回码(非 0 即为异常)。(对应 post-installation script subprocess returned error exit status 1
)
一、预备知识:关于 dpkg 安装状态流转
如果想理解这个问题,必须先对 dpkg 安装软件的流程有所了解。dpkg -i
如果正确安装软件,其流程概述如下
- 软件状态存储在
/var/lib/dpkg/status
文件中(所有已安装和安装中的软件都会有记录),在一个软件安装中会上锁,所以 debian 系列系统都无法并行使用dpkg
命令操作两个软件 - 初始状态为
not-installed
,开始进行解包操作(解包过程中处于half-installed
状态).deb
包为ar
归档格式- 提取控制信息(如
control
文件)到/var/lib/dpkg/info/
等目录,用于后续配置和状态记录 - 软件内容会先解包到临时文件
/var/lib/dpkg/tmp.ci/
中 - 然后依据解包时的目录结构,把对应数据文件(例如可执行文件等)拷贝到对应目录
- 解包操作成功后进入
unpacked
状态,开始进行配置(configure
)操作(配置过程中处于half-configured
状态)- 运行包内
DEBIAN/postinst
脚本(如创建用户、初始化服务、配置环境变量等) - 处理配置文件(如合并用户自定义配置与默认配置,例如升级时不能覆盖已有的配置文件)
- 更新包依赖关系记录,确保系统依赖状态一致性(
/var/lib/dpkg/status
和/var/lib/dpkg/available
文件)
- 运行包内
- 如果配置不报错,就会进入
installed
状态,这个状态就是安装完成状态
完整的状态流转如下,包含安装、卸载和一些异常状态:
二、问题排查
使用如下命令,查看有哪些 dpkg
包 not fully installed or removed
。
dpkg --configure -a
参考上面的内容和流程图,可以知道这句命令的作用是把所有 dpkg
安装的所有未完成配置(配置失败、解压后未配置)的软件,重新触发一次配置,这样就可以清晰的看出问题软件的信息。起输出如下:
# dpkg --configure -a
Setting up linux-libc-dev:amd64 (5.15.0-156.166) ...
Setting up perl-modules-5.34 (5.34.0-3ubuntu1.5) ...
Setting up libc6:amd64 (2.35-0ubuntu3.11) ...
dpkg: error processing package libc6:amd64 (--configure):installed libc6:amd64 package post-installation script subprocess returned error exit status 1
dpkg: dependency problems prevent configuration of libc6-dev:amd64:libc6-dev:amd64 depends on libc6 (= 2.35-0ubuntu3.11); however:Package libc6:amd64 is not configured yet.dpkg: error processing package libc6-dev:amd64 (--configure):dependency problems - leaving unconfigured
dpkg: dependency problems prevent configuration of libc-dev-bin:libc-dev-bin depends on libc6 (>> 2.35); however:Package libc6:amd64 is not configured yet.libc-dev-bin depends on libc6 (<< 2.36); however:Package libc6:amd64 is not configured yet.dpkg: error processing package libc-dev-bin (--configure):dependency problems - leaving unconfigured
dpkg: dependency problems prevent configuration of libc-devtools:libc-devtools depends on libc6 (>= 2.34); however:Package libc6:amd64 is not configured yet.dpkg: error processing package libc-devtools (--configure):dependency problems - leaving unconfigured
dpkg: dependency problems prevent configuration of libperl5.34:amd64:libperl5.34:amd64 depends on libc6 (>= 2.35); however:Package libc6:amd64 is not configured yet.dpkg: error processing package libperl5.34:amd64 (--configure):dependency problems - leaving unconfigured
dpkg: dependency problems prevent configuration of perl:perl depends on libperl5.34 (= 5.34.0-3ubuntu1.5); however:Package libperl5.34:amd64 is not configured yet.dpkg: error processing package perl (--configure):dependency problems - leaving unconfigured
dpkg: dependency problems prevent processing triggers for man-db:man-db depends on libc6 (>= 2.34); however:Package libc6:amd64 is not configured yet.dpkg: error processing package man-db (--configure):dependency problems - leaving triggers unprocessed
dpkg: dependency problems prevent processing triggers for libc-bin:libc-bin depends on libc6 (>> 2.35); however:Package libc6:amd64 is not configured yet.libc-bin depends on libc6 (<< 2.36); however:Package libc6:amd64 is not configured yet.dpkg: error processing package libc-bin (--configure):dependency problems - leaving triggers unprocessed
Errors were encountered while processing:libc6:amd64libc6-dev:amd64libc-dev-binlibc-devtoolslibperl5.34:amd64perlman-dblibc-bin
其错误依赖如下图
可见所有问题的源头在于 libc6:amd64
的 post-installation
脚本执行失败,退出状态码为 1。
手动执行 libc6:amd64
的 post-installation
脚本(位于 /var/lib/dpkg/info/libc6:amd64.postinst
,参数2.35-0ubuntu3.11
在最开始的报错里有)
bash -x /var/lib/dpkg/info/libc6:amd64.postinst configure 2.35-0ubuntu3.11 2>&1
补充知识:
bash -x
会输出执行的语句,这样方便定位脚本执行到哪里出了问题(只是说可能)。输出中每个 bash 语句前面的+
的数量代码脚本的深度(当脚本互相调用时)
结果如下:
+ set -e
+ export LC_ALL=C
+ LC_ALL=C
+ type=configure
+ preversion=2.35-0ubuntu3.11
+ '[' configure = configure ']'
+ all_upgraded=yes
+ for pkg in libc6.1-alphaev67 libc6-xen
++ dpkg-query -l libc6.1-alphaev67
++ sed -e '/^[a-z][a-z]\s/!d;/^.[nc]/d;' -e 's/^..\s\+libc6.1-alphaev67[0-9a-z:]*\s\+//;s/\s.*//g'
+ ver=
+ '[' -n '' ']'
+ for pkg in libc6.1-alphaev67 libc6-xen
++ dpkg-query -l libc6-xen
++ sed -e '/^[a-z][a-z]\s/!d;/^.[nc]/d;' -e 's/^..\s\+libc6-xen[0-9a-z:]*\s\+//;s/\s.*//g'
+ ver=
+ '[' -n '' ']'
+ '[' yes = yes ']'
+ rm -f /etc/ld.so.nohwcap
+ '[' configure = configure -a -z '' ']'
+ '[' -f /usr/share/debconf/confmodule ']'
+ . /usr/share/debconf/confmodule
++ '[' '!' '' ']'
++ PERL_DL_NONLAZY=1
++ export PERL_DL_NONLAZY
++ '[' '' ']'
++ exec /usr/share/debconf/frontend /var/lib/dpkg/info/libc6:amd64.postinst configure 2.35-0ubuntu3.11
执行后执行 echo $?
,发现退出状态确实为 1。
那我们的排查思路,就是在 /var/lib/dpkg/info/libc6:amd64.postinst
中关键步骤上增加日志输出,来定位脚本究竟执行到哪里产生了问题。
备注 1:由于这个脚本行数较多,我的日志策略是首先在每个 IF-ELSE 分支入口打日志,定位到线性执行代码的逻辑处;再进行”折半查找“打日志,可以高效定位到问题代码行数。
备注 2:重要 - 修改之前一定要记得备份原有脚本!!!
最终可以定位到如下命令执行失败,状态码为 1
systemctl daemon-reexec
补充知识:正常 bash 脚本某一条命令失败后会继续后面的脚本
但是如果在脚本的最开始设置了set -e
时,就会当某条命令失败时脚本会立即终止,并将该命令的退出状态码作为整个脚本的退出状态码
我们可以暂时跳过这个错误(临时方案),将文件 /var/lib/dpkg/info/libc6:amd64.postinst
中原有的 systemctl daemon-reexec
修改为如下代码
# 无论 systemctl daemon-reexec 成功失败都返回成功
systemctl daemon-reexec || true
然后尝试执行 dpkg --configure -a
,发现处于 half-configured
状态中的软件,可以被配置好不报错了。
再次执行 apt-get
的安装操作,一切正常。
三、关于 systemctl daemon-reexec
失败的问题
这台服务器遭受过挖矿木马的攻击(查杀过程可以参考之前的博文)
后来又经过长时间的排查,发现这台服务器的 systemctl
命令被篡改过,调用特定命令时会尝试执行某脚本(但是已经被查杀了),这导致 systemctl daemon-reexec
实际是执行成功了(生效),但是返回 1(因为恶意代码没有执行成功)。
后来我还想将 systemctl
恢复回去,但是发现难度很大,也没有继续尝试了。
总结
本文记录了 apt 安装任意软件产生 libc6:amd64 package post-installation
异常问题排查思路和解决途径,最关键的还是要了解 dpkg 的执行过程。
不过遗憾的是,被篡改的 systemctl
未能修复。