modulejail: 通过将所有当前未使用的模块加入黑名单,主动缩小 Linux 主机的内核模块攻击面

Lobsters Hottest 工具

摘要

ModuleJail 是一个 POSIX shell 脚本,通过将所有当前未使用的模块加入黑名单来缩小 Linux 主机的内核模块攻击面,帮助系统管理员降低即将到来的内核模块漏洞风险。

<p><a href="https://lobste.rs/s/xugaho/modulejail_proactively_shrink_linux">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/20 16:29

jnuyens/modulejail

源代码:https://github.com/jnuyens/modulejail 一个单文件 POSIX shell 脚本,通过编写一个 modprobe.d 黑名单,将当前未使用的所有内核模块(减去内置基线及可选的系统管理员白名单)列入黑名单,从而缩小 Linux 主机的内核模块攻击面。无需守护进程、无需更改 initramfs、工具内部无 AI。一个脚本,一次运行,一个黑名单文件。

为什么?

AI 辅助安全扫描将像十年前大规模模糊测试对用户空间代码所做的那样,对 Linux 内核产生影响,只是速度更快、规模更大得多。内核模块中许多潜在的权限提升漏洞将在未来数周和数月内接连暴露。长期来看,这对内核安全是一次重大胜利:每次披露都会关闭攻击者原本可以暗中利用的大门。短期来看,这对系统管理员是一场噩梦。每发布一次公开披露,就要在数千台主机上争分夺秒地与补丁周期、供应商反向移植和重启赛跑。

ModuleJail 并不试图修复内核漏洞,它也做不到。它只做系统管理员今天就能在任何主机上数秒内完成的一件事:缩小攻击面,使得下一个被披露的漏洞更可能落在主机甚至未加载的模块上。典型的 Linux 主机搭载数千个内核模块,但只使用其中数百个。ModuleJail 将剩下的全部列入黑名单。下一个 CVE 发生在未被使用的 90% 模块上,在该主机上就变得无关紧要,而集群运维人员就能按自己的节奏安排补丁,而不是凌晨三点紧急传呼。

这是一个故意设计得很无趣的工具。内部没有 AI、没有守护进程、没有持续监控、没有风险评分、没有 CVE 数据库查询。只有一个 shell 脚本,在稳态主机上运行一次,生成 /etc/modprobe.d/modulejail-blacklist.conf,将数千个未使用的模块(针对你的系统)列入黑名单。

快速开始

curl -fsSL https://raw.githubusercontent.com/jnuyens/modulejail/v1.2.3/modulejail | sudo sh

警告:方便但不安全。 这会从网络向 root shell 输入未经验证的字节。下面更安全的替代方案是推荐路径。

在笔记本电脑或工作站上?添加 -p desktop

默认配置是 conservative(服务器和虚拟机)。它不包含 WiFi、蓝牙、音频或视频驱动在内置基线中,因此如果这些驱动在运行时恰好未加载(WiFi 断开、蓝牙关闭、耳机未插入等),它们可能会被列入黑名单并在下次启动后不可用。desktop 配置会无条件地将它们保留在白名单中。

curl -fsSL https://raw.githubusercontent.com/jnuyens/modulejail/v1.2.3/modulejail | sudo sh -s -- -p desktop

详见下方的配置

默认情况下,脚本会将黑名单写入 /etc/modprobe.d/modulejail-blacklist.conf。要使用不同路径:

curl -fsSL https://raw.githubusercontent.com/jnuyens/modulejail/v1.2.3/modulejail | sudo sh -s -- -o /etc/modprobe.d/site-blacklist.conf

更安全的替代方案

下载、检查、然后运行:

curl -fsSL https://raw.githubusercontent.com/jnuyens/modulejail/v1.2.3/modulejail -o /tmp/modulejail
less /tmp/modulejail
sudo sh /tmp/modulejail

这是任何生产部署的推荐路径。脚本是纯 POSIX shell,检查时间不超过十分钟。

原生包 (.deb / .rpm)

对于 Debian/Ubuntu 和 RHEL/Fedora/Rocky 主机,预构建的包随 GitHub 发布页面提供:

# Debian / Ubuntu:
curl -fsSLO https://github.com/jnuyens/modulejail/releases/download/v1.2.3/modulejail_1.2.3_all.deb
sudo dpkg -i modulejail_1.2.3_all.deb
# RHEL / Fedora / Rocky:
curl -fsSLO https://github.com/jnuyens/modulejail/releases/download/v1.2.3/modulejail-1.2.3-1.noarch.rpm
sudo rpm -i modulejail-1.2.3-1.noarch.rpm

