登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

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
Linux dm-verity 数据区、哈希区、Merkle 哈希树与可信 root hash 的静态结构说明图
图1:说明图,展示数据区、哈希区与可信 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
Linux dm-verity open 创建 root-ro 只读映射并连接到根文件系统挂载边界的结构说明图
图2:结构说明图,展示 open、只读映射、内核按需校验与根文件系统挂载之间的边界,不是运行截图。

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 失败或损坏状态。

哈希区一定要单独占一个分区吗?

不一定。它可以是独立设备,也可以和数据位于同一设备的不同区域;后一种方式必须准确配置起始偏移、块数量和边界,并确保哈希区域不落入可写数据范围。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>