Logo
概览
容器运行时与 OpenSSL 漏洞整改:一次排查记录与学习总结

容器运行时与 OpenSSL 漏洞整改:一次排查记录与学习总结

2026年9月15日
3 分钟阅读

收到漏洞扫描结果时,我最初关注的是“应该升到哪个版本”。沿着组件关系、软件包来源和官方公告继续查,才发现升级之前还有一个更基础的问题:报告发现的组件,和我正在检查的组件,是否是同一个对象?

这篇文章围绕两条告警,整理一次测试环境中的排查与升级记录:

  • CVE-2021-30465:runc 挂载路径处理中的竞争条件问题。
  • CVE-2025-15467:OpenSSL 解析 CMS 消息时的栈缓冲区溢出问题。

重点是记录判断过程,以及这次排查帮助我理解的几个概念。

本文记录 Ubuntu 测试环境中的版本变化与核验过程,版本组合仅用于说明当时的测试,不作为当前安装推荐。官方资料核对日期为 2026 年 9 月 15 日;该日期不是实际整改日期。

一、先确定排查对象

同一个组件名称,可能出现在不同位置:宿主机软件包、容器镜像、应用自带依赖,甚至是不再使用的旧文件。扫描报告里的一个版本号,不能替代对这些位置的确认。

本次记录中的测试基线为 Ubuntu 22.04 LTS,测试机没有已有容器。保留的版本变化如下:

组件测试前测试后
Docker Engine / CLI24.0.628.5.2
containerd1.6.241.7.29
runc1.1.91.3.3
openssl / libssl33.0.2-0ubuntu1.183.0.2-0ubuntu1.29

以下检查命令在待核查的 Ubuntu 宿主机执行,用于记录系统、包版本和组件来源:

Terminal window
cat /etc/os-release
docker version
containerd --version
runc --version
dpkg -S /usr/bin/runc
dpkg-query -W -f='${Package}\t${Version}\n' openssl libssl3
apt-cache policy docker-ce docker-ce-cli containerd.io openssl libssl3

其中 docker version 要同时看客户端与服务端信息;如果 Docker CLI 连接的是远端 daemon,本机的 runc --version 就不能用来说明远端运行时的版本。多套运行时并存时,还需核对 daemon 配置与实际使用的二进制路径。

我的排查顺序因此变成了:先定位扫描对象,再确认组件来源,最后比较修复版本。

二、runc 告警:先理清谁提供了运行时

Docker、containerd 与 runc 的关系

在本文涉及的常见 Linux Docker 配置中,可以把调用关系简化为:

Docker:提供容器管理接口
↓
containerd:管理镜像与容器生命周期
↓
runc:按 OCI 规范创建和启动容器进程
↓
Linux namespace / cgroup / mount 等内核机制
↓
容器进程

这是一张帮助理解职责的简图,省略了 shim 等中间环节。

本次执行 dpkg -S /usr/bin/runc 得到:

containerd.io: /usr/bin/runc

这说明该路径由 containerd.io 软件包管理。对于这套安装方式,应沿着 Docker CE 软件包体系检查和更新运行时,避免另装发行版 runc 包或手工覆盖二进制,造成包管理记录与实际文件不一致。Docker 官方也说明,containerd.io 打包提供 containerd 和 runc,并列出了与发行版软件包的冲突关系。Docker 的 Ubuntu 安装说明

这不意味着每次修复 runc 都必须同时升级全部 Docker 组件;具体更新范围要看修复所在的软件包和兼容要求。本次选择了配套升级验证。

CVE-2021-30465 是否影响原版本

这个漏洞涉及 TOCTOU(检查时与使用时不一致):挂载目标通过检查后,路径可能在实际使用前被符号链接交换改变,导致挂载越出预期的容器根文件系统边界。利用还依赖特定的容器配置与共享卷条件。

runc 官方公告列出的受影响范围为 1.0.0-rc94 及以前,修复版本为 1.0.0-rc95。runc 安全公告

据此,记录中的 runc 1.1.9 不在该公告列出的受影响范围内。这里的判断前提是:扫描对象确实使用这份正常来源的 runc,且版本与文件核验一致。

告警仍存在时,可以继续检查扫描路径、运行时配置、旧文件残留及扫描规则,不能仅凭终端输出就宣布扫描器误报。针对一个 CVE 不受影响,也不能推导出这个版本没有其他漏洞。

三、运行时升级:把安装检查与运行验证分开

为什么采用分阶段测试

当时软件源中可见的候选还包括 Docker 29.x 与 containerd 2.x。考虑到跨主要版本会扩大排查范围,本次先验证 Docker 28.5.2 与 containerd 1.7.29,再考虑后续迁移。

这个选择反映的是当时的测试拆分方式,不代表中间版本天然安全,也不保证可以简单降级回滚。真正的变更方案仍需核对目标版本公告、业务兼容性以及数据和配置的恢复方式。

