dm-crypt/设备加密

出自 ArchWiki

本节介绍如何从命令行手动使用 dm-crypt 来加密系统。

准备工作

在使用 cryptsetup 之前,请始终确保已加载 dm_crypt 内核模块

Cryptsetup 用法

cryptsetup(8) 是一个命令行工具,用于与 dm-crypt 交互,以创建、访问和管理加密设备。该工具后来扩展为支持依赖于 Linux 内核 设备映射器加密 模块的不同加密类型。最值得注意的扩展是 Linux 统一密钥设置 (LUKS) 扩展,它将 dm-crypt 所需的所有设置信息存储在磁盘本身上,并抽象化了分区和密钥管理,以尝试提高易用性。通过设备映射器访问的设备称为块设备。有关更多信息,请参阅 Data-at-rest encryption#块设备加密

该工具的使用方法如下

# cryptsetup action options device dmname

它为选项和加密模式内置了默认值,如果在命令行上没有指定其他选项,将使用这些默认值。请查看

$ cryptsetup --help 

其中按顺序列出了选项、操作以及加密模式的默认参数。完整的选项列表可以在手册页中找到。由于不同的参数是必需的或可选的,具体取决于加密模式和操作,以下各节将进一步指出差异。块设备加密速度很快,但速度也很重要。由于在设置后更改块设备的加密密码很困难,因此预先检查 dm-crypt 对于各个参数的性能非常重要

$ cryptsetup benchmark 

可以为在安装前决定算法和密钥大小提供指导。如果某些 AES 密码在吞吐量方面表现出色,那么这些密码很可能是在 CPU 中有硬件支持的密码。

提示: 在学习时,您可能需要练习在 虚拟机 中加密虚拟硬盘。

Cryptsetup 密码和密钥

加密的块设备受密钥保护。密钥可以是

两种密钥类型都有默认的最大大小:密码最多可以有 512 个字符,密钥文件最多可以有 8192 KiB。

此时需要注意 LUKS 的一个重要区别是,密钥用于解锁 LUKS 加密设备的主密钥,并且可以使用 root 权限更改。其他加密模式不支持在设置后更改密钥,因为它们不使用主密钥进行加密。有关详细信息,请参阅 Data-at-rest encryption#块设备加密

加密选项

Cryptsetup 支持不同的加密操作模式,以便与 dm-crypt 一起使用

可用于所有模式的加密密码和哈希的基本加密选项依赖于内核加密后端功能。所有加载并在运行时可用作选项的选项都可以使用以下命令查看

$ less /proc/crypto 
提示: 如果列表很短,请执行 $ cryptsetup benchmark,这将触发加载可用的模块。

以下介绍了 luksluks1luks2plain 模式的加密选项。请注意,表格列出了本文各自示例中使用的选项,而不是所有可用的选项。

LUKS 模式的加密选项

在 LUKS 加密模式下设置新的 dm-crypt 设备的 cryptsetup 操作是 luksFormat。与名称暗示的不同,它不格式化设备,而是设置 LUKS 设备标头并使用所需的加密选项加密主密钥。

为了使用 cryptsetup --help 列出的内置默认值创建一个新的 LUKS 容器,只需执行

# cryptsetup luksFormat device

从 cryptsetup 2.4.0 开始,这相当于

# cryptsetup luksFormat --type luks2 --cipher aes-xts-plain64 --hash sha256 --iter-time 2000 --key-size 256 --pbkdf argon2id --use-urandom --verify-passphrase device

下表将默认值与加密级别更高的规范示例进行比较,并附带注释

选项 Cryptsetup 2.1.0 默认值 示例 注释
--cipher

-c

aes-xts-plain64 aes-xts-plain64 版本 1.6.0 将默认值更改为 密码 中的 AES XTS 模式(请参阅 FAQ 中的第 5.16 项)。建议不要使用以前的默认值 --cipher aes-cbc-essiv,因为它存在已知的 问题 以及针对它们的实际 攻击
--key-size

-s

256 (XTS 为 512) 512 默认情况下,XTS 密码使用 512 位密钥大小。但请注意,XTS 将提供的密钥分成两半,因此最终使用的是 AES-256。
--hash

-h

sha256 sha512 用于 密钥派生 的哈希算法。版本 1.7.0 将默认值从 sha1 更改为 sha256,"不是出于安全原因 [而是] 主要为了防止在已淘汰 SHA1 的强化系统上出现兼容性问题"[1]。以前的默认值 sha1 仍然可以用于与旧版本的 cryptsetup 兼容,因为它被 认为是安全的(请参阅第 5.20 项)。
--iter-time

-i

2000 5000 用于 PBKDF 密码处理的毫秒数。版本 1.7.0 将默认值从 1000 更改为 2000,以 "尝试保持 PBKDF2 迭代计数仍然足够高,并且对于用户来说仍然可以接受。"[2]。此选项仅与设置或更改密码的 LUKS 操作相关,例如 luksFormatluksAddKey。将参数指定为 0 会选择内置的默认值。
--use-urandom --use-urandom --use-random 选择要使用的 随机数生成器。请注意,/dev/random 阻塞池已被移除。因此,--use-random 标志现在等同于 --use-urandom
--verify-passphrase

-y

- 在 Arch Linux 中,对于 luksFormatluksAddKey 默认启用。
--sector-size 5124096(取决于设备) 4096 设置用于磁盘加密的扇区大小(以字节为单位)。对于报告自身为 4Kn 或 512e 的块设备,默认值为 4096,对于报告自身为 512n 的块设备,默认值为 512。将扇区大小从 512 字节增加到 4096 字节可以在大多数现代存储设备上提供更好的性能。请参阅 Advanced Format#dm-crypt