这两个包都会安装 /usr/bin/modulejail、位于 /usr/share/man/man8/modulejail(8) 手册页,以及位于 /usr/share/doc/modulejail/ 的 README 和许可证。它们依赖 coreutilsfindutilsawk/gawk(均为标准工具),并推荐 curlwget,以便运行后可选的更新检查能够访问 GitHub。

安装后,man 8 modulejail 显示完整的参考信息:选项、配置、安全模型、幂等性、退出码、环境和示例。

要从源码检出本地重建包:

./packaging/build.sh          # 构建本机工具链支持的所有格式
./packaging/build.sh --deb    # 仅 .deb(需要 dpkg-deb)
./packaging/build.sh --rpm    # 仅 .rpm(需要 rpmbuild)

输出位于 packaging/dist/。缺少相应工具链的主机会优雅地跳过。

ModuleJail 是什么

ModuleJail 对当前加载的模块集(/proc/modules)进行快照,并计算其与完整模块树(/lib/modules/$(uname -r))的补集。补集中的每个模块,减去内置的必需模块基线以及可选的系统管理员提供的白名单,都会以 install <module> 指令的形式输出到一个 modprobe.d 兼容的黑名单文件中。

从 v1.2 开始,指令体可以是 /bin/sh -c 'logger -t modulejail "blocked: <module>"; exit 0'(当 /usr/bin/logger 可用时默认使用,这样被阻止的尝试会产生 syslog 日志),或者 /bin/true(使用 --no-syslog-logging 时,或静默回退,或 logger 不存在时)。参见下方的查看被阻止的模块尝试部分。

该工具面向需要针对 AI 辅助的内核权限提升漏洞浪潮加固大量服务器的 Linux 集群运维人员。每多加载一个模块,就多了一份潜在的攻击面,可能被下一个披露的 CVE 利用。ModuleJail 的模型很简单:如果稳态主机今天没有加载它,就把它列入黑名单。

该脚本可在 Debian/Ubuntu、RHEL/Rocky、Arch、Alpine 和 SUSE 系列之间移植。除 awkcommfindsha256sum 以及标准 coreutils(包括 busybox 中的)之外,没有其他运行时依赖。

安全模型

不变量是:当前已加载的任何模块都被假定为主机正常运行所必需,并予以保留。 ModuleJail 不猜测;它在运行时读取 /proc/modules,并将该精确集合视为保留列表。

这意味着运维人员的责任是在主机处于已知良好的稳态配置时运行 ModuleJail:在所有服务启动、所有内核驱动程序加载、所有文件系统挂载之后。在不完整或状态变化中的系统上运行它,可能会将一个偶尔需要的模块列入黑名单。

生成的文件放置于 /etc/modprobe.d/ 下。要还原,只需删除该文件(无需重启——参见还原部分)。

内置基线确保无论运行哪个配置,核心文件系统、存储控制器和基本网络模块都不会被列入黑名单。

明确限制

  • 没有 initramfs 处理。 内置于 initramfs 中的模块不在范围内。目标是已加载模块的攻击面;内置模块不是相关的攻击向量。
  • 没有还原工具。 还原路径是“删除生成的文件”(无需重启;modprobe 在加载时查询黑名单,因此删除文件会立即生效)。由系统管理员自律替代工具护栏。
  • 没有守护进程或持续监控。 一次性脚本。
  • 工具内部无 AI。 AI 是威胁模型背景,而非功能。
  • v1 中没有按发行版打包。 curl 单行命令和克隆的仓库是分发渠道。
  • 没有模块风险评分。 模型是“未使用即列入黑名单”,而非“有漏洞即列入黑名单”。
  • 无需重新编译内核。 仅运行时黑名单。

配置

ModuleJail 内置三个基线配置。所选配置决定了无论加载状态如何,始终保留哪些模块。

# 通过 -p 选择配置(默认:conservative)
sudo sh modulejail -p conservative
sudo sh modulejail -p minimal
sudo sh modulejail -p desktop

配置说明(来自 --help):

minimal       仅包含核心文件系统 + 必需内核模块
conservative  包含 minimal + 常见服务器/虚拟机驱动(默认)
desktop       包含 conservative + WiFi、蓝牙、音频、视频驱动