先看候选包,再模拟安装

测试机最初没有可用的 Docker CE 软件源配置,因此 apt-cache policy docker-ce 只能显示已安装版本。补齐软件源后,才看到了升级候选。

重新操作时,应按 Docker 官方安装说明 配置适合当前发行版的软件源,并查看实际可用版本:

Terminal window
# 在准备升级的测试宿主机执行,只查询候选版本
apt-cache policy docker-ce docker-ce-cli containerd.io
apt-cache madison docker-ce docker-ce-cli containerd.io

下面保留的是当时的安装模拟命令,其中 -s 表示模拟,不会实施安装:

Terminal window
apt-get -s install \
docker-ce=5:28.5.2-1~ubuntu.22.04~jammy \
docker-ce-cli=5:28.5.2-1~ubuntu.22.04~jammy \
containerd.io=1.7.29-1~ubuntu.22.04~jammy

当时的输出摘要为:

3 upgraded
0 newly installed
0 to remove

模拟没有报告依赖冲突或软件包移除,随后在测试机完成了安装。这里能得出的结论是“包管理器能够解析这组安装请求”,业务能否正常运行需要单独验证。

镜像拉取失败,先分辨失败发生在哪一层

升级后,Docker 与 containerd 的服务状态均为 active,但运行 hello-world 时出现镜像仓库请求超时:

Get "https://registry-1.docker.io/v2/": context deadline exceeded

在同一测试宿主机执行独立请求,也出现连接超时:

Terminal window
curl -v --connect-timeout 10 https://registry-1.docker.io/v2/

这支持先排查仓库访问链路,包括 DNS、代理、防火墙与出口连通性。它还不足以证明 Docker 配置完全正常,但说明本次失败停留在镜像获取阶段,尚未验证容器创建和进程执行。

用本地镜像补上最小运行验证

测试机已有 busybox-static。我用它构造最小根文件系统(rootfs),再通过 docker import 导入本地镜像,随后执行:

Terminal window
# 在已导入该本地测试镜像的宿主机执行
docker run --rm local-busybox:test /bin/echo "runc test ok"

实际输出:

runc test ok

这条命令依赖已导入的 local-busybox:test 镜像;镜像中需要包含可执行的 BusyBox,并提供 /bin/echo 入口。它用于验证已有本地镜像的运行结果,不能单独完成镜像构建。

相比只查询版本,这一步确认了当时配置下的容器创建与命令执行路径可用。不过,空测试机上的最小容器没有覆盖业务镜像、网络、数据卷、权限、重启恢复及持续运行表现。

四、OpenSSL:为什么打了补丁还是 3.0.2

上游版本与 Ubuntu 包版本是两套判断依据

CVE-2025-15467 涉及 OpenSSL 解析带有恶意 AEAD 参数的 CMS AuthEnvelopedData 或 EnvelopedData 消息,可能造成栈缓冲区溢出,导致崩溃或潜在代码执行。风险与应用是否处理相应不可信内容有关,不能把“安装了 OpenSSL”直接等同于“所有 TLS 服务都能通过这个漏洞被利用”。OpenSSL 安全公告

上游公告为 3.0 分支给出的修复版本是 3.0.19;其他维护分支有各自的修复版本。Ubuntu 22.04 则沿用 3.0.2 基础版本,通过 backport(补丁回移) 将修复应用到发行版软件包。

因此,升级后 openssl version 仍显示 OpenSSL 3.0.2,不能据此判断补丁未安装。需要查看完整包版本和发行版公告。

Ubuntu 对 Jammy 的 openssl 源软件包列出的该漏洞修复版本为 3.0.2-0ubuntu1.21。Ubuntu CVE-2025-15467 状态页

本次记录中的安装版本从 3.0.2-0ubuntu1.18 更新到 3.0.2-0ubuntu1.29。在确认其来自对应 Ubuntu 官方更新源的前提下,它已达到这个 CVE 的修复基线。

用软件包工具比较完整版本

下面的命令在安装了 Ubuntu Jammy 官方软件包的宿主机执行,分别检查包版本、候选来源与版本比较结果:

Terminal window
dpkg-query -W -f='${Package}\t${Version}\n' openssl libssl3
apt-cache policy openssl libssl3
# 只演示 Debian/Ubuntu 包版本比较;退出码 0 表示满足 ge(大于等于)
dpkg --compare-versions '3.0.2-0ubuntu1.29' ge '3.0.2-0ubuntu1.21'
echo $?

这个比较应放在同一发行版、同一软件包维护来源下理解。不要只比较末尾数字,也不要拿上游 3.0.19 与 Ubuntu 的 3.0.2-0ubuntu1.29 做普通字符串大小判断。

为什么同时检查 openssl 和 libssl3

发行版安全页面通常按源软件包追踪,而安装到系统里的往往是它构建出的多个二进制软件包:

