dm-integrity

dm-integrity 目标模拟了一个块设备,该设备具有额外的按扇区标签,可用于存储完整性信息。

将完整性标签与每个扇区一起存储的一个普遍问题是,写入扇区和完整性标签必须是原子的——即在崩溃的情况下,要么扇区和完整性标签都写入,要么都不写入。

为了保证写入原子性,dm-integrity 目标使用日志,它将扇区数据和完整性标签写入日志,提交日志,然后将数据和完整性标签复制到各自的位置。

dm-integrity 目标可以与 dm-crypt 目标一起使用——在这种情况下,dm-crypt 目标生成完整性数据,并通过附加到 bio 的 bio_integrity_payload 将它们传递给 dm-integrity 目标。在此模式下,dm-crypt 和 dm-integrity 目标提供经过身份验证的磁盘加密——如果攻击者修改加密设备,则会返回 I/O 错误而不是随机数据。

dm-integrity 目标也可以作为独立目标使用,在此模式下,它在内部计算并验证完整性标签。在此模式下,dm-integrity 目标可用于检测磁盘上或 I/O 路径中的静默数据损坏。

dm-integrity 还有一种替代操作模式,它使用位图而不是日志。如果位图中的一个位为 1,则对应区域的数据和完整性标签未同步——如果机器崩溃,未同步区域将被重新计算。位图模式比日志模式快,因为我们不必写入两次数据,但它也更不可靠,因为如果机器崩溃时发生数据损坏,可能无法检测到。

首次加载目标时,内核驱动程序将格式化设备。但它只会在超级块包含零时格式化设备。如果超级块既无效又非零,则无法加载 dm-integrity 目标。

对包含校验和(又称标签)的磁盘元数据区域的访问通过 dm-bufio 进行缓冲。当访问任何给定元数据区域时,每个唯一的元数据区域都会获得其自己的缓冲区。缓冲区大小限制为元数据区域的大小,但可能更小,从而需要多个缓冲区来表示完整的元数据区域。较小的缓冲区大小将为小读取/写入生成对元数据区域的较小读取/写入操作。即使在对单个缓冲区覆盖的数据进行完全写入时,元数据仍然会被读取。

首次使用目标

  1. 用零覆盖超级块

  2. 以一个扇区大小加载 dm-integrity 目标,内核驱动程序将格式化设备

  3. 卸载 dm-integrity 目标

  4. 从超级块读取“provided_data_sectors”值

  5. 以目标大小“provided_data_sectors”加载 dm-integrity 目标

  6. 如果您想将 dm-integrity 与 dm-crypt 一起使用,请以大小“provided_data_sectors”加载 dm-crypt 目标

目标参数

  1. 底层块设备

  2. 设备开头的保留扇区数——dm-integrity 不会读取或写入这些扇区

  3. 完整性标签的大小(如果使用“-”,则大小取自内部哈希算法)

  4. 模式

    D - 直接写入(无日志)

    在此模式下,不使用日志,数据扇区和完整性标签单独写入。在崩溃的情况下,数据和完整性标签可能不匹配。

    J - 日志写入

    数据和完整性标签写入日志,并保证原子性。在崩溃的情况下,要么数据和标签都写入,要么都不写入。日志模式会将写入吞吐量降低一倍,因为数据必须写入两次。

    B - 位图模式 - 数据和元数据写入时没有任何

    同步,驱动程序维护一个脏区域的位图,其中数据和元数据不匹配。此模式只能与内部哈希一起使用。

    R - 恢复模式 - 在此模式下,日志不重放,

    校验和不检查,并且不允许写入设备。如果设备无法以任何其他标准模式激活,此模式对于数据恢复很有用。

    I - 内联模式 - 在此模式下,dm-integrity 将存储完整性

    数据直接存储在底层设备扇区中。底层设备必须具有允许存储用户完整性数据并为所选完整性标签提供足够空间的完整性配置文件。

  5. 附加参数的数量

附加参数

journal_sectors:数量

日志的大小,此参数仅在格式化设备时使用。如果设备已格式化,则使用超级块中的值。

interleave_sectors:数量 (默认 32768)

交错扇区数。此值向下舍入到二的幂。如果设备已格式化,则使用超级块中的值。

meta_device:设备

不要在设备上交错数据和元数据。为元数据使用单独的设备。

buffer_sectors:数量 (默认 128)

一个元数据缓冲区中的扇区数。该值向下舍入到二的幂。

journal_watermark:数量 (默认 50)

日志水印百分比。当日志大小超过此水印时,将启动刷新日志的线程。

commit_time:数量 (默认 10000)

提交时间(毫秒)。当此时间过去时,日志将被写入。如果收到 FLUSH 请求,日志也会立即写入。

internal_hash:算法(:密钥) (密钥可选)

使用内部哈希或 CRC。当使用此参数时,dm-integrity 目标将不接受来自上层目标的完整性标签,但它将自动生成和验证完整性标签。

您可以使用 CRC 算法(例如 crc32),然后完整性目标将保护数据免受意外损坏。您还可以使用 HMAC 算法(例如“hmac(sha256):0123456789abcdef”),在此模式下,它将提供数据的加密认证而无需加密。

当不使用此参数时,完整性标签将从上层目标(例如 dm-crypt)接收。上层目标应检查完整性标签的有效性。

recalculate

自动重新计算完整性标签。仅在使用内部哈希时有效。