LUKS 功能和选项的属性在 LUKS1 (pdf) 和 LUKS2 (pdf) 规范中进行了描述。

提示: 项目开发人员的 devconfcz2016 (pdf) 演示文稿总结了将规范主要更新到 LUKS2 的动机。另请参阅 cryptsetup 的主要 v2 发行说明

迭代时间

来自 cryptsetup FAQ§2.1§3.4

密钥槽的解锁时间 [...] 在设置密码时计算。默认情况下为 1 秒(LUKS2 为 2 秒)。 [...]
密码迭代计数基于时间,因此安全级别取决于创建 LUKS 容器的系统的 CPU 性能。 [...]
如果您在快速计算机上设置密码,然后在慢速计算机上解锁它,则解锁时间可能会长得多。

因此,最好始终在最常访问容器的计算机上创建容器。

阅读这些部分的其余部分,以获取有关如何在需要时正确调整迭代计数的建议。

plain 模式的加密选项

在 dm-crypt plain 模式下,设备上没有主密钥,因此,无需设置它。相反,要使用的加密选项直接用于创建加密磁盘和命名设备之间的映射。可以针对分区或完整设备创建映射。在后一种情况下,甚至不需要分区表。

要使用 cryptsetup 的默认参数创建 plain 模式映射

# cryptsetup open --type plain options device dmname

执行它将提示输入密码,该密码应具有非常高的熵,可以使用 --verify-passphrase 选项,但这不是默认选项。通常,建议确切记录创建时使用的加密选项,因为它们无法从加密设备或可选的密钥文件中派生出来,并且上游默认值可能会更改。

以下是将默认参数与 dm-crypt/Encrypting an entire system#Plain dm-crypt 中的示例进行的比较。

选项 Cryptsetup 2.7.0 默认值 示例 注释
--hash

-h

sha256 - 哈希用于从密码创建密钥;它不用于密钥文件。
--cipher

-c

aes-xts-plain64 aes-xts-plain64 密码由三个部分组成:密码-链模式-IV 生成器。有关这些设置的说明,请参阅 Data-at-rest encryption#密码和操作模式,有关可用选项,请参阅 DMCrypt 文档
--key-size

-s

256 512 密钥大小(以位为单位)。大小将取决于正在使用的密码以及正在使用的链模式。XTS 模式需要的密钥大小是 CBC 的两倍
--size

-b

目标磁盘的实际大小 - (应用默认值) 限制设备的最大大小(以 512 字节扇区为单位)。
--offset

-o

0 0 从目标磁盘开头(以 512 字节扇区为单位)开始映射的偏移量。
--skip

-p

0 2048(将跳过 512B×2048=1MiB) 在初始化向量 (IV) 计算中要跳过的加密数据的 512 字节扇区数。
--key-file

-d

