最近把自己手底下那批SUSE 15服务器做了一次内核升级,起因就是CVE-2025-38352这个编号的漏洞公告。做运维的看到“Kernel”和“漏洞修复”这两个词放在一起,基本就知道这趟活儿省不了心:升级窗口要排,依赖要理顺,第三方模块还要提前确认兼容性。这篇文章就把我这次从威胁研判、升级前检查到实际执行、验证回滚的完整过程拆开写一遍,重点放在SUSE 15(SLES 15)上用zypper做内核升级的具体操作步骤,以及那些官方文档里不会告诉你的坑和判断逻辑,给后面需要同样操作的兄弟们做个参考。
1. 漏洞背景与影响范围
1.1 CVE-2025-38352到底是什么
CVE-2025-38352属于内核级别的安全漏洞。虽然不同SP版本受影响的严重程度不一样,但这类漏洞通常有两个共性:一个是CVSS评分往往不低,另一个是修复只能靠内核版本升级,通过打补丁的方式去修很难完全根治,因为漏洞点直接位于内核子系统内部代码里。
我看到公告的第一反应不是急着去点升级按钮,而是先搞清楚自己的环境是不是真的在受影响范围之内。SUSE的CVE公告页面一般会写清楚受影响的包名、修复包名、内核基准版本号。比如kernel-default包会给出完整的内核RPM包名,对照当前系统里的版本号,一眼就能判断出风险。
这里要给刚接触SUSE的朋友补充一个点:SLES 15不是只有一个内核版本,它有不同的Service Pack(SP)分支。SP1、SP2、SP3、SP4、SP5、SP6各自维护不同的内核基线,所以CVE-2025-38352在不同SP分支上的“修复版本号”是完全不一样的。升级之前一定不要只记一个版本号,要看你的SP版本对应的那个修复版本。
1.2 受影响系统与风险评分
从实际影响来看,这类漏洞如果触发成功,通常可能导致本地权限提升、系统崩溃,严重情况下可以让攻击者获得更高的操作权限。对于跑生产业务的SLES 15环境来说,权限提升意味着后续横向移动、篡改配置、窃取数据都成了可能,所以风险等级我一般按高优先级处理。
不过优先级高不代表要马上在生产环境上动手。正确的处理方式是把受影响的机器分个类:哪些是测试环境、哪些是灰度环境、哪些是核心业务生产机。测试环境必须第一时间升级,跑一遍业务回归;核心生产机则放在维护窗口内执行,而且要有完整的回滚方案之后才动。
1.3 修复策略选型:内核升级还是临时缓解
SUSE官方针对有些内核漏洞会给出临时缓解措施,常见的就是sysctl参数调整、限制某些内核模块加载这类操作。但我的建议很简单:临时缓解只能用于争取时间,最终问题还是要靠内核升级来解决。
原因有三个。第一,临时缓解措施只针对已知的攻击路径,攻击者换一个调用链就可能绕过。第二,每个缓解参数都可能影响系统性能或者业务行为,不敢长时间挂着。第三,内核漏洞往往会随着开发者的深入利用研究被不断发现新的变种,最稳妥的做法就是直接升到修复版本上,一了百了。
所以这次我没有去研究那些临时参数,直接把重点放到了内核升级的完整流程上。接下来的内容,就是从升级前准备到实际操作再到验证回滚的全过程。
2. 升级前要做的四件事
2.1 确认当前内核版本和目标版本
无论你多清楚内核对齐的重要性,实际动手之前都要在系统里跑一遍确认命令。我自己基本上固定用下面这几条来摸底:
cat /etc/os-release uname -r zypper se kernel-default hostnamectl第一行看系统版本,确认是SLES 15还是SLE 15 SP几;第二行看当前运行的内核;第三行能从仓库里看到最新的可用内核包版本;最后一行能顺便确认机器角色。不要凭印象写结论,必须把这些信息先抓出来。
拿到当前内核版本之后,对照SUSE官方CVE页面上给出的“Fixed version”字段,就能确认升级目标版本。举个例子,如果公告里修复版本是kernel-default-5.14.21-150500.53.1,而你当前是5.14.21-150400.24.1,那目标就很明确了。
注意:这里说的版本号只是示例,实际请以你订阅的SUSE仓库里
zypper se -s kernel-default输出的可用版本为准。不同SP版本的目标内核包名后缀差异很大。
2.2 磁盘空间与引导空间评估
内核升级最容易被忽略的就是磁盘空间检查,尤其是/boot分区。很多SLES 15机器的/boot分区大小只有几百兆,旧内核、新内核、initrd镜像、System.map文件全部堆在一起,很容易把/boot塞满。
检查命令是:
df -h /boot df -h /var为什么还要看/var?因为zypper的缓存和下载的RPM包会放在/var/cache/zypp/packages,如果/var空间不够,下载中断会导致依赖解析出错。经验上/boot剩余空间建议大于500MB,/var剩余空间建议大于2GB,才算比较稳妥。
如果空间紧张,可以在升级前先用zypper clean清理缓存,或者手动清理掉不再用的旧内核包。但注意:不要让zypper把当前运行的内核也清理掉,手动删除旧内核时务必保留当前版本。
2.3 业务影响评估与维护窗口
内核升级无法热切换,必须重启系统才能生效,这是所有人都绕不开的硬约束。所以升级前一定要跟业务方确认维护窗口。
我自己一般会重点关注这几项:
- 应用是否有集群模式、多节点负载分担,是否允许逐台重启。
- 有没有长连接任务、批量任务、定时任务在维护窗口内启动。
- 数据库等有状态服务,重启前是否已经完成持久化或主备切换。
- 是否有外接硬件依赖对应驱动模块,升级后驱动是否还能加载。
这些评估结果直接决定我是在凌晨操作还是可以工作时间段执行。做过几年生产环境运维的人应该都清楚,内核升级本身成功率不低,真正导致翻车的往往是业务侧没评估到位。
2.4 回滚预案设计
每次升级内核之前,我都强迫自己做好回滚预案。回滚预案不是说“出问题就重启进旧内核”,而是要提前确认旧内核的启动项是否还在,新内核是否已经写入了GRUB配置,以及如果新内核启动失败,默认启动项是否指向旧内核。
在SLES 15上,安装完新内核后GRUB会把新内核放在启动菜单最前面。正常情况下没问题,但如果新内核和机器上的某些第三方内核模块不兼容,启动阶段就可能卡住或直接panic。所以升级前最好手动看一眼当前GRUB菜单里有哪些启动项:
grub2-once --list 2>/dev/null || cat /boot/grub2/grub.cfg | grep 'menuentry '把输出保存下来,心里有数。真到需要回滚的时候,重启机器,在GRUB菜单里选择旧内核的启动项,进系统后检查后续状态,再决定是重新排查还是保持旧内核运行。
3. 内核升级实操全过程
3.1 刷新仓库并确认候选内核
在SLES 15上安装新内核前,先把软件源刷新到最新,不然仓库里的元数据可能是旧的,压根看不到修复版本。
zypper refresh刷新完成之后,用下面的命令确认仓库里是否有修复内核包:
zypper se -s kernel-default | tail -5-s的作用是同时显示版本号、架构和仓库来源,便于准确判断目标版本。这里我想提醒一句:SLES 15的模块化仓库结构里,内核包一般来自SLE-Module-Basesystem和SLE-Module-Legacy,这两个模块必须启用。如果你发现zypper se kernel-default看不到任何候选版本,首先检查的就是模块是否启用。
检查模块状态:
zypper repos输出里SLE-Module-Basesystem状态应该是enabled,如果不是,先启用再刷新。
3.2 安装新内核与相关工具包
确认候选版本没问题之后,执行安装命令。我建议直接指定版本号安装,而不是直接zypper up全局升级。指定版本号的好处是只动内核,不碰其他软件包,避免依赖面被扩大。
zypper in -y kernel-default-5.14.21-150500.53.1整体安装时间取决于网络和源的速度,一般在几分钟到十几分钟不等。安装完成后,zypper会自动触发mkinitrd生成新的initrd镜像,同时更新GRUB配置。但这一步不总是可靠的,尤其当你改了/etc/default/grub里的自定义参数时,自动生成的引导配置可能不符合预期。
所以安装完之后,我都会再做一次手动检查:
ls -lt /boot/vmlinuz-* ls -lt /boot/initrd-*确认新内核的vmlinuz和initrd都已经生成,而且文件时间是最近几分钟内的。
提示:SUSE 15里
mkinitrd已经被dracut逻辑接管,你在系统里可能看到/sbin/mkinitrd指向dracut的包装脚本,这是正常的。手动重建initrd的命令是mkinitrd -k vmlinuz-xxx,但正常安装流程会自动处理,不手动干预。
3.3 配置引导加载器
SUSE 15默认使用GRUB2作为引导加载器。新内核装好后,GRUB配置文件里会新增一个menuentry,但我一般会手动重新生成一次配置,确保万无一失。
grub2-mkconfig -o /boot/grub2/grub.cfg生成完之后可以确认新内核的菜单项存在:
grep -E 'menuentry|kernel-default' /boot/grub2/grub.cfg如果这一步报错,最常见的原因有两个:一是/boot/grub2/grub.cfg路径不一样(有些服务器做了软链接指向/boot/grub.cfg),二是os-prober检测其他操作系统时出现异常。路径问题用ls -l /boot/grub2就能看出来,如果是软链接,就按链接指向的目标路径生成。
另外,除非你的机器用了传统BIOS引导而不是UEFI,否则不需要执行grub2-install。SLES 15默认都是UEFI或者管理程序引导,改配置文件就够。
3.4 重启与新内核确认
一切准备就绪后,确认维护窗口内可以重启,然后执行:
reboot重启之后,系统默认会加载GRUB菜单最顶层的启动项,也就是最新安装的内核。登录系统后第一件事就是确认当前运行的内核版本:
uname -r如果输出和你安装的目标版本一致,那就成功了一大半。如果不一致,可能原因有三个:GRUB默认启动项仍是旧内核、刚安装的内核没有出现在菜单里、或者启动过程中直接回退到了旧内核(这是某些配置下出现initrd问题时会发生的)。
接下来还要继续做系统状态检查,这一部分我在第4节展开讲。
4. 升级后的验证与业务回归
4.1 内核版本确认三连查
版本确认这种事,光靠uname -r一条还不够。我习惯性要跑三连查:
uname -r cat /proc/version rpm -q kernel-default第一条看当前实际运行的内核;第二条看内核编译信息里包含的gcc版本和时间戳;第三条看已安装的RPM包版本。正常情况下三条信息指向的内核基线一致。如果rpm -q kernel-default显示的是新版本而uname -r显示旧版本,说明重启后实际加载的还是旧内核,这就必须查GRUB配置和启动日志了。
出现这种情况的时候,先看启动过程里有没有内核对initrd的处理失败:
journalctl -b -0 -k | grep -Ei 'error|fail|panic'重点看有没有类似Failed to load module、No space left on device这类报错。很多时候旧内核还在运行是因为新内核的initrd生成不完整,启动时直接跳过了新内核项。
4.2 系统日志与驱动状态检查
内核升级完之后,驱动状态是重中之重。SLES 15默认仓库里的开源网卡驱动、存储驱动一般都能跟着内核一起编译进模块,但第三方厂商提供的外置驱动就不一定了。
检查当前加载的模块状态:
lsmod dmesg | tail -50 journalctl -b -0 | grep -Ei 'module|driver|firmware' | tail -50lsmod看到的就是系统当前加载的内核模块列表,重点看网卡、磁盘控制器、文件系统相关模块是否在列表里。dmesg里如果出现大量Module version mismatch或者Unknown symbol,基本可以断定是某个第三方模块没跟上新内核,这时候必须在升级前就准备好的模块版本对应方案里找答案。
如果发现固件缺失,检查/lib/firmware里对应文件是否存在。SUSE 15升级内核时一般会同时升级kernel-firmware包,但如果机器上有非标准硬件,可能需要单独确认固件包版本。
4.3 业务应用回归测试清单
系统层面没问题之后,下一步是业务回归。这一步没有统一模板,因为每个环境的业务形态差别太大,但我建议至少把下面这些项目过一遍:
- 基础网络:
ping网关、ssh登录、DNS解析是否正常。 - 文件系统:挂载点是否全部成功挂载,读写权限是否正常。
- 关键服务:数据库、消息队列、中间件进程是否全部处于运行状态。
- 性能基线:重点看CPU负载、内存使用率、IO wait是否和升级前在同一水平。
- 应用日志:抽样查看应用日志中有无异常报错、连接中断记录。
回归测试的时间长短取决于业务复杂度,但前四项至少应该在重启后一小时内完成。如果全部通过,这台机器基本可以确认修复到位,后续再让它稳定运行几天观察一下。
5. 升级过程中的坑与排查技巧
5.1 zypper依赖冲突
SLES 15内核安装过程中最常见的报错就是依赖冲突,表现为zypper提示某个包被另一个包替代,或者需要降级才能满足依赖关系。
我碰到的典型场景是:机器上装了来自第三方源的内核模块包,而该包只匹配旧内核,新内核安装后触发依赖崩溃。zypper会直接拒绝继续执行。
处理思路有两个方向。第一个方向是先检查是否有kernel-*相关的包被锁定:
zypper locks如果有,临时解除锁定后重新安装。第二个方向是对于第三方模块包,先通过zypper rm卸载旧的不兼容版本,完成内核升级后再从厂商渠道获取支持新内核的模块包版本重新安装。
不建议使用zypper up --allow-vendor-change --force-resolution这种强制变更的手段,因为它可能把跟内核无关的包也全部换掉,产生不可控风险。
5.2 第三方内核模块失效
内核从旧版升到新版,API和ABI都可能发生变化,第三方内核模块如果还是按旧内核编译的,就会在启动时加载失败。
最典型的表现是启动报Module XXX not found、驱动加载失败,网络或存储功能异常。排查方法是用modinfo查询模块属性:
modinfo /lib/modules/$(uname -r)/kernel/drivers/xxx/xxx.ko 2>/dev/null如果文件本身不存在,说明模块原厂包的安装脚本没有针对新内核重新编译,需要回到厂商官网下载新版本。如果是开源驱动,确认kernel-devel和kernel-source包已安装到新版本,再用make重新编译外部模块。
所以在升级前,我强烈建议先跟设备厂商确认硬件驱动对新内核的支持情况。如果是老设备,厂商还没有适配新版内核,那就不能盲目升级,需要在漏洞修复需求和业务稳定性之间做一个权衡决策。
5.3 回滚:怎么启动到旧内核
真遇到新内核起不来的情况,回滚操作并不复杂,但前提是你提前做好了备份。重启时进入GRUB界面,选择Advanced options for SLES(实际菜单名可能略有差异),在里面会看到旧版本内核的menuentry,选它启动即可。
进系统之后如果希望把默认启动项重新指回旧内核,可以用以下命令:
grub2-set-default "SLES 15 SP5, with Linux 5.14.21-150000.58.1" grub2-mkconfig -o /boot/grub2/grub.cfg注意grub2-set-default参数必须和GRUB菜单里的menuentry名称完全一致,否则不生效。建议先执行grep 'menuentry' /boot/grub2/grub.cfg把菜单项原样复制出来,再填到命令里。
回滚成功之后,新内核的RPM包还在系统里,之后可以慢慢排查问题,找到原因后再次尝试升级。
5.4 常见问题速查表
把这次升级过程中以及以往经验里典型的问题整理成一个速查表,方便遇到类似情况时直接对照排查。
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| zypper找不到目标内核包 | 模块仓库未启用、订阅过期 | 启用Basesystem模块,刷新仓库 |
| /boot空间不足 | 旧内核和initrd堆满分区 | 清理旧内核包、zypper clean |
| 重启后内核版本没变化 | GRUB默认启动项未更新 | 手动grub2-mkconfig,grub2-set-default |
| 新内核启动后网卡无法工作 | 第三方网卡驱动未适配 | 重装/升级第三方驱动包 |
| 开机报Unknown symbol | 模块版本不匹配 | 重新编译或卸载不兼容模块 |
| initrd生成失败 | dracut依赖缺失 | mkinitrd手动重建,检查fstab |
| 登录后磁盘挂载丢失 | 新内核下udev规则变化 | 检查/etc/fstab,重新挂载并保存配置 |
| 系统升级后应用连接数下降 | 内核网络参数被重置 | 按需重新设置sysctl参数并持久化 |
这张表不用死记,关键是出现问题要先判断问题层级:引导层问题看GRUB,内核加载问题看journalctl,模块问题看lsmod和dmesg,业务层问题看应用日志。一层一层往下查,比乱猜要快得多。
6. 一点个人实操心得
内核升级这种操作,本质上是一道“风险评估题”而不只是“命令执行题”。命令翻来覆去就那么几条,但真正决定一次升级成败的是升级前的判断和预案:你有没有确认你的SP版本对应的修复内核版本,你有没有留好/boot空间,你有没有提前问清楚业务方能不能接受重启,你有没有把旧内核启动项保留干净。
我个人的经验是,每次内核升级前都先把回滚步骤打印出来放在手边,哪怕这个漏洞再危险,也不要省略这一步。因为漏洞修复的前提是系统还能正常用,如果为了修漏洞直接把系统搞趴下,那修复本身就没有意义了。
另外一个小技巧:升级完成之后不要马上关掉维护窗口,至少让系统稳定运行一两个小时,确认日志里没有持续报错再把窗口关闭。内核漏洞修复讲究的是“稳”,你已经把漏洞堵上了,剩下的就是给系统一点时间证明自己真的稳得住。