conservative 适用于虚拟化或裸机服务器 Linux。desktop 适用于笔记本电脑和工作站,需保留 WiFi、蓝牙、音频和视频驱动。minimal 适用于完全控制加载哪些驱动的环境,并希望基线尽可能小。

系统管理员白名单

脚本顶部附近的站点本地 WHITELIST 变量以空格分隔的模块名称列表形式保存,这些模块在所选基线之外始终保留。默认情况下为空。要使用它,打开脚本并找到 === SYSADMIN WHITELIST === 部分:

# === SYSADMIN WHITELIST ===
# Site-local additions to the keep-set, in addition to the selected baseline
# profile. Modules listed here will never appear in the generated blacklist.
#
# Format: space-separated module names in canonical underscore form
# (the pipeline normalizes - to _, so either form works).
# Default: empty.
#
# Example (uncomment and adapt):
# WHITELIST='nft_compat xt_owner'
WHITELIST=''
# === END SYSADMIN WHITELIST ===

编辑 WHITELIST='',添加你站点特定的模块。=== 横幅锚点专为 Ansible 模板插入(lineinfileblockinfile)设计。

站点本地白名单文件

从 v1.2 开始,ModuleJail 从外部文件读取站点本地模块。当你不想(或不能)就地编辑脚本时,这是首选路径——例如,你通过 .deb / .rpm / curl | sh 安装 ModuleJail,而你的站点本地添加内容会在下次重新安装时丢失。

默认路径是 /etc/modulejail/whitelist.conf。如果该文件存在,ModuleJail 会自动检测并在 stderr 上打印一条 info: 行,以便选择不被忽略:

modulejail: info: using default whitelist file /etc/modulejail/whitelist.conf (--no-whitelist-file to opt out)

要在某次运行中跳过默认文件(例如在恢复期间),请传递 --no-whitelist-file。要使用不同位置,请传递 --whitelist-file PATH

文件格式:

# /etc/modulejail/whitelist.conf
# 每行一个模块。允许空行和以'#'开头的注释。
# 名称可以使用短横线或下划线形式("nft-compat" 或 "nft_compat")——管道会将 - 规范化为 _。
# 文件模式不得为组可写或全局可写
# (否则 ModuleJail 将拒绝运行)。
nft_compat
xt_owner
zfs

三种调用方式:

# 1. 默认位置(推荐用于生产部署):
sudo install -d -m 0755 /etc/modulejail
sudo install -m 0644 my-whitelist /etc/modulejail/whitelist.conf
sudo modulejail   # 自动检测 /etc/modulejail/whitelist.conf

# 2. 显式的非默认路径(覆盖或使用站点本地 NFS 挂载):
sudo modulejail --whitelist-file /etc/default/modulejail-whitelist

# 3. 跳过默认文件一次(强制“无站点本地添加”):
sudo modulejail --no-whitelist-file

该文件会附加到脚本内部的 WHITELIST 中;两者是相加的。一直编辑脚本内部 WHITELIST(v1.0 路径)的运维人员可以保持该编辑不变;文件是叠加在其上的无副作用覆盖层。

