在隔离网络中真正构建 DKMS 模块
本实验在真正的 VM 中运行
这是一台 Ubuntu 24.04 VM。构建内核模块并加载需要内核权限,所以这里用的是 VM,而不是 Pod。VM 上已装好 dkms 和构建工具,示例模块的源码位于 /opt/airgap-hello/(airgap_hello.c、Makefile)。NVIDIA 驱动不能再分发,所以用这个 20 行的 GPL 模块走一遍同样的路径(导入头文件 → DKMS 构建 → modprobe)。VM 起初可以访问互联网(80/443)——到第 2 步为止是联网状态下的导入准备,第 3 步会封锁对外发送。评分由从外部连入 VM 的 8899 端口的代理完成,所以如果切断已建立的连接,评分就会中断。
目标
导入与正在运行的内核完全一致的头文件并离线安装,用 DKMS 构建、安装并加载模块,然后留存头文件不匹配时的错误输出作为证据。
为什么重要
隔离网络中安装驱动时,最容易出问题的不是驱动,而是内核头文件。DKMS 用正在运行的内核的构建树(/lib/modules/<커널>/build)来构建模块,导入的头文件哪怕只差一个版本,构建也连开始都做不到。而且内核更新一到,同样的事情会再次发生。本实验在真正的内核上走一遍这条路径。
预计需要 50 分钟。会话结束后 VM 会消失,想保留的文件请在结束前另行保存。
步骤
- 在
/root/dkms/kernel.txt中写入running=(正在运行的内核)、headers_pkg=(该内核的头文件软件包名称)和build_dir=(yes 或 no——当前/lib/modules/<커널>/build是否存在)。 - 在联网期间,不安装该内核的头文件软件包以及它所依赖的头文件软件包,只把它们下载到
/root/dkms/bundle/,并在该目录中生成/root/dkms/bundle/SHA256SUMS。 - 用重复运行也只留下一套规则的
/root/dkms/egress.sh封锁对外发送(链为AIRGAP-EGRESS,保留回环和已建立的连接)。 - 确认哈希后,用导入包中的 .deb 离线安装头文件,使
/lib/modules/<커널>/build出现。 - 把模块源码放到
/usr/src/airgap-hello-1.0/中,编写/usr/src/airgap-hello-1.0/dkms.conf,并 add 到 DKMS。 - 用正在运行的内核执行 build 和 install,使
dkms status显示 installed。 - 用
site=lab加载模块,并在/root/dkms/load.txt中写入vermagic=、signer=(没有则为 none)和sb_state=(mokutil --sb-state 的结果,一行)。 - 用没有头文件的内核版本
6.8.0-9999-generic去构建,把 DKMS 给出的错误保存到/root/dkms/mismatch.txt。
参考
- 正在运行的内核:
uname -r· 头文件软件包名称:linux-headers-$(uname -r) - 只下载:
apt-get download <패키지...>· 它所依赖的软件包:apt-cache depends <패키지>或dpkg-deb -f <deb> Depends - DKMS:
dkms add <소스디렉터리>·dkms build -m <이름> -v <버전> [-k <커널>]·dkms install ...·dkms status - 模块信息:
modinfo -F vermagic <모듈>、modinfo -F signer <모듈>· 日志:dmesg | grep airgap_hello - 常见错误 1:下载
linux-headers-generic。它是指向最新内核头文件的元软件包,所以可能与正在运行的内核不同。 - 常见错误 2:只下载一个头文件软件包。内核头文件分为两个软件包(按 flavor 区分的和公共的)。
正在运行的内核与所需的头文件
在 /root/dkms/kernel.txt 中写入 running=、headers_pkg=、build_dir= 三行。
DKMS 使用的构建树是 /lib/modules/<内核>/build。Ubuntu 的头文件软件包名称,就是把正在运行的内核字符串直接接在后面。请确认当前是否存在该目录。
不安装只下载头文件并生成哈希清单
把正在运行的内核的头文件软件包以及它所依赖的头文件软件包下载到 /root/dkms/bundle/,并生成 /root/dkms/bundle/SHA256SUMS。
apt-get download 只下载 .deb,不安装。查看已下载头文件软件包的 Depends,就能看到同时需要的公共头文件软件包。请在导入包目录内用相对路径生成哈希清单。
封锁对外发送,构造隔离网络
用重复运行也只留下一套规则的 /root/dkms/egress.sh 封锁对外发送。
新建一条链,在 OUTPUT 的最前面把流量转到这条链,先放行回环、已建立的连接和评分代理,再封锁其余流量。此后 apt 就无法从外部下载了。
离线安装导入的头文件
确认哈希后,用导入包中的 .deb 安装头文件,使 /lib/modules/<커널>/build 出现。
先在导入包目录中用哈希清单检查。把两个 .deb 一起交给 dpkg,它会一次性解决二者互相的依赖。安装后请看 build 是指向哪里的链接。
编写 dkms.conf 并 add
把模块源码放到 /usr/src/airgap-hello-1.0/ 中,编写 /usr/src/airgap-hello-1.0/dkms.conf,并 add 到 DKMS。
DKMS 在 /usr/src/<名称>-<版本> 中查找源码。dkms.conf 中要写软件包名称和版本、将生成的模块名称(不带 .ko)、安装位置,以及新内核装入时是否自动重新构建。
用正在运行的内核执行 build 和 install
用正在运行的内核执行 build 和 install,使 dkms status 显示 installed。
build 在 /var/lib/dkms 下构建模块,install 把它移到 /lib/modules/<内核>/ 下并运行 depmod。如果失败,输出中会给出 make.log 的路径。
加载并记录 vermagic、签名和 Secure Boot
用 site=lab 加载模块,并在 /root/dkms/load.txt 中写入 vermagic=、signer= 和 sb_state=。
modprobe 会从 depmod 生成的列表中找到模块并连同依赖一起加载,参数按 名称=值 的形式传入。用 modinfo -F 可以只取出一个字段。Secure Boot 状态由 mokutil 给出(如果 VM 没有 EFI 变量,它会如实说明)。
用没有头文件的内核去构建
用内核版本 6.8.0-9999-generic 去构建,把 DKMS 给出的错误保存到 /root/dkms/mismatch.txt。
dkms build 有一个选择目标内核的选项。如果该内核的构建树不存在,DKMS 会在开始编译之前就停下来。请把输出(包括标准错误)原样保存。