上个月例行安全扫描的时候,某现场环境提交上来的报告里有一行需要优先处理的条目:SUSE 15 内核漏洞 CVE-2025-38352,官方给出的修复路径是内核升级(Kernel升级)。很多运维兄弟看到CVE编号第一反应是赶紧升级内核,但实际操作里真正麻烦的往往不是那一条命令,而是围绕它的一整套流程:怎么确认当前环境到底受不受影响、打补丁走哪条通道最稳、重启后怎么证明问题已经修复、新内核起不来怎么办。这篇文章我就按自己处理这件事的顺序,把从定性到验证、再到回滚预案的完整路径整理出来,给后面要处理同类任务的团队留个参考。
这个任务适合谁看?一类是服务器上正在跑 SLES 15 或 openSUSE Leap 15 系的运维人员,另一类是手上设备数量多、需要统一做补丁管理的同学。内容不会停留在“执行 zypper patch”这种表面操作,而是会把每一步背后的判断逻辑讲清楚,包括什么时候该走补丁通道、什么时候该手动换包、旧内核在 GRUB 里怎么保留、第三方驱动怎么避免掉链子。先把结论放在前面:CVE 修复不是一次性动作,它是一条从情报核对到运行验证的完整链路,链路越完整,升级越安全。
1. 先定性:CVE-2025-38352到底影响你哪一层
外行人容易把“漏洞修复=升级补丁”画等号,但我处理这类内核漏洞的第一步永远是定性。所谓定性,就是把三件事查清楚:这个编号是针对哪个组件的、官方公告里说的受影响范围是什么、修复版本落在哪个内核包版本号上。只有把这三个问题确认了,后面的所有操作才有一个可执行的目标。
1.1 从编号能读出什么,读不出什么
CVE 编号的格式很好解读:CVE-2025-38352 表示这是一个在 2025 年被公开收录的漏洞编号。前半段年份说明披露时间窗口,后半段只是分配的唯一序号,并不代表漏洞严重性排名,所以看到编号本身,我们只能知道它是一个被国际公开收录的漏洞记录,具体技术类型、风险等级和影响组件,全部要以发布机构或操作系统厂商的安全公告为准。
这也顺便回答了一个常见问题:为什么不能拿编号去网上搜个关键词就直接开干?因为同一个 CVE 在不同发行版上的处理方式可能完全不同,内核版本基线、补丁打包方式、依赖关系都有差别,在 SUSE 15 上适用的修复路径,放在其他发行版上未必成立,甚至同一套系统不同 SP 版本都会导致补丁编号不同。举个例子,SLES 15 SP3 和 SP5 虽然都叫“SUSE 15”,但内核基线相差好几个代际,适用的补丁包完全是两套东西。
1.2 官方公告核对清单:别凭印象猜影响范围
拿到 CVE 编号后,可靠的做法是到 SUSE 官方安全公告页面搜索 CVE-2025-38352,重点看 Patch、受影响产品和修复包三块信息。我自己会列一个核对清单,避免漏项:
| 核对项 | 查询内容 | 说明 |
|---|---|---|
| 受影响操作系统 | SLED/SLES 15 的哪个 SP 版本 | 不同 SP 的内核基线不同,直接影响补丁路径 |
| 受影响架构 | x86_64、aarch64、s390x 等 | 部分漏洞只在特定架构上可利用或修复 |
| 修复包信息 | kernel-default 等包名及版本号 | 确认修复后版本,用来做升级后的比对 |
| 补丁编号 | SUSE 安全补丁编号 | 补丁通道操作时直接按补丁号应用,比按 CVE 号更精确 |
| 风险等级 | 官方给的严重性评价 | 决定整改窗口长短,不能自己凭感觉定 |
这个清单看起来简单,但很容易漏掉两个点。第一是 SP 版本判断,很多机器表面显示 SLES 15,实际是 15 SP2 还是 SP6,内核基线差着好几个代际,修复补丁不是简单向下兼容的。第二是仓库来源,生产环境一般会走内部镜像仓库,如果镜像同步不及时,公告上明明发了补丁,本机仓库却搜索不到,这时优先同步镜像而不是绕过补丁通道强行装内核包。
2. 升级前的系统体检:不该跳过的三项准备
定性完成后,理论上可以直接进入升级,但我强烈建议先把三项体检做完:系统身份确认、仓库可用性确认、备份与回滚入口确认。跳过任何一项,都有可能让一个本来半小时完成的补丁操作变成一整个通宵的救火作业。
2.1 确认SLES版本和当前内核身份
首先要做的不是升级,而是先看自己站在哪一块地上。用下面三条命令把系统身份、内核身份一次拿到:
# 确认发行版与 SP 版本 cat /etc/os-release # 查看当前正在运行的内核 uname -r # 查看已经安装到磁盘上的内核包 rpm -q kernel-default三行输出要结合看:系统显示 SLES 15 SP3,但 uname -r 显示的可能是这个 SP 下某个维护版本,rpm -q 会给出具体的包版本号,比如 5.14.21-150400.xx.x 这种格式。SUSE 15 系内核包版本号中会带“150xxx”这样的构建代号,这个代号直接对应 SP 版本,比如 150400 对应 SP4 基线,150500 对应 SP5 基线,核对的时候能一眼看出当前系统的补丁级别。
2.2 检查补丁源与仓库权限
内核补丁必须从正确的补丁源拉取。SUSE 15 在默认安装后一般会自带的仓库包含基础补丁源,但很多环境出于内网安全考虑,会把默认仓库替换成内部镜像,这时候至少要做两件事:确认补丁通道存在,确认本机有权限读取。
# 查看仓库列表 zypper repos # 只看与补丁相关的仓库 zypper repos | grep -i update这里容易踩的第一个坑是“仓库存在但内容未同步”。内网镜像仓库往往由值班同事定时同步,公告发布和镜像同步之间存在窗口期,如果搜索补丁时返回空列表,不要第一时间怀疑系统坏了,先到镜像服务器上确认同步状态,或者临时挂载官方在线仓库完成本次补丁拉取,补丁拉起后切回内网源继续日常使用。
2.3 备份方案与回滚入口
内核升级这种操作不像数据库迁移那么复杂,但由于它涉及整个系统运行底座,哪怕概率很低,也必须有备案。我的习惯是记录三项内容构成回滚预案:当前内核包版本号记录、配置文件变更清单、可用启动项确认。当前内核包版本号记录来自 rpm -q kernel-default;配置文件变更主要是 GRUB 相关目录和 /etc/modprobe.d 等模块参数目录;可用启动项确认则是在升级前先看一眼 GRUB 菜单里已有几个内核项。
这三项中,最容易忽视的是启动项确认。很多运维会以为“反正是多内核共存,肯定能回滚”,但实际上一台机器如果之前被人为设过启动项清理定时任务,或者 GRUB 配置损坏,升级后很可能找不到可用的旧内核入口。所以升级前花十秒钟执行 grep -c '^menuentry' /boot/grub2/grub.cfg,确认至少保留两个内核 entry,比出事后再翻文档要高效得多。
3. 内核升级主流程:补丁通道还是手动换包
体检做完,进入真正动手的环节。这一节先把两条路线讲清楚,再给出推荐操作。两条路线分别适合不同场景,但核心原则只有一个:变更范围越精准,回归风险越可控。
3.1 为什么不建议直接zypper update一把梭
很多人处理漏洞的习惯是先来一发 zypper update 把系统整体更新一遍。这种方法在个人实验机上问题不大,但在生产环境里风险集中在一点上:更新范围不受控。zypper update 会把主更新仓库里所有可更新的包全部拉下来,其中大部分和 CVE-2025-38352 这个漏洞毫无关系,这等于用一次大范围变更去修复一个小问题,一旦某个关联包出现兼容问题,排查的边界会扩散到整个操作系统。SUSE 官方设计的补丁通道正是为了解决这个问题:补丁是按主题打包的,应用一个补丁只会带来与该主题相关的一组包变更,范围可控、依赖经过验证。
3.2 通过补丁通道处理CVE的标准操作
针对 CVE 编号,标准操作是先列出相关补丁,再查看内容,最后精准应用。在 SUSE 15 上可以这样:
# 查询与 CVE-2025-38352 相关的补丁 zypper list-patches --cve=CVE-2025-38352 # 查看补丁详情,确认包含哪些包变更 zypper patch-info <补丁编号> # 给该补丁权限内全部应用 zypper patch --cve=CVE-2025-38352补丁列表里如果出现了 kernel-default、kernel-devel 这类包名,说明该补丁确实会更新内核,继续执行 zypper patch 即可。这里要提醒一个细节:zypper patch 本身是一个交互式过程,如果担心挂起,可以先加 --non-interactive 参数试跑,确认输出无误后再正式执行。执行结束后,用 rpm -q kernel-default 再次确认新内核包版本号与公告上的修复版本是否一致,这一步才是“已安装”的证据。
3.3 手动指定内核包版本的适用场景与连带关系
补丁通道是首选,但有些场景必须走手动换包:离线内网环境无法直接访问在线补丁源,且内部镜像仓库同步永远滞后;或者集群里所有节点被发现运行在不同小版本上,需要统一到同一个内核版本。这时候可以这样操作:
# 查询仓库中可用的内核包版本 zypper se -s kernel-default # 安装指定版本(示例格式,实际版本以仓库输出为准) zypper in kernel-default-5.14.21-150500.55.73.1手动安装内核包时有条连带规则必须记住:内核跑起来依赖的是 kernel-default、kernel-devel、kernel-source、kernel-firmware 这一组包的一致性。只升级 kernel-default 而不管 kernel-devel,后面做内核模块编译或 DKMS 重建时就会失败。所以手动换包时建议把这一组包一起列出来同步操作,版本对齐后再生成 initrd。如果仓库里只有新版没有旧版,也不要试图用 zypper 来“回装”不存在的包,那属于另一个问题,后面第 5 节会展开讲。
4. 重启与验证:内核更新后必须确认的四个状态
内核包更新完,真正的“临门一脚”是重启。重启不是拍一下 reboot 就结束的,从按下回车之前到系统回到可用状态,有几个状态必须落实。我把它们拆成四件事:引导条目、内核身份、启动时间、运行态健康,四件事都确认过,这次升级才算真正做完。
4.1 重启前核对引导条目
重启前先确认新内核已经出现在 GRUB 菜单里,否则重启后跟你打交道的可能还是旧内核。用这条命令查看当前生成的启动项:
grep -E '^menuentry|^submenu' /boot/grub2/grub.cfg绝大多数情况下安装内核包时就会自动刷新 GRUB 配置,但如果是手动 rpm 安装、或者做过自定义 GRUB 配置的环境,自动刷新可能没有生效。看到新内核没出现在菜单里时,执行一次重建配置:
grub2-mkconfig -o /boot/grub2/grub.cfg这里也要确认默认启动项指向的是新内核。执行 grub2-editenv list,看看 saved_entry 是否指向新版菜单项。如果环境调整为“启动时手动选择”,也要在下一次重启时不要手滑选错项,否则整个验证过程会变成乌龙。
4.2 重启后的内核身份与启动时间确认
重启完成后,第一件事是把身份信息再拉一遍:
uname -r cat /proc/cmdlineuname -r 确认当前运行内核等于公告的修复版本,cat /proc/cmdline 确认系统确实从新内核的菜单项启动,而不是从旧内核项启动。这里有个判断技巧:如果重启后 uname -r 显示的版本和升级前一样,不一定表示升级失败,更可能是在启动菜单里选了旧内核。用 uptime -s 看一眼启动时间,再对比你按下重启按钮的时间,两个时间吻合,说明系统确实是刚完成重启并跑在你期望的内核版本上。
4.3 运行态体检:dmesg、服务状态和模块加载
版本对上了,还要确认运行态没有隐藏问题。我一般是按“错误日志、服务状态、模块加载”三步走:
# 看启动以来内核是否报错 dmesg -T | grep -iE 'error|fail|panic|OOM' | grep -v 'audit' # 查看所有失败状态的服务 systemctl --failed # 确认第三方模块是否已经加载到新内核 lsmod | grep -E '模块名'过滤日志时不要指望完全干净,内核日志里会有些良性提示,重点看有没有和文件系统、存储、网络驱动相关的硬错误。如果发现某个服务因为依赖模块没加载而启动失败,八成是第三方驱动的锅,这个场景放在第 5 节展开。模块加载则要对照升级前的 lsmod 输出,如果发现重要模块缺失,说明该模块对应的驱动没有随内核升级重建。
5. 升级失败的应急回滚与启动流程急救
按理说做了这么多前置工作,升级失败的场景应该很少,但生产环境总会有意外。这一节是“万一”的路径,而且我认为每个执行内核升级的人都应该先读这一节再动手,而不是等出了问题再翻。
5.1 从GRUB选择旧内核冷启动恢复
如果重启后新内核直接崩溃、黑屏或者卡在内核 panic 状态,最快的恢复手段是冷启动并选择旧内核项。SUSE 15 的 GRUB 菜单默认会将历史内核保留为独立菜单项,开机后在 GRUB 界面选择不含新版本号的内核项进入系统。开机菜单显示时间可能很短,可以在重启前先把 GRUB 超时改长一点,或直接在 GRUB 启动项里按住方向键调出菜单。
进入旧内核后,不要着急做任何修改,先用 uname -r 确认自己确实跑在旧内核上,再检查新内核到底为什么起不来。这时候 /var/log/messages、journalctl -k 的输出会给你线索,很多时候能看到网卡驱动或特定硬件驱动的加载报错,这属于第三方模块问题。
5.2 用包管理器回到上一版本:顺序比拉包更重要
确认旧内核运行稳定后,再考虑是否将新内核包卸载回退。顺序很重要:先在旧内核下禁用新内核的默认启动指向,再卸载新内核包,最后重新生成引导配置。直接在新内核起不来的状态下尝试卸载新内核包是没法操作的,因为卸载动作本身需要在可运行的系统里执行。
在旧内核环境下,回退命令大致是这样:
# 先卸载新内核包 zypper rm kernel-default-新版本号 # 重新生成引导配置,确保只保留旧内核 grub2-mkconfig -o /boot/grub2/grub.cfg卸载时要注意:如果补丁同时更新了 kernel-firmware 或 kernel-devel,这三个包需要视情况一起处理。我遇到过只回滚 kernel-default、但 firmware 留在新版的情况,大部分设备没问题,可个别网卡固件和旧内核配合后出现罕见丢包,最后把 firmware 也回滚才恢复。所以我的建议是:回滚前先记录补丁更新了哪些包,回滚时尽量整套回退,宁可多回滚一层,也不要留版本错配。
5.3 第三方驱动与新内核不匹配的处理思路
升级内核后最容易出现的启动失败原因之一,是第三方内核模块没有跟上。系统里如果装了外部供应商提供的驱动包,模块编译目录是按旧内核路径放的,新内核找不到对应模块,就直接把相关服务或文件系统挂掉了。处理思路不是回退内核,而是把第三方驱动重新适配到新内核上:确认驱动安装包是否支持当前新内核,如果支持,重装驱动包并重建 initrd;如果不支持,那就只能暂时回退内核,等待驱动厂商发新版。
重建 initrd 在 SUSE 15 上很直接:
# 重新构建 initrd,把新内核模块打包进去 dracut -f # 或老版本使用的命令 mkinitrd重建后再重启验证。这里分享一个排查判断:如果重启后能进单用户模式或救援模式,可以先用 ls /lib/modules/$(uname -r) 看新内核的模块目录,对比一下驱动包提供的模块是否存在于对应目录,这样能快速定位是“模块没装”还是“模块装了但没进 initrd”。
6. 生产环境上的排坑清单与实践心得
最后一部分,整理几条我在 SUSE 15 内核升级过程中反复遇到的坑,以及对应的建议。这些内容不来自任何文档,全是从实际操作里攒下来的。
6.1 升级过程中反复出现的坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 补丁已应用但版本号没变 | rpm -q 显示的版本和公告不一致 | 用 rpm -qa | grep kernel-default 看完整安装列表,确认是否多版本共存 |
| 调优参数被重置 | 重启后 sysctl 设置恢复默认值 | 升级前导出 sysctl -a 留档,重启后 diff 比对 |
| 批量重启同频 | 整个集群同时中断,一旦新内核有问题影响面全开 | 先升级一台边缘节点,验证后再分批推进 |
| 网卡固件被顺带更新 | 业务出现偶发断连,排查发现是固件包版本被补丁带上去 | 记录补丁包清单,必要时回滚固件包 |
第一坑是本机仓库里补丁已经应用,但 rpm -q 版本号没变。这种多半是补丁被应用到系统后,又因为其他操作把包回滚了,或者同时存在多内核版本时 rpm -q 只显示了最新安装的一个,需要用 rpm -qa | grep kernel-default 看看完整安装列表。
第二坑是升级后 sysctl 和 tuned 调优参数被重置。内核更新包裹带一些系统默认值变更,如果之前手动改过 /etc/sysctl.conf 或通过 tuned 模板做过性能调优,重启后这些配置可能被覆盖或不再匹配。建议在升级前把 sysctl -a 的当前生效配置导出留档,重启后做一次 diff。
6.2 哪些步骤可以按环境精简:我的取舍建议
流程虽然长,但不是每个环境都需要全套执行。个人测试机或开发环境,我一般只做基础身份确认和磁盘空间检查,直接 zypper patch 或 zypper in 升级,省去仓库核对和回滚预案;生产环境则严格执行本章完整流程,特别是补丁通道、重启前引导条目确认和模块健康检查这三步不能省。
如果是维护大批量同构机器,建议把前面第 2、3 节的固定命令整理成脚本,统一记录升级前后的内核包版本、补丁编号、模块列表,既方便审计,也便于事后追溯。脚本里最后一步建议自动输出升级前后对照,而不是只输出一个“完成”,这样出问题时能快速定位差异。
6.3 最典型的失败路径:跳过补丁通道带来的失控
我见过不止一次这样的场景:有人处理同类型漏洞时,直接在一台有业务流量的节点上执行了 zypper update,结果升级前后两个小时内业务日志出现大量连接异常,排查半天发现是某个网卡固件包被顺带更新了,驱动和新固件配合不稳定。最后回滚了整个补丁批次才恢复。这个案例想说明的是,漏洞修复的优先级固然高,但“修复”和“引入新风险”之间必须靠精准补丁和分批执行的纪律来平衡。
我个人现在的习惯是,遇到 CVE 相关内核升级,先写一张一页纸的操作单,把定性信息、升级命令、验证命令、回滚命令四块列在上面,做完一项划掉一项。看着有些笨,但配合半自动脚本,能明显降低漏步骤的概率。补丁升级这种事,真正的风险往往不在那条核心命令上,而在那些你以为“应该没问题”的边边角角。