ModuleJail 对该文件强制设置两个安全门:

  1. 文件模式不得为组可写或全局可写。 这与 sshd 对 authorized_keys、sudo 对 sudoers 施加的强化措施相同。如果文件是 g+wo+w,ModuleJail 拒绝运行并打印 chmod go-w PATH 作为提示。退出码为 77EX_NOPERM)。理由:该文件中的模块名称会进入生成的 modprobe.d 指令,因此拥有共享系统管理员组写入权限的攻击者可能会注入内核稍后运行的 install 段落。
  2. 每行必须匹配 [a-zA-Z0-9_-]+ 注释(#)和空行会被静默跳过;其他所有内容必须是纯模块名称。任何格式错误的行都会在 stderr 上被拒绝,并引用文件路径、行号和违规内容。退出码为 65EX_DATAERR)。

查看被阻止的模块尝试

从 v1.2 开始,当运行 ModuleJail 的主机上 /usr/bin/logger 可执行(且未设置 --no-syslog-logging)时,生成的 install 行会调用 logger -t modulejail "blocked: <module>",因此之后尝试的 modprobe <module> 会产生一个标记为 modulejail 的 syslog 条目:

# systemd 主机(journald):
sudo journalctl -t modulejail --since '1 hour ago'
# 传统 syslog 主机:
sudo grep modulejail /var/log/syslog

生成文件的头部会注释当前使用的 install 行形式。查找:

# install-line: /bin/sh + logger (syslog tag: modulejail)

要选择退出并恢复 v1.1.4 的 /bin/true install 行主体(适用于字节一致性回归合约、没有 logger 的主机,或 minimal/initramfs 构建),请传递 --no-syslog-logging

sudo modulejail --no-syslog-logging

头部注释随后变为:

# install-line: /bin/true (silent, --no-syslog-logging or logger absent)

如果主机上 /usr/bin/logger 不存在且未设置 --no-syslog-logging,ModuleJail 会静默回退到 /bin/true 形式(匹配 v1.1.4 在最小主机上的行为)。不会发出 stderr 警告;头部注释是唯一可见的线索。

黑名单的范围(它阻止什么,不阻止什么)

modprobe.d 黑名单会阻止自动模块加载:硬件热插拔时的 udev 事件、modprobe foo 时的依赖解析、通过别名系统的自动加载模块。它阻止(设计如此):

  • insmod /path/to/module.ko——insmod 完全绕过 modprobe,从不读取 modprobe.d/。有意的 root 用户总是可以直接插入模块。
  • modprobe --ignore-install <module>——modprobe 的显式逃生舱。用户选择不使用 ModuleJail 所依赖的 install 行间接机制。

两者都是内核模块加载器中有意的逃生舱。ModuleJail 是一个默认安全的策略层:它移除了自动加载攻击面(udev 热插拔 + 依赖解析),这正是无特权或远程攻击者可以利用的。它不能——也不可能——阻止一个有意的 root 用户加载任何他们想要的东西。将黑名单视为“锁前门”的工具,而不是“锁保险柜”的工具。

退出码

退出码遵循 sysexits.h 惯例(参见 man 3 sysexits)。集群自动化工具可以干净地使用 case $?

代码含义
0成功
64命令行参数错误(错误标志、缺少值、未知配置)
65--whitelist-file 中存在无效数据(模块名称格式错误)
66缺少必需的内核输入(/proc/modules/lib/modules/
70安全防护触发(空黑名单或超过 99% 的模块被列入黑名单)
71操作系统级错误(mktemp 工作目录,或 /lib/modules 上的 find 错误)
73无法创建输出路径(符号链接/目录/尾部斜杠,或 mktemp 失败)
77目标目录不可写(尝试 sudo,或使用 -o <path>

幂等性约定

在未变更的主机上连续两次运行会产生字节完全相同的输出文件。生成的黑名单头部携带一个 sha256 运行指纹,而不是挂钟时间戳,该指纹通过对规范输入进行哈希计算得出:排序后的已加载模块集、排序后的基线集、排序后的白名单、配置名称和内核版本。由于指纹是输入的确定性函数,相同的输入会产生相同的指纹,从而产生相同的输出文件。

# fingerprint: sha256:e284ee9741eb544adf1af6c0fffc162dedd6029191673237a8155cd497908686

集群运维人员可以使用指纹来跨机器关联“主机在加固时有什么”:两台具有相同指纹的主机在 ModuleJail 运行时具有相同的加载集、基线、白名单、配置和内核版本。没有挂钟漂移,配置管理系统中没有虚假的差异。

更新检查

成功运行后

相似文章

近期内核漏洞、攻击面减少,以IPSEC为例

Lobsters Hottest

Hanno Böck 讨论了影响 ESP (IPSEC) 模块的近期内核漏洞,并建议禁用与 IPSEC 相关的内核配置选项以减少攻击面,突出显示了默认情况下加载了许多未使用的内核模块。

ASGuard:激活缩放防护以缓解针对性越狱攻击

Hugging Face Daily Papers

ASGuard是一种基于机制的防御框架,通过电路分析识别脆弱的注意力头,并应用有针对性的激活缩放和微调,在保持模型能力的同时提高拒绝行为的鲁棒性,从而缓解针对LLM的越狱攻击。

killswitch:一种基于函数粒度的短路熔断原语

Lobsters Hottest

# 新 Linux 内核补丁提议引入"killswitch"原语,可即时禁用存在漏洞的内核函数 一项新的 Linux 内核补丁提议引入一种"killswitch"原语,允许管理员立即禁用存在漏洞的内核函数(例如 `af_alg_sendmsg`),使其返回 `-EPERM`,从而为安全问题提供快速的临时缓解措施,无需重启系统或重新编译内核。