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 - 恢复模式 - 在此模式下,不重放日志,

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

  5. 其他参数的数量

其他参数

journal_sectors:number

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

interleave_sectors:number(默认 32768)

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

meta_device:device

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

buffer_sectors:number(默认 128)

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

journal_watermark:number(默认 50)

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

commit_time:number(默认 10000)

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

internal_hash:algorithm(:key) (密钥是可选的)

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

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

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

recalculate

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

journal_crypt:algorithm(:key) (密钥是可选的)

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

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

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

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

当使用internal-hash时,此选项不是必需的,因为在这种模式下,在重放日志时会检查日志条目的完整性。因此,在这种情况下会检测到修改后的扇区号。

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。重新加载目标时,不应更改其他参数,因为磁盘数据的布局取决于这些参数,并且重新加载的目标将无法正常工作。

例如,在默认的交错扇区interleave_sectors为32768、块大小block_size为512以及带有4字节标签大小的crc32c的internal_hash的设备上,将需要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或更大。

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