journal_crypt:算法(:密钥) (密钥可选)

使用给定算法加密日志,以确保攻击者无法读取日志。您可以在此处使用分组密码(例如“cbc(aes)”)或流密码(例如“chacha20”或“ctr(aes)”)。

日志包含块设备上次写入的历史记录,攻击者读取日志可以看到上次写入的扇区号。从扇区号,攻击者可以推断出写入文件的大小。为了防止这种情况,您可以加密日志。

journal_mac:算法(:密钥) (密钥可选)

保护日志中的扇区号免受意外或恶意修改。为了防止意外修改,请使用 CRC 算法;为了防止恶意修改,请使用带有密钥的 HMAC 算法。

在使用内部哈希时不需要此选项,因为在此模式下,在重放日志时会检查日志条目的完整性。因此,在此阶段将检测到修改的扇区号。

block_size:数量 (默认 512)

数据块的大小(字节)。块大小越大,每块完整性元数据的开销越小。支持的值为 512、1024、2048 和 4096 字节。

sectors_per_bit:数量

在位图模式下,此参数指定与一个位图位对应的 512 字节扇区数。

bitmap_flush_interval:数量

位图刷新间隔(毫秒)。当此间隔到期时,元数据缓冲区将同步。

allow_discards

允许对完整性设备进行块丢弃请求(即 TRIM)。丢弃仅允许用于使用内部哈希的设备。

fix_padding

使用更小的标签区域填充,更节省空间。如果不存在此选项,则使用大填充——这是为了与旧内核兼容。

fix_hmac

提高 internal_hash 和 journal_mac 的安全性

  • 区段号混入 mac 中,以便攻击者无法将扇区从一个日志区段复制到另一个日志区段

  • 超级块受 journal_mac 保护

  • 超级块中存储的 16 字节盐值混入 mac 中,以便攻击者无法检测到两个磁盘具有相同的 HMAC 密钥,并且也禁止攻击者将扇区从一个磁盘移动到另一个磁盘

legacy_recalculate

允许重新计算带有 HMAC 密钥的卷。出于安全原因,默认禁用此功能——攻击者可以修改卷,将 recalc_sector 设置为零,而内核将不会检测到修改。

日志模式 (D/J)、buffer_sectors、journal_watermark、commit_time 和 allow_discards 可以在重新加载目标时更改(加载非活动表并使用 suspend 和 resume 交换表)。重新加载目标时,其他参数不应更改,因为磁盘数据的布局取决于它们,重新加载的目标将无法工作。

例如,在默认 interleave_sectors 为 32768、block_size 为 512,以及内部哈希为 crc32c 且标签大小为 4 字节的设备上,跟踪完整数据区域需要 128 KiB 的标签,每个数据区域需要 256 个元数据扇区。默认 buffer_sectors 为 128,这意味着每个元数据区域将有 2 个缓冲区,或者每 16 MiB 数据有 2 个缓冲区。

状态行

  1. 完整性不匹配的数量

  2. 提供的扇区数据——即用户可以使用的扇区数量

  3. 当前重新计算位置(如果未重新计算,则为‘-’)

格式化块设备的布局

  • 保留扇区

    (它们不被此目标使用,可用于存储 LUKS 元数据或用于其他目的),保留区域的大小在目标参数中指定

  • 超级块 (4kiB)
    • 魔术字符串 - 标识设备已格式化

    • 版本

    • log2(交错扇区)

    • 完整性标签大小

    • 日志区段数

    • 提供的扇区数据——此目标提供的扇区数(即设备大小减去所有元数据和填充的大小)。此目标的用户不应发送超出“提供的扇区数据”限制的 BIOS 访问数据。

    • 标志
      SB_FLAG_HAVE_JOURNAL_MAC
      • 如果使用 journal_mac,则设置此标志

      SB_FLAG_RECALCULATING
      • 正在重新计算

      SB_FLAG_DIRTY_BITMAP
      • 日志区域包含脏块的位图

    • log2(每块扇区数)

    • 重新计算完成的位置

  • 日志

    日志分为多个区段,每个区段包含

    • 元数据区域 (4kiB),其中包含日志条目

      • 每个日志条目包含

        • 逻辑扇区(指定数据和标签应写入的位置)

        • 数据的最后 8 字节

        • 完整性标签(大小在超级块中指定)

      • 每个元数据扇区以

        • mac (8 字节) 结尾,8 个元数据扇区中的所有 mac 组成一个 64 字节的值。它用于存储日志区段中扇区号的 HMAC,以防止攻击者篡改日志中的扇区号。

        • 提交 ID

    • 数据区域(大小可变;取决于有多少日志条目适合元数据区域)

      • 数据区域中的每个扇区都包含

        • 数据(504 字节的数据,最后 8 字节存储在日志条目中)

        • 提交 ID

    为了测试整个日志区段是否正确写入,日志的每个 512 字节扇区都以 8 字节的提交 ID 结尾。如果日志区段中所有扇区的提交 ID 都匹配,则假定该区段已正确写入。如果提交 ID 不匹配,则该区段仅部分写入,不应重放。

  • 一个或多个交错标签和数据的运行。

    每个运行包含

    • 标签区域 - 包含完整性标签。数据区域中的每个扇区都有一个标签。此区域的大小始终为 4KiB 或更大。

    • 数据区域 - 包含数据扇区。一个运行中的数据扇区数必须是二的幂。此值的 log2 存储在超级块中。