禁止自动加载内核模块

备注

我在 安装NVIDIA Linux驱动 实践中乌龙,导致反复排查内核模块自动加载问题。内核模块自动加载的防范有多种方法,本文试图按照最新Linux操作系统进行整理,为后续相关运维打基础。

How do I prevent a kernel module from loading automatically? Red Hat官方知识库文档英文版写得非常详尽,但是中文版只是一个非常简略的早期版本,所以本文会详细记录一下不同版本RedHat的配置方法,但实践在Fedora 35上进行,相当于Red Hat Enterprise Linux 8/9

很多时候,我们需要禁止内核自动加载模块,例如 安装NVIDIA Linux驱动 ,就需要屏蔽掉冲突的开源驱动 nouveau 内核模块。此外有些硬件设备我们并不希望启用,可能也需要通过禁止内核特定驱动模块来禁用设备。

说明

  • 为防止在引导期间加载内核模块,必须将模块名称添加到 modprobe 程序的配置文件,该配置文件必须位于 /etc/modprobe.d 目录下

  • 在屏蔽内核模块之前,必须确保被屏蔽模块没有配置在 /etc/modprobe.conf , /etc/modprobe.d/* 以及 /etc/rc.modules/etc/sysconfig/modules/* 这些配置文件中加载 (避免冲突)

  • RHEL 5、6、7、8 和 9 的有一些共享配置步骤,但是每个版本也有一些特定步骤,需要完成对应版本的所有配置才能确保不加载需要屏蔽的内核模块。

RHEL 5, 6, 7, 8 和 9共有的初始配置步骤

由于内核模块可以直接加载,也可以作为另一个模块的依赖项加载,或者由于启动过程中加载: 所以我们必须采用多种步骤来防止模块被加载( 有点小复杂 ):

  • [步骤1]如果运行的系统已经加载了内核模块,我们需要先卸载内核模块:

    modprobe -r module_name
    
  • [步骤2]为防止模块被加载,需要将黑名单添加于 /etc/modprobe.d/ 目录下的配置文件,例如名为 local-dontload.conf (名字不重要,主要是文件中配置项 blacklist ):

    echo "blacklist module_name" >> /etc/modprobe.d/local-dontload.conf
    
  • [步骤3]在 /etc/modprobe.d/local-dontload.conf 配置中添加 install 行使得模块只会运行 /bin/false 而不是安装模块:

    echo "install module_name /bin/false" >> /etc/modprobe.d/local-dontload.conf
    

RHEL 8 和 9特定步骤

  • [步骤4]备份 initramfs

    cp /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.$(date +%m-%d-%H%M%S).bak
    
  • [步骤5]如果内核模块是 initramfs 的一部分,可以通过以下命令检查:

    lsinitrd /boot/initramfs-$(uname -r).img|grep module-name.ko
    

备注

lsinitrd 工具位于 dracut-core 软件包,在Ubuntu上需要单独安装

确定内核模块已经包含在 initramfs 中,则需要重新build initramfs 的ramdisk镜像,避免包含模块:

dracut --omit-drivers module_name -f

为了在今后内核升级和initramfs重建时持续屏蔽掉指定内核模块,可以将配置添加到 dracut 的配置中:

MODNAME="module_name"; echo "omit_dracutmodules+=\"$MODNAME\"" >> /etc/dracut.conf.d/omit-$MODNAME.conf

备注

在 Ubuntu 系统中,提供了一个 update-initramfs 工具,可以读取 /etc/modprobe.d 目录下配置,就能将 blacklist 的屏蔽内核模块去除:

update-initramfs -u -k $(uname -r)
  • [步骤6]获取当前内核命令行参数:

    grubby --info DEFAULT
    grubby --info ALL
    
  • [步骤7]在内核参数上添加 module_name.blacklist=1 rd.driver.blacklist=module_name ,这样RHEL8 和RHEL9的 grubby 工具就会修改内核参数:

    grubby --args  "<module_name.blacklist=1 rd.driver.blacklist=module_name>"  --update-kernel ALL
    

这个内核参数修改我建议直接修改 /etc/default/grub 配置,然后执行 update-grub 工具来修订

  • [步骤8]备份kdump initramfs:

    cp /boot/initramfs-$(uname -r)kdump.img /boot/initramfs-$(uname -r)kdump.img.$(date +%m-%d-%H%M%S).bak
    
  • [步骤9]将kdump initramfs也忽略模块,配置方法也是添加内核参数 rd.driver.blacklist=module_name

    sed -i '/^KDUMP_COMMANDLINE_APPEND=/s/"$/ rd.driver.blacklist=module_name"/' /etc/sysconfig/kdump
    
  • [步骤10]重启kdump服务:

    kdumpctl restart
    
  • [步骤11]重新build kdump initial ramdisk镜像:

    mkdumprd -f /boot/initramfs-$(uname -r)kdump.img
    
  • [步骤12] 重启系统使修改生效:

    reboot
    

RHEL 7特定步骤

  • [步骤4] 备份initramfs:

    cp /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.$(date +%m-%d-%H%M%S).bak
    
  • [步骤5] 如果内核模块是initramfs的组成部分,也需要使用 dracut 来删除掉内核模块:

    dracut --omit-drivers module_name -f
    

并且建议将配置持久化到内核更新和initramfs重建流程:

MODNAME="module_name"; echo "omit_dracutmodules+=\"$MODNAME\"" >> /etc/dracut.conf.d/omit-$MODNAME.conf
  • [步骤6]修订内核参数:

    sed -i '/^GRUB_CMDLINE_LINUX=/s/"$/ module_name.blacklist=1 rd.driver.blacklist=module_name"/' /etc/default/grub
    
  • [步骤7]重新安装grub2:

    grub2-mkconfig -o /boot/grub2/grub.cfg
    

如果系统使用UEFI,则输出路径是 `` /boot/efi/EFI/redhat/grub.cfg``

grub2-mkconfig -o /boot/grub2/grub.cfg
  • [步骤8]备份kdump initramfs:

    cp /boot/initramfs-$(uname -r)kdump.img /boot/initramfs-$(uname -r)kdump.img.$(date +%m-%d-%H%M%S).bak
    
  • [步骤9]添加 rd.driver.blacklist=module_name/etc/sysconfig/kdump 的配置项 KDUMP_COMMANDLINE_APPEND

    sed -i '/^KDUMP_COMMANDLINE_APPEND=/s/"$/ rd.driver.blacklist=module_name"/' /etc/sysconfig/kdump
    
  • [步骤10]重启kdump服务使修改作用到kdump的initrd:

    kdumpctl restart
    
  • [步骤11]重建kdump initial ramdisk镜像:

    mkdumprd -f /boot/initramfs-$(uname -r)kdump.img
    
  • [步骤12]重启系统生效:

    reboot
    

参考