QEMU LUKS 分离卷头(Detached Header)完整指南:架构原理、qemu-img 创建与 libvirt QMP 热插拔实战
虚拟化硬件仿真【免费下载链接】qemuOfficial QEMU mirror. Please see https://www.qemu.org/contribute/ for how to submit changes to QEMU. Pull Requests are disabled. Please only use release tarballs from the QEMU website.项目地址https://gitcode.com/gh_mirrors/qe/qemu点击查看免费下载LUKS 分离卷头detached header允许将卷头与密钥材料存放在独立于负载数据的磁盘/文件上是 QEMU 自 9.0 起正式支持的能力对应 QAPI 字段detached-header。本文以 QEMU 仓库中的设计文档 docs/devel/luks-detached-header.rst 为主体结合 crypto/、block/crypto.c 与 iotests 测试实现系统讲解其设计动机、块设备图架构并给出qemu-img创建、-blockdev启动配置与 libvirtvirsh qemu-monitor-command热插拔三条完整可运行的实操路径。读完本文你将掌握分离卷头与普通 LUKS 卷的差异、QEMU 块层如何用header子节点关联两块存储以及如何在创建和运行两个阶段把加密盘接入虚拟机。背景为什么需要把 LUKS 卷头单独存放LUKS 格式本身支持将卷头存放在与负载payload不同的卷上。QEMU 的 LUKS 驱动block/crypto.c crypto/block-luks.c扩展了这一能力使 qemu-img 和 QEMU 块层都能读写这种分离卷头的加密卷。普通 LUKS 卷在单块磁盘上的布局如下----------------------------------------------- | | | | disk | header | key material | disk payload data | | | | | -----------------------------------------------使用分离卷头后需要两块磁盘disk1存放卷头和密钥材料disk2存放纯负载数据-------------------------- disk1 | header | key material | -------------------------- --------------------- disk2 | disk payload data | ---------------------这样做能带来四类实际收益设计文档原文归纳保密性Secrecy由于卷上没有卷头disk2无法被识别为包含 LUKS 卷不会暴露加密盘的身份。访问控制Control如果disk1的访问受到限制即使有人拿到disk2也无法解锁。典型场景是把负载盘放在 NFS 上但只向被指定的宿主机动态提供卷头访问权从而限制哪些宿主机能从中启动 VM 实例。灵活性Flexibility应用数据卷往往已有固定大小扩容来加加密层很不方便。此时可以单独存放 LUKS 卷头直接复用现有存储卷作为负载。可恢复性Recovery卷头中一个比特的损坏可能让整个负载无法访问。分离卷头便于单独备份卷头主卷头损坏时仍可通过指向备份的分离卷头来解锁数据。架构块设备图上的两层父子关系以 qcow2 加密为例分离卷头在 QEMU 块设备图block graph中的拓扑如下来自设计文档----------------------------- Root node | foo[luks] | ----------------------------- | | file | header | | | --------------------- ------------------ Child node |payload-format[qcow2]| |header-format[raw]| --------------------- ------------------ | | file | file | | | ---------------------- --------------------- Child node |payload-protocol[file]| |header-protocol[file]| ---------------------- --------------------- | | Host storage Host storage根节点foo[luks]有两个子节点file负载数据所在的 qcow2 格式节点其下再挂 file 协议节点访问宿主存储与headerLUKS 卷头和密钥材料所在的 raw 格式节点同样经 file 协议节点落到另一份宿主存储。源码中这一拓扑的落点如下在 block/crypto.c 的block_crypto_open_generic()中驱动通过bdrv_open_child(..., header, ...)以BDRV_CHILD_METADATA角色打开header子节点并将引用保存在BlockCrypto结构的BdrvChild *header字段block/crypto.c。一旦检测到header子节点存在就会置位QCRYPTO_BLOCK_OPEN_DETACHED标志block/crypto.c再调用qcrypto_block_open()打开加密卷。在加密层内部QCryptoBlock结构以bool detached_header字段记录这一状态crypto/blockpriv.h。这一设计意味着QEMU 块层并不要求负载必须是 qcow2——raw、qcow2 等任何可作为file子节点的格式都可以作为加密负载卷头侧则统一以 raw 格式承载。使用一用 qemu-img 创建带分离卷头的 LUKS 盘创建分离卷头与负载第一步用qemu-img create以detached-headertrue创建只含卷头的镜像文件第二步创建负载镜像第三步用 JSON 语法把它们组装成一个 LUKS 卷来查看信息# qemu-img create --object secret,idsec0,dataabc123 -f luks \ -o cipher-algaes-256,cipher-modexts -o key-secretsec0 \ -o detached-headertrue test-header.img # qemu-img create -f qcow2 test-payload.qcow2 200G # qemu-img info json:{driver:luks,file:{filename: \ test-payload.img},header:{filename:test-header.img}}三个命令的要点第一个命令的-f luks -o detached-headertrue是关键block_crypto_co_create_luks()在解析选项时通过qemu_opt_get_bool_del(opts, detached-header, false)读取该布尔选项block/crypto.c若为真则向加密层传递QCRYPTO_BLOCK_CREATE_DETACHED标志block/crypto.c。在加密层创建流程中block-detached_header flags QCRYPTO_BLOCK_CREATE_DETACHEDcrypto/block.c。随后 crypto/block-luks.c 会做两件事把payload_offset_sector置 0表示卷头镜像本身从 0 扇区开始读写并调用initfunc()为卷头镜像预留(header_sectors QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * split_key_sectors) * sector_size的空间容纳卷头与全部密钥槽。第三个命令中file指向负载镜像、header指向卷头镜像QEMU 通过qemu-img info的 JSON 协议描述即可同时解析两块镜像。qemu-img info的format-specific输出中会包含detached-header字段见下文测试章节用于确认卷是否处于分离状态。打开时如何判定分离状态打开而非创建时判定逻辑藏在负载偏移上crypto/block-luks.c 中block-detached_header (block-payload_offset 0)。也就是说LUKS 卷头中记录的payload_offset若为 0则视为分离卷头——此时负载数据从文件起始处读写而正常内嵌卷头情形下负载偏移等于卷头密钥材料的长度。QAPI 层面对齐qemu-img info输出中的detached-header字段来自 QAPI 结构QCryptoBlockInfoLUKS其定义位于 qapi/crypto.json注释明确标注 whether the LUKS header is detached (Since 9.0)。该信息最终由 crypto/block-luks.c 的info-u.luks.detached_header block-detached_header填充。设计文档与 QAPI 均将分离卷头能力标记为 QEMU 9.0 起引入。使用二启动 VM 时用 -blockdev 组装分离卷头 LUKS 盘在qemu-system-x86_64启动参数中分离卷头盘需要四个块设备节点存储层 ×2、格式层 ×2再加一个 luks 格式层共五个节点# qemu-system-x86_64 ... \ -object {qom-type:secret,id:libvirt-3-format-secret, \ data:abc123} \ -blockdev {driver:file,filename:/path/to/test-header.img, \ node-name:libvirt-1-storage} \ -blockdev {node-name:libvirt-1-format,read-only:false, \ driver:raw,file:libvirt-1-storage} \ -blockdev {driver:file,filename:/path/to/test-payload.qcow2, \ node-name:libvirt-2-storage} \ -blockdev {node-name:libvirt-2-format,read-only:false, \ driver:qcow2,file:libvirt-2-storage} \ -blockdev {node-name:libvirt-3-format,driver:luks, \ file:libvirt-2-format,header:libvirt-1-format,key-secret: \ libvirt-3-format-secret} \ -device {driver:virtio-blk-pci,bus:XXX,addr:YYY,drive: \ libvirt-3-format,id:virtio-disk1}节点组织与架构图的对应关系节点驱动作用对应架构图角色libvirt-1-storagefile打开卷头镜像文件header-protocol[file]libvirt-1-formatraw以 raw 格式解析卷头header-format[raw]libvirt-2-storagefile打开负载镜像文件payload-protocol[file]libvirt-2-formatqcow2以 qcow2 格式解析负载payload-format[qcow2]libvirt-3-formatluks通过fileheader绑定两者并解密foo[luks]根节点关键点luks 节点的file指向 qcow2 格式节点header指向 raw 格式节点key-secret指向解密用的 secret 对象。这个header参数正是 block/crypto.c 中bdrv_open_child(..., header, ...)所消费的选项。secret 对象通过data直接给出口令生产环境建议改用file指向密钥文件以避免口令出现在命令行中。bus、addr需按实际 PCI 拓扑填写示例中用XXX/YYY占位。使用三运行时向运行中的 VM 热插拔分离卷头盘libvirt QMP若 VM 已由 libvirt 管理可在运行期间通过virsh qemu-monitor-command以 QMP 消息逐步完成热插拔。整个过程与-blockdev参数一一对应共 7 步。第 1 步注入解密 secret# virsh qemu-monitor-command vm {execute:object-add, \ arguments:{qom-type:secret, id: \ libvirt-4-format-secret, data:abc123}}第 2 步添加卷头协议节点file# virsh qemu-monitor-command vm {execute:blockdev-add, \ arguments:{node-name:libvirt-1-storage, driver:file, \ filename: /path/to/test-header.img }}第 3 步添加卷头 raw 格式节点# virsh qemu-monitor-command vm {execute:blockdev-add, \ arguments:{node-name:libvirt-1-format, driver:raw, \ file:libvirt-1-storage}}第 4 步添加负载协议节点file# virsh qemu-monitor-command vm {execute:blockdev-add, \ arguments:{node-name:libvirt-2-storage, driver:file, \ filename:/path/to/test-payload.qcow2}}第 5 步添加负载 qcow2 格式节点# virsh qemu-monitor-command vm {execute:blockdev-add, \ arguments:{node-name:libvirt-2-format, driver:qcow2, \ file:libvirt-2-storage}}第 6 步添加 luks 格式节点用 header 字段关联卷头# virsh qemu-monitor-command vm {execute:blockdev-add, \ arguments:{node-name:libvirt-3-format, driver:luks, \ file:libvirt-2-format, header:libvirt-1-format, \ key-secret:libvirt-2-format-secret}}注意设计文档第 6 步示例中的key-secret写作libvirt-2-format-secret与第 1 步创建的libvirt-4-format-secret名称不一致实操时应保持 secret id 前后统一例如统一改为libvirt-4-format-secret否则解锁会因找不到 secret 而失败。这也提醒我们header参数指定的是卷头格式节点而key-secret指定的是口令对象两者职责不同。第 7 步热插拔 virtio-blk 设备# virsh qemu-monitor-command vm {execute:device_add, \ arguments: {driver:virtio-blk-pci, \ drive: libvirt-3-format, id:virtio-disk2}}至此运行中的 VM 内会新增一块由分离卷头解密、qcow2 负载的加密 virtio 磁盘。要撤销可依次device_del、blockdev-del逆序拆除节点。源码级佐证iotests 测试如何验证整个链路仓库自带的集成测试 tests/qemu-iotests/tests/luks-detached-header归入rw auto测试组随qemu-iotests运行覆盖了与本文完全一致的三种场景可作为实操模板创建阶段测试同时使用两条路径创建分离卷头一是blockdev-createdriver: imgfmt, header: luks-2-header-storage, file: luks-2-payload-storage二是qemu_img_create(-f, luks, ..., -o, detached-headertrue, ...)验证了 CLI 与 QMP 两条创建路径等价。qemu-img info校验test_img_creation()断言普通 LUKS 盘的format-specific.data[detached-header]为False而 raw 负载与 qcow2 负载两种分离卷头盘均为True正好印证上文 QAPI 字段与打开判定逻辑。I/O 验证test_detached_luks_header()分别对普通盘、raw 负载分离盘、qcow2 负载分离盘执行write -P/read -P模式写读并在tearDown()中检查日志里是否出现 Pattern verification failed。测试里 qemu-io 的write -P 41raw 负载、write -P 42qcow2 负载等模式写读全部通过说明分离卷头下的加解密路径与普通卷完全一致。此外打开分离卷头时加密层会做针对性优化include/crypto/block.h中QCRYPTO_BLOCK_OPEN_DETACHED标志的注释明确说明 the open process will be optimized to skip the LUKS payload overlap check跳过 LUKS 负载重叠检查即分离场景下无需校验负载是否与卷头区域重叠。已知限制与展望设计文档在 TODO 一节中记录了一项待办支持 VM 内共享同一分离 LUKS 卷头目前一个卷头节点对应一个 luks 节点多个盘共享同一卷头的场景例如同一密钥加密的多个负载盘共用一个卷头文件尚未实现属于后续演进方向。实际使用时还需注意分离卷头盘的备份策略要分别对待——负载盘可以随意备份/复制但卷头文件必须妥善保管并单独备份这正是分离带来的恢复性优势若卷头丢失即使拥有负载盘也无法解密数据。小结概念分离卷头把 LUKS 卷头与密钥材料从负载中剥离获得保密性、访问控制、灵活性与可恢复性四类收益。架构QEMU 块层以luks根节点 file负载/header卷头两个子节点组织header子节点以BDRV_CHILD_METADATA角色挂载block/crypto.c。判定创建侧由detached-headertrue选项驱动block/crypto.c打开侧以payload_offset 0判定crypto/block-luks.cqemu-img info通过detached-header字段QEMU 9.0对外暴露qapi/crypto.json。实操创建用qemu-img create -o detached-headertrue启动用 5 节点-blockdev链热插拔用 7 步 QMP 消息全部流程可对照 tests/qemu-iotests/tests/luks-detached-header 验证。相关参考文件设计文档、块层驱动、加密层 LUKS 实现、QAPI 定义、加密层标志、集成测试。赞分享虚拟化硬件仿真【免费下载链接】qemuOfficial QEMU mirror. Please see https://www.qemu.org/contribute/ for how to submit changes to QEMU. Pull Requests are disabled. Please only use release tarballs from the QEMU website.项目地址https://gitcode.com/gh_mirrors/qe/qemu点击查看免费下载相关推荐QEMU 的 LUKS 分离头Detached Header加密卷架构原理与实战配置QEMU 的 LUKS 分离头Detached Header加密卷架构原理与实战配置 本文基于 QEMU 仓库中的 Cryptography in QEM虚拟化硬件仿真QEMU 虚拟 CPUvCPU热插拔完整实战基于 QMP device_add / device_del 的在线增删 CPU 指南QEMU 虚拟 CPUvCPU热插拔完整实战基于 QMP device_add / device_del 的在线增删 CPU 指南 导读 本文以 QEMU虚拟化硬件仿真QEMU qdev 设备模型 API 完全指南从设备创建、realize 到 GPIO 与热插拔QEMU qdev 设备模型 API 完全指南从设备创建、realize 到 GPIO 与热插拔 本文是 QEMU 开源项目内部 API 参考文档 docs虚拟化硬件仿真创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考