名称在 Ubuntu 22.04 中的主要作用
openssl 源软件包提供构建所用的源码与发行版补丁
openssl 二进制包提供 OpenSSL 命令行工具
libssl3 二进制包提供应用加载的 libssl.so.3、libcrypto.so.3 等共享库
libssl-dev 二进制包提供编译所需的头文件与开发链接文件

只检查命令行工具,可能遗漏应用实际使用的库。即使磁盘上的包已更新,长期运行的进程也可能仍加载旧库;需要按维护计划重启相关服务或系统,再检查应用状态。这次测试仅核验了软件包版本,尚不能确认所有受影响进程都已重新加载新库。对应的 Ubuntu 安全更新通知 也要求在更新后重启系统。

针对这次系统包修复,没有必要为了匹配扫描报告里的上游版本号而手工覆盖系统 OpenSSL;应先核对发行版提供的更新。应用自带或静态链接的 OpenSSL 则需要沿应用自身的构建与发布渠道处理。

五、几个容易混淆的边界

同一个 CVE 下,不同软件包的状态可以不同

Ubuntu CVE 页可能同时列出 openssl、nodejs、edk2 等源软件包,需要逐行查看软件包与发行版的组合。它们可能通过直接提供、嵌入或携带相关代码而被纳入追踪。

状态阅读时应理解为
Fixed对应的软件包已有修复版本,还要核对本机是否安装
Vulnerable对应的软件包仍标记为受影响
Not affected对应的软件包在该发行版下不受此漏洞影响
Not in release该发行版没有发布这个软件包
Needs evaluation仍待评估,不能当作不受影响

例如,openssl1.0 在 Jammy 下显示 Not in release,不能解释成“系统上的所有 OpenSSL 都安全”。官方仓库没有某个包,也不能排除本机手工安装了相关代码。Ubuntu CVE 状态页

宿主机修复不会自动更新镜像里的库

宿主机执行 openssl version 与容器中执行同一命令,通常检查的是两份不同文件。容器里的软件包还需要按镜像自身的发行版及安装方式核验。

Terminal window
# 在 Docker 宿主机执行,将 CONTAINER_NAME 换成目标容器名
# 仅适用于镜像内有 openssl 命令的容器
docker exec CONTAINER_NAME openssl version -a

这里也只是初步定位,不能替代容器内完整包版本与依赖来源检查。镜像中没有 openssl 命令,同样不能证明应用没有携带 OpenSSL 库。镜像整改应进入基础镜像更新、重新构建、重新部署和扫描复核流程。

定向更新与系统升级应分别评估

当时执行 apt-get -s dist-upgrade 后,看到的模拟计划涉及内核、系统库和其他组件,还可能改变运行时主要版本,因此没有把它作为只更新 OpenSSL 的步骤。

dist-upgrade 可以安装或移除软件包以解决依赖变化;它本身不等于把 Ubuntu 22.04 切换到 24.04。APT 命令手册 说明了这类依赖处理行为。这次两个 CVE 的修复判断也不要求仅为它们进行发行版跨版本升级。

我的理解是:定向整改需要控制变更范围,系统性安全更新也要持续安排,两者都需要各自的验证计划。

六、这次记录能证明什么

已有证据可以支持的结论尚未覆盖的范围
runc 版本记录与上游公告记录中的 1.1.9 不在 CVE-2021-30465 公告受影响范围内扫描对象与实际文件的一致性、其他 CVE
Docker 与 containerd 为 active服务已启动业务功能与稳定性
本地镜像输出 runc test ok最小容器创建与命令执行成功网络、数据卷、业务镜像与恢复能力
openssl / libssl3 完整包版本为 .29对应官方 Jammy 包达到 CVE-2025-15467 修复基线旧进程、自带依赖、静态链接与镜像内部组件

这次能够确认的是测试环境升级与补丁版本核验。业务回归、受影响进程重新加载和扫描复核的结果仍待确认,因此还不能据此认定整个整改已经完成。

七、以后遇到类似告警,我会这样查

  1. 明确对象:记录 CVE、扫描路径,以及它属于宿主机、镜像还是应用依赖。
  2. 确认来源:核对发行版、完整包版本、安装渠道与实际使用的二进制。
  3. 查官方依据:上游公告用于理解问题,发行版公告用于判断对应软件包的修复状态。
  4. 确定变更范围:把必须修复的包与其他基线升级分清,先做依赖模拟和测试验证。
  5. 验证补丁生效:同时检查安装版本、进程重新加载和业务行为。
  6. 回到原告警复核:保留验证证据,明确完成项与尚未覆盖的对象。

这次最大的收获,是把“版本看起来够新”换成更具体的判断:哪个对象、哪份代码、由谁维护、哪个包修复,以及验证覆盖到了哪里。 这些问题问清楚后,升级决策和整改结论都会更有依据。