默认使用密码 /dev/sdZ(或例如 /boot/keyfile.enc 要用作密钥的设备或文件。有关更多详细信息,请参阅 #密钥文件
--keyfile-offset 0 0 密钥开始的文件开头的偏移量(以字节为单位)。cryptsetup 1.6.7 及更高版本支持此选项。
--keyfile-size

-l

8192kB - (应用默认值) 限制从密钥文件中读取的字节数。cryptsetup 1.6.7 及更高版本支持此选项。
--sector-size 512 4096 设置用于磁盘加密的扇区大小(以字节为单位)。除了 4Kn 块设备外,所有设备的默认值均为 512。将扇区大小从 512 字节增加到 4096 字节可以在大多数现代存储设备上提供更好的性能。请参阅 Advanced Format#dm-crypt

使用设备 /dev/sdX,上面的右列示例结果为

# cryptsetup open --type plain --cipher=aes-xts-plain64 --offset=0 --skip=2048 --key-file=/dev/sdZ --key-size=512 --sector-size 4096 /dev/sdX enc
警告: 与使用 LUKS 加密不同,每当需要重新建立映射时,都必须完整地执行上述命令,因此记住密码、哈希和密钥文件详细信息非常重要。不要依赖 cryptsetup 默认值,因为它们在版本之间会发生变化。

我们现在可以检查是否已创建映射

# fdisk -l

现在应该存在 /dev/mapper/enc 的条目。

使用 cryptsetup 加密设备

本节介绍如何使用选项来创建新的加密块设备并手动访问它们。

警告: GRUB 对 LUKS2 的支持有限;有关详细信息,请参阅 GRUB#加密 /boot。对于 GRUB 需要解锁的分区,请使用带有 PBKDF2 的 LUKS2 (cryptsetup luksFormat --pbkdf pbkdf2)。

使用 LUKS 模式加密设备

格式化 LUKS 分区

为了将分区设置为加密的 LUKS 分区,请执行

# cryptsetup luksFormat device

然后将提示您输入密码并验证它。

有关命令行选项,请参阅 #LUKS 模式的加密选项

您可以使用以下命令检查结果

# cryptsetup luksDump device

您会注意到,转储不仅显示密码标头信息,还显示了 LUKS 分区正在使用的密钥槽。

以下示例将在 /dev/sda1 上创建加密的根分区,使用 XTS 模式下的默认 AES 密码,有效加密位数为 256 位

# cryptsetup luksFormat -s 512 /dev/sda1
使用 LUKS 和密钥文件格式化分区

创建新的 LUKS 加密分区时,可以在创建分区时使用密钥文件关联分区

# cryptsetup luksFormat device /path/to/mykeyfile

有关如何生成和管理密钥文件的说明,请参阅 #密钥文件

使用设备映射器解锁/映射 LUKS 分区

创建 LUKS 分区后,即可解锁它们。

解锁过程将使用设备映射器将分区映射到新的设备名称。这会提醒内核 device 实际上是一个加密设备,应通过 LUKS 使用 /dev/mapper/dm_name 进行寻址,以避免覆盖加密数据。为了防止意外覆盖,请阅读有关在完成设置后 备份加密标头 的可能性。

为了打开加密的 LUKS 分区,请执行

# cryptsetup open device dm_name

然后将提示您输入密码以解锁分区。通常,设备映射名称描述了映射的分区的功能。例如,以下命令解锁根 luks 分区 /dev/sda1 并将其映射到名为 root 的设备映射器

# cryptsetup open /dev/sda1 root 

打开后,根分区设备地址将为 /dev/mapper/root,而不是分区(例如 /dev/sda1)。

为了在加密层之上设置 LVM,解密卷组的设备文件将类似于 /dev/mapper/root 而不是 /dev/sda1。然后,LVM 将为所有创建的逻辑卷提供额外的名称,例如 /dev/lvmpool/root/dev/lvmpool/swap

为了将加密数据写入分区,必须通过设备映射名称访问它。访问的第一步通常是 创建文件系统。例如

# mkfs.ext4 /dev/mapper/root

然后可以像其他任何分区一样 挂载 设备 /dev/mapper/root

要关闭 LUKS 容器,请卸载分区并执行

# cryptsetup close root

使用 TPM 存储密钥

请参阅 Trusted Platform Module#LUKS 加密

使用 plain 模式加密设备

dm-crypt plain 模式加密的创建和后续访问都只需要使用带有正确 参数cryptsetup open 操作。以下通过两个非根设备的示例说明了这一点,但通过堆叠两者(即第二个在第一个内部创建)增加了一个怪癖。显然,堆叠加密会使开销加倍。这里的用例只是为了说明密码选项用法的另一个示例。

第一个映射器是使用 cryptsetup 的 plain 模式默认值创建的,如上表左列所述

# cryptsetup --type plain -v open /dev/sdxY plain1
WARNING: Using default options for cipher (aes-xts-plain64, key size 256 bits) that could be incompatible with older versions.
WARNING: Using default options for hash (sha256) that could be incompatible with older versions.
For plain mode, always use options --cipher, --key-size and if no keyfile is used, then also --hash.
Enter passphrase for /dev/sdxY:
Command successful.

现在我们在其中添加第二个块设备,使用不同的加密参数和(可选)偏移量,创建一个文件系统并挂载它

# cryptsetup --type plain --cipher=serpent-xts-plain64 --hash=sha256 --key-size=256 --offset=10  open /dev/mapper/plain1 plain2
Enter passphrase for /dev/mapper/plain1:
# lsblk -p
 NAME                                                     
 /dev/sda                                     
 ├─/dev/sdxY          
 │ └─/dev/mapper/plain1     
 │   └─/dev/mapper/plain2              
 ...
# mkfs -t ext2 /dev/mapper/plain2
# mount -t ext2 /dev/mapper/plain2 /mnt
# echo "This is stacked. one passphrase per foot to shoot." > /mnt/stacked.txt

我们关闭堆栈以检查访问是否有效

# cryptsetup close plain2
# cryptsetup close plain1

首先,让我们尝试直接打开文件系统

# cryptsetup --type plain --cipher=serpent-xts-plain64 --hash=sha256 --key-size=256 --offset=10 open /dev/sdxY plain2
# mount -t ext2 /dev/mapper/plain2 /mnt
mount: /mnt: wrong fs type, bad option, bad superblock on /dev/mapper/plain2, missing codepage or helper program, or other error.
       dmesg(1) may have more information after failed mount system call.

为什么不起作用?因为 “plain2” 起始块 (10) 仍然使用来自 “plain1” 的密码加密。它只能通过堆叠的映射器访问。但是,错误是任意的,尝试错误的密码或错误的选项会产生相同的结果。对于 dm-crypt plain 模式,open 操作本身不会出错。

再次尝试,顺序正确

# cryptsetup close plain2    # dysfunctional mapper from previous try
# cryptsetup --type plain open /dev/sdxY plain1
WARNING: Using default options for cipher (aes-xts-plain64, key size 256 bits) that could be incompatible with older versions.
WARNING: Using default options for hash (sha256) that could be incompatible with older versions.
For plain mode, always use options --cipher, --key-size and if no keyfile is used, then also --hash.
Enter passphrase for /dev/sdxY:
# cryptsetup --type plain --cipher=serpent-xts-plain64 --hash=sha256 --key-size=256 --offset=10 open /dev/mapper/plain1 plain2
Enter passphrase for /dev/mapper/plain1:
# mount /dev/mapper/plain2 /mnt && cat /mnt/stacked.txt
This is stacked. one passphrase per foot to shoot.

dm-crypt 也将处理带有某些混合模式的堆叠加密。例如,LUKS 模式可以堆叠在 “plain1” 映射器上。然后,当 “plain1” 关闭时,其标头将在 “plain1” 内部加密。

仅对于 plain 模式,选项 --shared 可用。使用它,可以将单个设备分段为不同的非重叠映射器。我们在下一个示例中这样做,这次对 “plain2” 使用 loopaes 兼容的密码模式

# cryptsetup --type plain --offset 0 --size 1000 open /dev/sdxY plain1
WARNING: Using default options for cipher (aes-xts-plain64, key size 256 bits) that could be incompatible with older versions.
WARNING: Using default options for hash (sha256) that could be incompatible with older versions.
For plain mode, always use options --cipher, --key-size and if no keyfile is used, then also --hash.
Enter passphrase for /dev/sdxY:
# cryptsetup --type plain --offset 1000 --size 1000 --shared --cipher=aes-cbc-lmk --hash=sha256 open /dev/sdxY plain2
WARNING: Using default options for cipher (aes-cbc-lmk, key size 256 bits) that could be incompatible with older versions.
For plain mode, always use options --cipher, --key-size and if no keyfile is used, then also --hash.
Enter passphrase for /dev/sdxY:
# lsblk -p
NAME                    
dev/sdxY                    
├─/dev/sdxY               
│ ├─/dev/mapper/plain1     
│ └─/dev/mapper/plain2     
...

正如设备树所示,两者都位于同一级别,即未堆叠,并且可以单独打开 “plain2”。

Cryptsetup 针对 LUKS 的特定操作

密钥管理

可以为 LUKS 分区定义额外的密钥。这使用户可以创建访问密钥以进行安全备份存储。在所谓的密钥托管中,一个密钥用于日常使用,另一个密钥托管起来,以便在忘记日常密码或密钥文件丢失/损坏时访问分区。还可以使用不同的密钥槽通过颁发第二个密钥并稍后再次撤销它来授予用户对分区的访问权限。

创建加密分区后,将创建初始密钥槽 0(如果未手动指定其他密钥槽)。其他密钥槽编号为 1 到 7。可以使用以下命令查看使用了哪些密钥槽

# cryptsetup luksDump /dev/device

其中 device 是包含 LUKS 标头的块设备。本节中的此命令和所有以下命令也适用于标头备份文件。

添加 LUKS 密钥

添加新密钥槽是通过 luksAddKey 操作完成的。为了安全起见,即使对于已解锁的设备,它也总是会要求提供有效的现有密钥(任何现有槽的密码),然后才能输入新密钥

# cryptsetup luksAddKey /dev/device [/path/to/additionalkeyfile]
Enter any existing passphrase:
Enter new passphrase for key slot:
Verify passphrase: 

如果给定了 /path/to/additionalkeyfile,cryptsetup 将为 additionalkeyfile 添加一个新的密钥槽。否则,它会提示输入新密码。要使用现有的 keyfile 授权该操作,请使用 --key-file-d 选项,后跟 “旧” keyfile 将尝试解锁所有可用的密钥文件密钥槽

# cryptsetup luksAddKey /dev/device [/path/to/additionalkeyfile] -d /path/to/keyfile

如果打算使用多个密钥并更改或撤销它们,可以使用 --key-slot-S 选项来指定槽

# cryptsetup luksAddKey /dev/device -S 6
WARNING: The --key-slot parameter is used for new keyslot number.
Enter any existing passphrase: 
Enter new passphrase for key slot: 
Verify passphrase:
# cryptsetup luksDump /dev/device
...
Keyslots:
...
6: luks2
      Key:      512 bits
      Priority: normal
...
提示: 对于 LUKS2,可以为各个密钥槽分配优先级,请参阅 cryptsetup-config(8)。例如,如果上面的槽 6 旨在在启动时解锁设备,请以 root 身份执行 cryptsetup config --priority prefer --key-slot 6 /dev/device

为了显示此示例中的关联操作,我们决定立即更改密钥

# cryptsetup luksChangeKey /dev/device -S 6
Enter passphrase to be changed: 
Enter new passphrase:
Verify passphrase:

然后再继续删除它。

移除 LUKS 密钥

有三种不同的操作可以从标头中删除密钥

  • luksRemoveKey 通过指定其密码/密钥文件来删除密钥。请参阅 cryptsetup-luksRemoveKey(8)
  • luksKillSlot 通过指定其槽(需要另一个有效密钥)来删除密钥。显然,如果您忘记了密码、丢失了密钥文件或无法访问它,这将非常有用。请参阅 cryptsetup-luksKillSlot(8)
  • erase 删除所有活动密钥。请参阅 cryptsetup-erase(8)
警告
  • 以上所有操作都可用于不可撤销地删除加密设备的最后一个活动密钥!
  • erase 命令不会提示输入有效密码!它不会 擦除 LUKS 标头,而是一次擦除所有密钥槽,因此,除非您有 LUKS 标头的有效备份,否则您将无法重新获得访问权限。
  • OPAL 硬件加密分区上使用 erase 命令时,LUKS 标头将被擦除,并且 OPAL 锁定范围将被移除。与软件加密不同,即使有有效的 LUKS 标头备份,此操作也是无法恢复的。

对于上述警告,最好知道我们要保留的密钥是有效的。一个简单的检查是使用 -v 选项解锁设备,这将指定它占用的槽

# cryptsetup --test-passphrase -v open /dev/device
No usable token is available.
Enter passphrase for /dev/device: 
Key slot 1 unlocked.
Command successful.

现在我们可以使用其密码删除在上一个子节中添加的密钥

# cryptsetup luksRemoveKey /dev/device
Enter passphrase to be deleted:

如果我们为两个密钥槽使用了相同的密码,则现在将擦除第一个槽。只有再次执行它才会删除第二个槽。

或者,我们可以指定密钥槽

# cryptsetup luksKillSlot /dev/device 6
Enter any remaining passphrase:

请注意,在这两种情况下,都不需要确认。

再次强调上面的警告:如果密钥槽 1 和 6 使用了相同的密码,则两者现在都将消失。

备份和恢复

如果 LUKS 加密分区的标头被破坏,您将无法解密您的数据。这与忘记密码或损坏用于解锁分区的密钥文件一样令人为难。损坏可能是由于您自己的错误(稍后重新分区磁盘时)或第三方程序误解分区表而发生的。因此,备份标头并将其存储在另一个磁盘上可能是一个好主意。

注意: 如果 LUKS 加密分区之一的密码泄露,您必须在每个加密标头的副本上撤销它,即使是您备份的副本也是如此。否则,使用泄露密码的备份加密标头副本可用于确定主密钥,而主密钥又可用于解密关联的分区(即使是您的实际分区,而不仅仅是备份版本)。另一方面,如果主密钥泄露,您必须重新加密整个分区。有关更多详细信息,请参阅 LUKS FAQ

使用 cryptsetup 备份

Cryptsetup 的 luksHeaderBackup 操作存储 LUKS 标头和密钥槽区域的二进制备份

# cryptsetup luksHeaderBackup /dev/device --header-backup-file /mnt/backup/file.img

其中 device 是包含 LUKS 卷的分区。

您还可以将纯文本标头备份到 tmpfs 中,并在将其写入持久存储之前使用例如 GPG 对其进行加密

# mount --mkdir -t tmpfs -o noswap tmpfs /root/tmp
# cryptsetup luksHeaderBackup /dev/device --header-backup-file /root/tmp/file.img
# gpg --recipient User_ID --encrypt /root/tmp/file.img 
# cp /root/tmp/file.img.gpg /mnt/backup/
# umount /root/tmp
提示: noswap 选项确保文件系统不会被交换到磁盘。

使用 cryptsetup 恢复

警告: 恢复错误的标头或恢复到未加密的分区将导致数据丢失!该操作无法检查标头是否实际上是该特定设备的正确标头。

为了避免恢复错误的标头,您可以首先将其用作远程 --header 来确保它确实有效

# cryptsetup -v --header /mnt/backup/file.img open /dev/device test
No usable token is available.
Enter passphrase for /dev/device:
Key slot 0 unlocked.
Command successful.
# mount /dev/mapper/test /mnt/test && ls /mnt/test 
# umount /mnt/test 
# cryptsetup close test 

既然检查成功,就可以执行恢复

# cryptsetup luksHeaderRestore /dev/device --header-backup-file ./mnt/backup/file.img

现在所有密钥槽区域都被覆盖;发出命令后,只有备份文件中的活动密钥槽可用。

手动备份和恢复

标头始终位于设备的开头,并且无需访问 cryptsetup 也可以执行备份。首先,您必须找出加密分区的有效负载偏移量

此文章或章节已过时。

原因: LUKS2 具有不同的 cryptsetup luksDump 输出。(在 Talk:Dm-crypt/Device encryption 中讨论)
# cryptsetup luksDump /dev/device | grep "Payload offset"
Payload offset:	4040

其次,检查驱动器的扇区大小

# fdisk -l /dev/device | grep "Sector size"
Sector size (logical/physical): 512 bytes / 512 bytes

现在您知道了这些值,您可以使用简单的 dd 命令备份标头

# dd if=/dev/device of=/path/to/file.img bs=512 count=4040

并安全地存储它。

然后可以使用与备份时相同的值执行还原

# dd if=./file.img of=/dev/device bs=512 count=4040

重新加密设备

此文章或章节需要扩充。

原因: 使用 LUKS2(带 16 MiB 标头)的 cryptsetup 2.2 支持在线加密/解密/重新加密。[3] (在 Talk:Dm-crypt/Device encryption 中讨论)

cryptsetup reencrypt 操作允许重新加密 LUKS 设备。对于 LUKS2 设备,可以执行在线重新加密,支持多个并行重新加密作业,并且对系统故障具有弹性。LUKS1 设备的重新加密只能离线(卸载)执行,使用单个进程且弹性较差。

有关操作模式和选项,请参阅 cryptsetup-reencrypt(8)

可以更改 #LUKS 模式的加密选项。它也可以用于将现有的未加密文件系统转换为 LUKS 加密的文件系统,或永久删除设备上的 LUKS 加密(使用 --decrypt;请参阅 删除系统加密)。重新加密对于分离的 LUKS 标头也是可行的,但请注意 --header 选项的警告。不支持重新加密 LUKS 以外的模式(例如 plain-mode)。

重新加密的一个应用可能是,在密码或 密钥文件 泄露并且无法确定是否已获取 LUKS 标头的副本后,再次保护数据。例如,如果仅密码被肩窥,但没有发生对设备的物理/逻辑访问,则仅更改相应的密码/密钥就足够了(#密钥管理)。

警告: 始终确保可靠的备份可用,并在使用该工具之前仔细检查您指定的选项!

以下示例展示了如何加密未加密的文件系统分区以及重新加密现有 LUKS 设备。

加密现有的未加密文件系统

提示: 如果您尝试加密现有的根分区,您可能需要创建一个单独的未加密的引导分区,该分区将挂载到 /boot(请参阅 Dm-crypt/加密整个系统#准备引导分区)。这并非绝对必要,但具有许多优点
  • 如果 /boot 位于加密的根分区内,则系统在机器开机时会要求输入两次密码。第一次发生在引导加载程序尝试读取加密的 /boot 内的文件时,第二次发生在内核尝试挂载加密分区时 [4]。这可能不是期望的行为,可以通过拥有一个单独的未加密的引导分区来防止。
  • 某些系统还原应用程序(例如,timeshift)如果 /boot 位于加密分区内,则无法工作 [5]
简而言之,如果需要,创建一个至少 260 MiB 大小的分区。请参阅 分区#/boot

LUKS 加密标头始终存储在设备的开头。由于现有的文件系统通常会分配所有分区扇区,因此第一步是缩小它,为 LUKS 标头腾出空间。

默认的 LUKS2 标头需要 16 MiB。如果当前文件系统占用了所有可用空间,我们将必须至少缩小这么多。要将 /dev/sdxY 上的现有 ext4 文件系统缩小到其当前可能的最小值

# umount /mnt
# e2fsck -f /dev/sdxY
e2fsck 1.46.5 (30-Dec-2021)
Pass 1: Checking inodes, blocks, and sizes
...
/dev/sda6: 12/166320 files (0.0% non-contiguous), 28783/665062 blocks
# resize2fs -p -M /dev/sdxY
e2fsck 1.46.5 (30-Dec-2021)
Resizing the filesystem on /dev/sdxY to 26347 (4k) blocks.
The filesystem on /dev/sdxY is now 26347 (4k) blocks long.
提示: 使用 -M 缩小到最小值可能需要很长时间。您可能希望计算一个比当前大小小 32 MiB 的大小,而不是使用 -M
警告: 文件系统应该在底层设备(例如,分区)保持其原始大小时缩小。某些图形工具(例如,GParted)可能会同时调整文件系统和分区的大小,并且加密后可能会发生数据丢失。

现在我们对其进行加密,使用默认密码,我们不必显式指定它

# cryptsetup reencrypt --encrypt --reduce-device-size 32M /dev/sdxY
WARNING!

========

This will overwrite data on LUKS2-temp-12345678-9012-3456-7890-123456789012.new irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for LUKS2-temp-12345678-9012-3456-7890-123456789012.new: 
Verify passphrase: 

完成之后,整个 /dev/sdxY 分区都被加密了,而不仅仅是文件系统缩小到的空间。作为最后一步,我们扩展原始 ext4 文件系统,使其再次占用所有可用空间,现在是在加密的分区上

# cryptsetup open /dev/sdxY recrypt
Enter passphrase for /dev/sdxY: 
...
# resize2fs /dev/mapper/recrypt
resize2fs 1.43-WIP (18-May-2015)
Resizing the filesystem on /dev/mapper/recrypt to 664807 (4k) blocks.
The filesystem on /dev/mapper/recrypt is now 664807 (4k) blocks long.
# mount /dev/mapper/recrypt /mnt

文件系统现在可以使用了。您可能希望将其添加到您的 crypttab

提示: 如果您刚刚加密了您的根分区,您可能需要执行一些加密后调整。
  1. 配置 mkinitcpio 和内核参数。请参阅 dm-crypt/系统配置#在早期用户空间解锁
  2. 更新 fstab/ 的条目,以使用解锁卷的说明符(例如 UUID)。

重新加密现有的 LUKS 分区

在此示例中,重新加密现有的 LUKS 设备。

警告: 切勿在没有可靠备份的情况下重新加密!如果您因为泄露(例如,肩窥)的密码而重新加密,请在重新加密和/或创建备份之前使用 luksChangeKey

为了使用其当前的加密选项重新加密设备,不需要指定它们

# cryptsetup reencrypt /dev/sdxY

当使用不同的密码和/或哈希重新加密设备时,现有密钥将被保留。

另一个用例是重新加密具有非当前加密选项的 LUKS 设备。在这种情况下,您必须指定所需的新选项。请注意,LUKS2 标头允许针对每个密钥槽的单独加密选项,因此重新加密将仅应用于数据段。

更改 LUKS 标头的能力也可能受到其大小的限制。例如,如果设备最初使用 CBC 模式密码和 128 位密钥大小进行 LUKS1 加密,则 LUKS 标头的大小将是上述 4096 扇区的一半

# cryptsetup luksDump /dev/sdxY | grep -e "mode" -e "Payload" -e "MK bits"
Cipher mode:   	cbc-essiv:sha256
Payload offset:	2048
MK bits:       	128

要升级此类设备的加密选项,请考虑在重新加密之前将标头转换为 LUKS2。如果由于标头大小不足而导致转换失败,您可能必须使用 --reduce-device-size 选项重新加密,以便为更大的 LUKS 标头腾出更多空间,然后再使用所需的新加密重新加密。请记住,这两种方法都带有内在风险:对于转换过程中的标头,以及如果需要使用额外的标头扇区的文件系统数据。

从 LUKS1 转换为 LUKS2 以及返回

cryptsetup 工具具有用于 LUKS1 和 LUKS2 标头格式转换的 convert 操作。建议在转换之前创建 标头备份。参数 --type必需的

从 LUKS1 迁移到 LUKS2

# cryptsetup convert --type luks2 /dev/sdxY
注意: LUKS 标头大小将为 16 MiB 而不是 2 MiB。

回滚到 LUKS1(例如,从 GRUB 与加密的 /boot 启动)

# cryptsetup convert --type luks1 /dev/sdxY
注意: 从 LUKS2 转换为 LUKS1 并非总是可能的。您可能会收到以下错误
Cannot convert to LUKS1 format - keyslot 0 is not LUKS1 compatible.

如果容器正在使用 Argon2,则需要将其转换为 PBKDF2 才能与 LUKS1 兼容。

# cryptsetup luksConvertKey --pbkdf pbkdf2 /dev/sdxY

调整加密设备的大小

此文章或章节需要扩充。

原因: 本节应重写以更通用地介绍调整大小。或许可以与 调整 LVM-on-LUKS 的大小 一起进行。(在 Talk:Dm-crypt/Device encryption 中讨论)

如果使用 dm-crypt 加密的存储设备被克隆(使用诸如 dd 之类的工具)到另一个更大的设备,或者分区被放大或缩小,则必须调整底层文件系统的大小。对于基于 LUKS 的设备,这是唯一必要的步骤,因为 LUKS 不存储任何关于分区大小的信息,但默认情况下使用整个大小,即,当没有传递 --size 参数时。为此,请按照必要的步骤 调整分区大小。以下是包含 ext4 文件系统的加密 LUKS 设备 /dev/sdX2 的示例,该设备需要放大

首先,使用 Partedfdisk 调整底层分区的大小。当缩小分区时,这需要在最后完成。现在,打开您的设备并调整文件系统的大小

警告: 考虑在调整大小之前备份您的数据,并且不要挂载相应的文件系统。
# cryptsetup luksOpen /dev/sdX2 sdX2
# e2fsck /dev/mapper/sdX2
# resize2fs /dev/mapper/sdX2  # Uses all available space on the enlarged LUKS partition

挂载映射的放大 LUKS 设备后,它可以像以前一样使用。

# mount /dev/mapper/sdX2 /mnt/enlarged_sdX2 
注意: cryptsetup-resize 是一个用于调整活动映射分区大小的命令,并且在调整 ext2/3/4 文件系统大小时通常不需要。

LVM on LUKS

请参阅 调整 LVM on LUKS 的大小

环回文件系统

假设加密的环回文件系统存储在文件 /bigsecret 中,环回到 /dev/loop0,映射到 secret 并挂载到 /mnt/secret,如 dm-crypt/加密非根文件系统#文件容器 中的示例所示。

如果容器文件当前已映射和/或已挂载,请卸载和/或关闭它

# umount /mnt/secret
# cryptsetup close secret
# losetup -d /dev/loop0

接下来,将容器文件扩展到您要添加的数据大小。在此示例中,文件将扩展 1 MiB × 1024,即 1 GiB。

警告: 绝对确保使用 oflag=append conv=notrunc 选项,否则您将覆盖文件而不是附加到文件。强烈建议在此步骤之前进行备份。
# dd if=/dev/urandom of=/bigsecret bs=1M count=1024 iflag=fullblock oflag=append conv=notrunc status=progress

现在将容器映射到环回设备

# losetup /dev/loop0 /bigsecret
# cryptsetup open /dev/loop0 secret

之后,将容器的加密部分调整为容器文件的新最大大小

# cryptsetup resize secret

最后,执行文件系统检查,如果没问题,则调整其大小(ext2/3/4 的示例)

# e2fsck -f /dev/mapper/secret
# resize2fs /dev/mapper/secret

您现在可以再次挂载容器

# mount /dev/mapper/secret /mnt/secret

完整性保护设备

如果设备使用完整性支持(例如,--integrity hmac-sha256)格式化,并且后备块设备被缩小,则无法使用此错误打开它:device-mapper: reload ioctl on failed: Invalid argument

为了在不再次擦除设备的情况下解决此问题,可以使用以前的主密钥对其进行格式化(保持每个扇区标签有效)。

# cryptsetup luksDump /dev/sdX2 --dump-master-key --master-key-file=/tmp/masterkey-in-tmpfs.key
# cryptsetup luksFormat /dev/sdX2 --type luks2 --integrity hmac-sha256 --master-key-file=/tmp/masterkey-in-tmpfs.key --integrity-no-wipe
# rm /tmp/masterkey-in-tmpfs.key

密钥文件

注意: 本节描述了如何使用纯文本密钥文件。如果您想加密您的密钥文件,从而为您提供双因素身份验证,请参阅 dm-crypt/特殊用途#使用 GPG、LUKS 或 OpenSSL 加密密钥文件 了解详细信息,但请仍然阅读本节。

什么是密钥文件?

密钥文件是一个文件,其数据用作解锁加密卷的密码。这意味着,如果此类文件丢失或更改,则可能无法再解密卷。

提示: 除了密钥文件之外,还定义一个密码,以便在定义的密钥文件丢失或更改时备份访问加密卷。

为什么要使用密钥文件?

密钥文件有很多种。下面总结了每种类型的密钥文件的优点和缺点

密钥文件的类型

密码

这是一个包含简单密码的密钥文件。这种类型的密钥文件的优点是,如果文件丢失,它包含的数据是已知的,并且希望加密卷的所有者容易记住。然而,这并没有比在初始系统启动期间输入密码增加任何安全性。

示例:correct horse battery staple

注意: 包含密码的密钥文件不能包含换行符。一种选择是使用以下命令创建它
# printf '%s' 'your_passphrase' | install -m 0600 /dev/stdin /etc/cryptsetup-keys.d/keyfile.key

如果文件包含引号或反斜杠等特殊字符,建议直接编辑密钥文件,直接输入或粘贴密码,然后使用方便的 perl 单行命令删除尾随换行符,而不是转义这些字符

# perl -pi -e 'chomp if eof' /etc/cryptsetup-keys.d/keyfile.key

随机字符

这是一个包含随机字符块的密钥文件。这种类型的密钥文件的优点是,它比简单的密码更能抵抗字典攻击。在这种情况下可以利用密钥文件的另一个优点是所用数据的长度。由于这不是一个旨在让人记住以便输入的字符串,因此创建包含数千个随机字符的文件作为密钥是很简单的。缺点是,如果此文件丢失或更改,则很可能无法在没有备份密码的情况下访问加密卷。

随机字符密钥文件可以使用任何字符集,但坚持使用可移植的 ASCII 字母和数字集可以使事情在键盘布局或 Unicode 支持不可靠的情况下更容易,例如在启动 LUKS 解锁阶段键入紧急密码,或使用旧的 Unicode-naive POSIX 实用程序测量密钥文件。您可以像这样生成这样的字符串

$ tr -dc '[:alnum:]' </dev/urandom | head -c64

示例:rTCBW6j1dI2aYC5KcD6Ar38rBGN2DkWyang3RT7pdMGpdf1kRuMXi8EBHKu0BJ8X

或者,随机 (UTF-8) Unicode 字符密钥文件可能如下所示

示例:W‰[�5ODó?Oéµ»9���…¬hjT}­�› DЧíŽ�uLÝæ•�Ýœ�§aþ�óx±)Ñ)l­éeð��•ú=èe

二进制

这是一个已被定义为密钥文件的二进制文件。当识别文件作为密钥文件的候选文件时,建议选择相对静态的文件,例如照片、音乐、视频剪辑。这些文件的优点是它们具有双重功能,这可以使它们更难被识别为密钥文件。密钥文件不是包含大量随机文本的文本文件,而是看起来像普通图像文件或音乐剪辑,对于普通的观察者而言。缺点是,如果此文件丢失或更改,则很可能无法在没有备份密码的情况下访问加密卷。此外,与随机生成的文本文件相比,存在理论上的随机性损失。这是因为图像、视频和音乐在相邻数据位之间存在一些内在关系,而随机文本文件不存在这种关系。然而,这是有争议的,并且从未被公开利用过。

示例:图像、文本、视频,...

使用随机字符创建密钥文件

将密钥文件存储在文件系统上

密钥文件可以是任意内容和大小。

这里使用 dd 生成一个 2048 字节的随机密钥文件,将其存储在文件 /etc/cryptsetup-keys.d/mykeyfile.key

# dd bs=512 count=4 if=/dev/random iflag=fullblock | install -m 0600 /dev/stdin /etc/cryptsetup-keys.d/mykeyfile.key

如果您计划将密钥文件存储在外部设备上,您也可以简单地将输出文件更改为相应的目录

# dd bs=512 count=4 if=/dev/random of=/run/media/user/usbstick/mykeyfile.key iflag=fullblock
安全地覆盖存储的密钥文件

如果您将临时密钥文件存储在物理存储设备上,并且想要删除它,请记住不要只是稍后删除密钥文件,而是使用类似

# shred --remove --zero mykeyfile

来安全地覆盖它。对于旧式文件系统(如 FAT 或 ext2),这已经足够了,但在日志文件系统、闪存硬件和其他情况下,强烈建议擦除整个设备

将密钥文件存储在 tmpfs 中(禁用交换)

或者,您可以挂载一个禁用了交换的 tmpfs 以临时存储密钥文件

# mount --mkdir -t tmpfs -o noswap tmpfs /root/mytmpfs
# cd /root/mytmpfs

优点是它驻留在 RAM 中而不是物理磁盘上,因此在卸载 tmpfs 后无法恢复它。在将密钥文件复制到另一个安全且持久的文件系统后,再次使用以下命令卸载 tmpfs

# umount /root/mytmpfs

配置 LUKS 以使用密钥文件

为密钥文件向 LUKS 标头添加一个密钥槽

# cryptsetup luksAddKey /dev/sda2 /etc/cryptsetup-keys.d/mykeyfile.key
Enter any existing passphrase:

使用密钥文件手动解锁分区

打开 LUKS 设备时使用 --key-file 选项

# cryptsetup open /dev/sda2 dm_name --key-file /etc/cryptsetup-keys.d/mykeyfile.key

在启动时解锁根分区

这只是配置 mkinitcpio 以包含必要的模块或文件,并配置 cryptkey 内核参数 以知道在哪里找到密钥文件的问题。

下面介绍了两种情况

  1. 使用存储在外部介质(例如 USB 闪存盘)上的密钥文件
  2. 使用嵌入在 initramfs 中的密钥文件

使用存储在外部介质上的密钥文件

配置 mkinitcpio

您必须将驱动器的 文件系统 的内核模块添加到 /etc/mkinitcpio.conf 中的 MODULES 数组 中。例如,如果文件系统是 Ext4,则添加 ext4,如果是 FAT,则添加 vfat

MODULES=(vfat)

如果在启动时出现关于坏超级块和坏代码页的消息,则您需要加载额外的代码页模块。例如,您可能需要 iso8859-1 代码页的 nls_iso8859-1 模块。

重新生成 initramfs.

配置内核参数

使用嵌入在 initramfs 中的密钥文件

警告: 仅当您通过以下方式充分保护密钥文件时,才使用嵌入式密钥文件
  • 在启动过程的早期使用某种形式的身份验证。否则,将发生自动解密,完全破坏块设备加密的目的。
  • /boot 已加密。否则,不同安装上的 root 用户(包括 Live 环境)可以从 initramfs 中提取您的密钥,并在没有任何其他身份验证的情况下解锁设备。

此方法允许使用一个特殊命名的密钥文件,该文件将嵌入到 initramfs 中,并由 encrypt 钩子 拾取,以自动解锁根文件系统 (cryptdevice)。当使用 GRUB 早期 cryptodisk 功能时,为了避免在启动期间输入两次密码,这可能很有用。

生成密钥文件,赋予其适当的权限,并将其添加为 LUKS 密钥

# dd bs=512 count=4 if=/dev/random iflag=fullblock | install -m 0600 /dev/stdin /etc/cryptsetup-keys.d/root.key
# cryptsetup luksAddKey /dev/sdX# /etc/cryptsetup-keys.d/root.key
注意: initramfs 由 mkinitcpio 默认使用 600 权限 生成,因此普通用户无法通过生成的 initramfs 读取密钥文件。

将密钥包含在 mkinitcpio 的 FILES 数组

/etc/mkinitcpio.conf
FILES=(/etc/cryptsetup-keys.d/root.key)

重新生成 initramfs.

对于 encrypt 钩子,密钥文件使用 cryptkey= 内核参数指定:对于 initramfs,语法为 rootfs:/path/to/keyfile。默认值为 /crypto_keyfile.bin,如果 initramfs 包含具有此路径的有效密钥,则可以省略 cryptkey。请参阅 dm-crypt/系统配置#cryptkey

对于上面的示例,在使用带有 encrypt 钩子的基于 busybox 的 initramfs 时,设置以下内核参数

cryptkey=rootfs:/etc/cryptsetup-keys.d/root.key

如果改用 sd-encrypt 钩子,则密钥文件使用 rd.luks.key= 内核参数指定:对于 initramfs,语法为 /path/to/keyfile。默认值为 /etc/cryptsetup-keys.d/name.key(其中 name#使用 cryptsetup 加密设备 中用于解密的 dm_name),如果 initramfs 包含具有此路径的有效密钥,则可以省略 rd.luks.key。请参阅 dm-crypt/系统配置#rd.luks.key

在下次重启时,您应该只需要输入一次容器解密密码。

(来源)