dm-crypt/设备加密
本节介绍如何从命令行手动使用 dm-crypt 加密系统。
准备
在使用 cryptsetup 之前,请始终确保 dm_crypt
内核模块 已加载。
Cryptsetup 用法
cryptsetup(8) 是一个命令行工具,用于与 dm-crypt 交互,以创建、访问和管理加密设备。该工具后来扩展以支持不同的加密类型,这些类型依赖于 Linux 内核设备-映射器和加密模块。最值得注意的扩展是针对 Linux 统一密钥设置 (LUKS) 扩展,它将 dm-crypt 所需的所有设置信息存储在磁盘本身上,并抽象化分区和密钥管理,以尝试提高易用性。通过设备映射器访问的设备称为块设备。更多信息请参阅 数据静态加密#块设备加密。
该工具的使用方法如下
# cryptsetup action options device dmname
它为选项和加密模式内置了默认值,如果在命令行上未指定其他值,则将使用这些默认值。请查看
$ cryptsetup --help
其中按顺序列出了选项、操作以及加密模式的默认参数。完整的选项列表可以在 man 手册页中找到。由于不同的参数是必需的或可选的,具体取决于加密模式和操作,以下部分将进一步指出差异。块设备加密速度很快,但速度也很重要。由于在设置后更改块设备的加密密码很困难,因此预先检查 dm-crypt 对各个参数的性能非常重要
$ cryptsetup benchmark
可以为安装前决定算法和密钥大小提供指导。如果某些 AES 密码在吞吐量方面表现出色,则这些密码可能是在 CPU 中具有硬件支持的密码。
Cryptsetup 密码和密钥
加密的块设备受密钥保护。密钥可以是
两种密钥类型都有默认的最大大小:密码短语最多可以有 512 个字符,密钥文件最多可以有 8192 KiB。
此时需要注意 LUKS 的一个重要区别是,密钥用于解锁 LUKS 加密设备的主密钥,并且可以使用 root 权限更改。其他加密模式不支持在设置后更改密钥,因为它们不使用主密钥进行加密。有关详细信息,请参阅 数据静态加密#块设备加密。
加密选项
Cryptsetup 支持不同的加密操作模式以与 dm-crypt 一起使用
--type luks
用于使用默认的 LUKS 格式版本 (cryptsetup < 2.1.0 时为 LUKS1,cryptsetup ≥ 2.1.0 时为 LUKS2),--type luks1
用于使用 LUKS1,LUKS 的早期版本,--type luks2
用于使用 LUKS2,LUKS 的当前版本,引入了额外的扩展,--hw-opal-only
用于在支持 TCG OPAL 标准的驱动器上进行基于硬件的加密。请参阅 自加密驱动器#使用 cryptsetup 和 cryptsetup(8) § SED (SELF ENCRYPTING DRIVE) OPAL EXTENSION。--hw-opal
用于 OPAL 硬件加密,并与 dm-crypt 软件加密分层。
--type plain
用于使用 dm-crypt plain 模式,--type loopaes
用于 loopaes 传统模式,--type tcrypt
用于 TrueCrypt 兼容模式。--type bitlk
用于 BitLocker 兼容模式。请参阅 cryptsetup(8) § BITLK (WINDOWS BITLOCKER COMPATIBLE) EXTENSION。
可用于所有模式的基本加密选项(用于加密密码和哈希)依赖于内核加密后端功能。所有已加载并在运行时可用作选项的都可以使用以下命令查看:
$ less /proc/crypto
$ cryptsetup benchmark
,这将触发加载可用的模块。以下介绍 luks
、luks1
、luks2
和 plain
模式的加密选项。请注意,表格列出了本文各自示例中使用的选项,而不是所有可用的选项。
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 版本 将默认值更改为 XTS 模式下的 AES 密码(请参阅 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 操作相关,例如 luksFormat 或 luksAddKey 。将参数指定为 0 会选择内置的默认值。 |
--use-urandom | --use-urandom
|
--use-random
|
选择要使用的 随机数生成器。请注意,/dev/random 阻塞池已被移除。因此,--use-random 标志现在等同于 --use-urandom 。 |
--verify-passphrase -y |
是 | - | 在 Arch Linux 中,对于 luksFormat 和 luksAddKey 默认启用。 |
--sector-size | 512 或 4096 (取决于设备) |
4096
|
设置用于磁盘加密的扇区大小(以字节为单位)。对于报告自身为 4Kn 或 512e 的块设备,默认值为 4096 ,对于报告自身为 512n 的块设备,默认值为 512 。将扇区大小从 512 字节增加到 4096 字节可以在大多数现代存储设备上提供更好的性能。请参阅 高级格式#dm-crypt。 |
LUKS 特性和选项的属性在 LUKS1 (pdf) 和 LUKS2 (pdf) 规范中描述。
迭代时间
来自 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 生成器。有关这些设置的说明,请参阅 数据静态加密#密码和操作模式,有关可用选项的更多信息,请参阅 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 字节可以在大多数现代存储设备上提供更好的性能。请参阅 高级格式#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
现在我们可以检查是否已完成映射
# fdisk -l
现在应该存在 /dev/mapper/enc
的条目。
使用 cryptsetup 加密设备
本节介绍如何使用选项来创建新的加密块设备并手动访问它们。
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 存储密钥
请参阅 可信平台模块#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”。
LUKS 特定的 Cryptsetup 操作
密钥管理
可以为 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
添加一个新的密钥槽。否则,它会提示输入新的密码短语。要使用现有的密钥文件授权操作,--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 ...
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 加密分区的标头被破坏,您将无法解密您的数据。这与忘记密码短语或损坏用于解锁分区的密钥文件一样令人困惑。损坏可能是由于您稍后重新分区磁盘时自己的错误或第三方程序误解分区表而发生的。因此,备份标头并将其存储在另一个磁盘上可能是一个好主意。
使用 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 也可以执行备份。首先,您必须找出加密分区的有效负载偏移量
# 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
重新加密设备
cryptsetup 的 reencrypt
操作允许重新加密 LUKS 设备。对于 LUKS2 设备,可以执行在线重新加密,支持多个并行重新加密作业,并且具有系统故障恢复能力。LUKS1 设备的重新加密只能离线(卸载)执行,并且是单进程的,恢复能力较差。
有关操作模式和选项,请参阅 cryptsetup-reencrypt(8)。
可以更改 #LUKS 模式的加密选项。它也可以用于将现有的未加密文件系统转换为 LUKS 加密的文件系统,或永久删除设备上的 LUKS 加密(使用 --decrypt
;请参阅 移除系统加密)。重新加密对于分离的 LUKS 标头也是可行的,但请注意 --header
选项的警告。不支持重新加密 LUKS 以外的模式(例如纯模式)。
重新加密的一个应用可能是,在口令或 密钥文件 泄露并且不能确定 LUKS 标头的副本是否已被获取后,重新保护数据。例如,如果只是口令被肩窥,但没有发生对设备的物理/逻辑访问,那么仅更改相应的口令/密钥就足够了(#密钥管理)。
以下示例展示了如何加密现有的未加密文件系统分区以及重新加密现有的 LUKS 设备。
加密现有的未加密文件系统
/boot
(请参阅 Dm-crypt/加密整个系统#准备启动分区)。这并非绝对必要,但有很多优点- 如果
/boot
位于加密的根分区内,则系统在机器启动时会要求输入两次口令。第一次发生在引导加载程序尝试读取位于加密的/boot
内的文件时,第二次发生在内核尝试挂载加密分区时 [4]。这可能不是期望的行为,可以通过拥有一个单独的未加密的启动分区来避免。 - 某些系统还原应用程序(例如,timeshift)如果
/boot
位于加密分区内将无法工作 [5]。
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
。现在我们对其进行加密,使用默认密码,我们不必显式指定它
# 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。
- 配置 mkinitcpio 和内核参数。请参阅 dm-crypt/系统配置#在早期用户空间解锁。
- 更新 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 以及从 LUKS2 转换回 LUKS1
cryptsetup 工具具有用于 LUKS1 和 LUKS2 标头格式转换的 convert
操作。建议在转换前创建 标头备份。参数 --type
是必需的。
从 LUKS1 迁移到 LUKS2
# cryptsetup convert --type luks2 /dev/sdxY
回滚到 LUKS1(例如,从 带有加密 /boot 的 GRUB 启动)
# cryptsetup convert --type luks1 /dev/sdxY
Cannot convert to LUKS1 format - keyslot 0 is not LUKS1 compatible.
如果容器正在使用 Argon2,则需要将其转换为 PBKDF2 才能与 LUKS1 兼容。
# cryptsetup luksConvertKey --pbkdf pbkdf2 /dev/sdxY
调整加密设备的大小
如果使用 dm-crypt 加密的存储设备被克隆(使用 dd 等工具)到另一个更大的设备,或者分区被扩大或缩小,则必须调整底层文件系统的大小。对于基于 LUKS 的设备,这是唯一必要的步骤,因为 LUKS 不存储任何关于分区大小的信息,但默认情况下使用整个大小,即,当没有传递 --size
参数时。为此,请按照必要的步骤 调整分区大小。以下是一个关于加密的 LUKS 设备 /dev/sdX2
的示例,该设备包含一个 ext4 文件系统,需要扩大大小
首先,使用 Parted 或 fdisk 调整底层分区的大小。当缩小分区时,这需要最后完成。现在,打开您的设备并调整文件系统的大小
# 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
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
密钥文件
什么是密钥文件?
密钥文件是一个文件,其数据用作解锁加密卷的口令。这意味着如果这样的文件丢失或更改,则可能无法再解密该卷。
为什么要使用密钥文件?
密钥文件有很多种。使用的每种类型的密钥文件都有优点和缺点,总结如下
密钥文件类型
口令
这是一个包含简单口令的密钥文件。这种类型的密钥文件的优点是,如果文件丢失,它包含的数据是已知的,并且希望加密卷的所有者可以轻松记住。然而,这并没有比在初始系统启动期间输入口令增加任何安全性。
示例: 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 不敏感的 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 内核参数 以知道在哪里找到密钥文件。
下面涵盖两种情况
- 使用存储在外部介质(例如 USB 驱动器)上的密钥文件
- 使用嵌入在 initramfs 中的密钥文件
使用存储在外部介质上的密钥文件
配置 mkinitcpio
您必须将驱动器的 文件系统 的内核模块添加到 /etc/mkinitcpio.conf
中的 MODULES 数组 中。例如,如果文件系统是 Ext4,则添加 ext4
,如果是 FAT,则添加 vfat
MODULES=(vfat)
如果在启动时出现关于坏超级块和坏代码页的消息,那么您需要加载一个额外的代码页模块。例如,您可能需要 iso8859-1
代码页的 nls_iso8859-1
模块。
配置内核参数
- 对于使用 encrypt hook 的基于 busybox 的 initramfs,请参阅 dm-crypt/系统配置#cryptkey。
- 对于使用 sd-encrypt hook 的基于 systemd 的 initramfs,请参阅 dm-crypt/系统配置#rd.luks.key。
使用嵌入在 initramfs 中的密钥文件
- 在启动过程的早期使用某种形式的身份验证。否则,将发生自动解密,完全破坏块设备加密的目的。
/boot
已加密。否则,不同安装上的 root(包括 live 环境)可以从 initramfs 中提取您的密钥,并在没有任何其他身份验证的情况下解锁设备。
此方法允许使用特别命名的密钥文件,该密钥文件将嵌入到 initramfs 中,并由 encrypt
hook 拾取,以自动解锁根文件系统(cryptdevice
)。当使用 GRUB early 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
600
权限 生成,因此普通用户无法通过生成的 initramfs 读取密钥文件。将密钥包含在 mkinitcpio 的 FILES 数组 中
/etc/mkinitcpio.conf
FILES=(/etc/cryptsetup-keys.d/root.key)
对于 encrypt
hook,密钥文件通过 cryptkey=
内核参数指定:对于 initramfs,语法为 rootfs:/path/to/keyfile
。默认值为 /crypto_keyfile.bin
,如果 initramfs 包含具有此路径的有效密钥,则可以省略 cryptkey
。请参阅 dm-crypt/系统配置#cryptkey。
对于上面的示例,在使用带有 encrypt
hook 的基于 busybox 的 initramfs 时,设置以下内核参数
cryptkey=rootfs:/etc/cryptsetup-keys.d/root.key
如果改用 sd-encrypt
hook,则密钥文件通过 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。
在下次重启时,您应该只需要输入一次容器解密口令。
(来源)