Linux dm-verity 如何校验只读根文件系统
来源:17golang原创
时间:2026-10-09 17:00:11 190浏览 收藏
Linux dm-verity 校验只读根文件系统的关键,不是给已经挂载的目录加一个普通校验命令,而是先为根文件系统生成 Merkle 哈希树,再用可信的 root hash 创建一个只读 device-mapper 映射。应用访问映射设备时,内核按块验证数据;校验失败就让 I/O 失败或按配置进入重启、panic 等处理。
官方资料:https://docs.kernel.org/admin-guide/device-mapper/verity.html
- 数据设备保存根文件系统,哈希设备保存验证树,两者可以是分区,也可以是镜像文件。
veritysetup verify是不创建内核映射的用户态检查;veritysetup open才会创建可挂载的只读映射。- dm-verity 证明的是完整性,不自动证明 root hash 的来源;生产启动链还要保护或签名 root hash。
先把数据区、哈希区和可信根哈希分开
dm-verity 的输入至少有三部分:待保护的 data device、保存哈希树的 hash device,以及需要信任的 root hash。哈希树的叶节点对应数据块,父节点继续哈希子节点,最终收敛到 root hash。内核文档建议新设备使用当前的格式 1;下面命令中的设备路径只是结构示例,不能直接覆盖生产盘。
先由 format 计算验证数据,再把输出的 root hash 放进受启动链保护的文件。生产环境不要把 root hash 当成和数据盘一样可被随意替换的普通配置。
# 为数据区生成哈希树;示例路径请替换为测试镜像或专用分区。 veritysetup format /dev/mapper/root-data /dev/mapper/root-hash # 保存 format 返回的十六进制 root hash,启动链应从可信来源读取它。 printf '%s\n' 'ROOT_HASH_FROM_FORMAT' > /etc/dm-verity/root.hash

哈希设备也可以位于同一块设备的数据区之后,但要明确哈希起始偏移,并保证哈希区域不被映射为可写数据。把数据区和哈希区分开通常更容易在启动介质、镜像制作和故障排查中管理。
用 verify 先确认根哈希能覆盖数据
写入启动流程前,可以使用 verify 做一次用户态校验。它不会创建内核 device-mapper 设备,适合检查镜像制作结果、复制后的哈希区以及保存的 root hash 是否匹配。
# 用户态扫描数据区与哈希区,不创建 root-ro 映射。
veritysetup verify /dev/mapper/root-data /dev/mapper/root-hash \
--root-hash-file /etc/dm-verity/root.hash
# 命令返回非零时先停止启动链,检查设备、哈希文件和制作时的参数。
test $? -eq 0 || { echo 'dm-verity verify failed' && exit 1; }
如果使用 --no-superblock,格式化和验证时的块大小、算法、偏移等参数必须保持一致;不要只复制一个 root hash 就认为另一套哈希树可以复用。哈希树本身发生变化时,旧 root hash 也不再匹配。
| 对象 | 作用 | 常见错误 |
|---|---|---|
| data device | 保存只读根文件系统内容 | 格式化后又修改内容,导致叶哈希失配 |
| hash device | 保存分层哈希树 | 复制不完整、偏移或块大小不一致 |
| root hash | 作为验证树的可信锚点 | 只校验格式,却没有保护 root hash 来源 |
创建只读映射,再交给根文件系统挂载
校验通过后,用 open 创建映射。映射名 root-ro 只是示例,真正的根文件系统启动阶段通常在 initramfs 中完成这一步,然后把映射设备作为新的根设备交给后续挂载逻辑。
# 创建只读 dm-verity 映射;root hash 从受保护文件读取。 veritysetup open /dev/mapper/root-data root-ro /dev/mapper/root-hash \ --root-hash-file /etc/dm-verity/root.hash # 测试阶段把映射挂载为只读目录,确认文件系统类型与内容可读。 mount -o ro /dev/mapper/root-ro /mnt/root

dm-verity 映射本身是只读目标,所以它解决的是“读取到的块是否仍与可信树一致”。它不负责把根文件系统变成可写层;需要写入时,应在上层设计 overlay、独立数据分区或其他明确的写入策略,不要尝试直接写入 /dev/mapper/root-ro。
检查状态并设置损坏处理边界
映射创建后,可以读取状态和磁盘上的 verity 参数。内核文档中的状态使用 V 表示到目前为止校验有效,使用 C 表示已经发现损坏;命令返回非零时,还要结合内核日志和 I/O 错误定位数据区、哈希区或存储介质。
# 读取映射状态;确认名称与 initramfs 中创建的名称一致。 veritysetup status root-ro # 查看哈希设备中保存的 verity 超级块参数,便于对照格式化配置。 veritysetup dump /dev/mapper/root-hash # 卸载测试映射,避免把打开的设备留在维护流程中。 umount /mnt/root veritysetup close root-ro
默认行为是让损坏数据的 I/O 失败。--ignore-corruption 会记录问题但继续读,--restart-on-corruption 或 --panic-on-corruption 则会改变故障处置,后两者必须避免无限重启或 panic 循环。性能受限的系统还可能使用 --check-at-most-once,但它降低了在线篡改被发现的能力,不能当成无损的优化开关。
常见问题
dm-verity 能防止别人替换 root hash 吗?
不能单独保证。root hash 是验证树的信任锚点,启动链需要通过签名、可信密钥环或其他已认证机制保护它;否则攻击者同时替换数据、哈希树和 root hash,校验仍可能“自洽”。
为什么 open 成功但读取文件时才报错?
dm-verity 按访问需求校验数据块,创建映射不等于已经读取了所有块。继续读取触发出错区域后,才能看到对应的 I/O 失败或损坏状态。
哈希区一定要单独占一个分区吗?
不一定。它可以是独立设备,也可以和数据位于同一设备的不同区域;后一种方式必须准确配置起始偏移、块数量和边界,并确保哈希区域不落入可写数据范围。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
348 收藏
-
193 收藏
-
220 收藏
-
227 收藏
-
180 收藏
-
331 收藏
-
386 收藏
-
261 收藏
-
233 收藏
-
179 收藏
-
文章 · linux | 1天前 | 容器 · Linux · 容器隔离 mount namespace PID namespace linux namespace network namespace268 收藏
-
490 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习