dm-crypt/设备加密
本节介绍如何从命令行手动使用 dm-crypt 来加密系统。
准备[编辑 | 编辑源代码]
在使用 cryptsetup包 之前,请始终确保已加载dm_crypt
内核模块(kernel module)。
Cryptsetup 用法[编辑 | 编辑源代码]
cryptsetup(8) 是与 dm-crypt 交互的命令行工具,用于创建、访问和管理加密设备。该工具后来被扩展为支持依赖 Linux 内核设备映射器和加密模块(Linux kernel device-mapper and the cryptographic modules)的不同加密类型。最显著的扩展是 Linux 统一密钥设置 (LUKS) 扩展,它将 dm-crypt 所需的所有设置信息存储在磁盘本身上,并抽象分区和密钥管理以提高易用性。通过设备映射器访问的设备称为块设备。有关详细信息,请参阅静态数据加密#块设备加密(Data-at-rest encryption#Block device encryption)。
该工具的使用如下:
# cryptsetup action options device dmname
它具有选项和加密模式的编译默认值,如果在命令行上未指定其他值,则将使用这些默认值。看看
$ cryptsetup --help
它按顺序列出了加密模式的选项、操作和默认参数。可以在手册页上找到完整的选项列表。由于需要或可选不同的参数,根据加密模式和操作,以下部分将进一步指出差异。块设备加密速度很快,但速度也很重要。由于在设置后更改块设备的加密密码很困难,因此提前检查各个参数的 dm-crypt 性能很重要:
$ cryptsetup benchmark
可以指导在安装之前决定算法和密钥大小。如果某些 AES 密码以相当高的吞吐量表现出色,那么这些密码可能是 CPU中具有硬件支持的密码。
Cryptsetup 密码和密钥[编辑 | 编辑源代码]
加密的块设备受密钥保护。密钥可能是以下两者之一:
- 密码: 参见 Security#Passwords。
- 密钥文件, 参见 #密钥文件。
两种密钥类型都有默认的最大大小:密码最多可包含 512 个字符,密钥文件最多可包含 8192KiB。
LUKS 在这一点上需要注意的一个重要区别是,该密钥用于解锁 LUKS 加密设备的主密钥,并且可以通过 root 访问权限进行更改。其他加密模式不支持在设置后更改密钥,因为它们不使用主密钥进行加密。有关详细信息,请参阅静态数据加密#块设备加密(Data-at-rest encryption#Block device encryption)。
加密选项[编辑 | 编辑源代码]
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 |
Yes | - | 在 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
Executing it will prompt for a password, which should have very high entropy, and the --verify-passphrase
option can be used but is not a default. In general it is advisable to make exact note of the encryption options used for the creation, because they can not be derived from the encrypted device, or an optional key-file, and upstream defaults may change.
执行它将提示输入密码,该密码应该具有非常高的熵。也可以添加 --verify-passphrase
选项(并非默认值)。一般来说,建议准确记录用于创建加密驱动器的选项,因为它们不能从加密设备或可选密钥文件中获得,而上游(简写的)默认值可能会更改。
选项 | Cryptsetup 2.7.0默认值 | 示例 | 注释 |
---|---|---|---|
--hash
-h |
sha256
|
- | 哈希(hash)用于从密码创建密钥;它不用于密钥文件。 |
--cipher
-c |
aes-xts-plain64
|
aes-xts-plain64
|
密码由三部分组成:cipher-chainmode-IV generator。请参阅 Data-at-rest encryption#Ciphers and modes of operation 了解这些设置的说明,以及DMCrypt 文档了解一些可用选项。 |
--key-size
-s |
256
|
512
|
密钥大小(以位(bit)为单位)。其大小将取决于使用的密码以及使用的链模式。 XTS 需要两倍于 CBC 的密钥大小. |
--size
-b |
目标硬盘的实际大小 | - (默认即可) | 限制设备的最大大小(以 512字节扇区为单位)。 |
--offset
-o |
0
|
0
|
从目标硬盘起始位置(the beginning of the target disk)的偏移量(以512 字节扇区为单位)开始映射。 |
--skip
-p |
0
|
2048 (512B×2048=1MiB将被跳过)
|
要跳过的 512 字节加密数据扇区的数量,用于计算初始向量(IV)。 |
--key-file
-d |
默认使用密码 | /dev/sdZ (或例如 /boot/keyfile.enc )
|
用作密钥的设备或文件。有关详细信息,请参阅#Keyfiles[损坏的链接:无效的章节]。 |
--keyfile-offset | 0
|
0
|
从密钥开始的文件开头(the beginning of the file)的偏移量(以字节为单位)。从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
现在可以检查是否已映射:
# fdisk -l
此时一个条目应当存在于/dev/mapper/enc
。
使用 cryptsetup 加密设备[编辑 | 编辑源代码]
本节介绍如何使用选项来创建新的加密块设备并手动访问它们。
cryptsetup luksFormat --pbkdf pbkdf2
)。使用 LUKS 模式加密设备[编辑 | 编辑源代码]
格式化 LUKS 分区[编辑 | 编辑源代码]
要将分区设置为加密的 LUKS分区,请执行:
# cryptsetup luksFormat device
然后将提示您输入密码并进行验证。
有关命令行选项,请参阅#Encryption options for LUKS mode[损坏的链接:无效的章节]。
您可以通过以下方式检查结果:
# cryptsetup luksDump device
您会注意到,dump 不仅显示密码头信息,还显示用于 LUKS 分区的密钥槽。
以下示例将使用 XTS 模式下的默认 AES 密码和有效的 256 位加密在 /dev/sda1
上创建一个加密的根分区
# cryptsetup luksFormat -s 512 /dev/sda1
使用LUKS来格式化带有密钥文件的分区[编辑 | 编辑源代码]
在创建新的 LUKS 加密分区时,密钥文件可能会在创建时与分区相关联,使用:
# cryptsetup luksFormat device /path/to/mykeyfile
有关如何生成和管理密钥文件的说明,请参阅#密钥文件。
使用设备映射器解锁/映射 LUKS分区[编辑 | 编辑源代码]
一旦创建了 LUKS 分区,就可以解锁它们。
解锁过程将使用设备映射器将分区映射到新的设备名称。这提醒内核,device
实际上是一个加密设备,应该通过 LUKS 使用 /dev/mapper/dm_name
寻址,以免覆盖加密数据。为防止意外覆盖,为了防止意外覆盖,请阅读在完成设置后备份cryptheader的可能性。
为了打开一个加密的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
可以像其他分区一样被挂载(mount)。
要关闭 LUKS 容器,请卸载分区并执行以下操作:
# cryptsetup close root
使用 TPM 存储密钥[编辑 | 编辑源代码]
使用plain模式加密设备[编辑 | 编辑源代码]
创建和后续访问dm-crypt plain模式加密只需要使用cryptsetup open
操作,并使用正确的参数。下面的例子展示了两个非根设备的例子,但通过将两个设备堆叠起来增加了一个quirk(即,第二个是在第一个设备内部创建的)。显然,叠加加密会使开销加倍。这里的用例只是为了演示使用cipher选项的另一个示例。
第一个映射器是使用 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
我们关闭堆栈(stack),以检查访问是否有效
# 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 # 上一次尝试的功能失调的映射器
# 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”中被加密。
仅可用于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 Preferred --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)。
对于上述警告,最好知道我们要保留的密钥是有效的。一个简单的检查是使用 -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 加密分区的)头并将其存储在另一个硬盘上可能是个好主意。
使用 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
现在所有的密钥槽区域都被重写了;发出命令后,只有备份文件中活跃的密钥槽可用。
手动备份和恢复[编辑 | 编辑源代码]
头(header)始终位于设备的开头,并且无需访问 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 以外的模式(例如 plain 模式)的重新加密。
重新加密的一种应用可能是在密码或#密钥文件被泄露并且不能确定(对方)没有获得 LUKS 头的副本后再次保护数据。例如,如果只有一个口令被窃取,但没有发生对设备的物理/逻辑访问,那么只需改变相应的口令/密钥即可(#密钥管理)。
下面展示了一个对未加密的文件系统分区进行加密以及对现有LUKS设备进行重新加密的例子。
加密现有的未加密文件系统[编辑 | 编辑源代码]
/boot
的单独的且未加密的引导分区(请参阅dm-crypt/加密整个系统#准备boot分区)。这不是绝对必要的,但有许多优点:
- 如果
/boot
位于加密的根分区内,系统将在机器开机时要求输入两次密码。第一次发生在引导加载程序尝试读取位于加密/boot
中的文件时,第二次发生在内核尝试挂载加密分区 [4]。这可能不是所需的行为,可以通过使用单独且未加密的引导分区来防止。 - 如果
/boot
位于加密分区,某些系统还原应用程序(例如,timeshift包)将无法运行[5]。
LUKS 加密头始终存储在设备的起始(the beginning of the device)。由于现有文件系统通常会分配所有分区扇区,因此第一步是缩小它以给 LUKS头腾出空间。
默认的LUKS2头需要16兆字节。如果当前的文件系统占据了所有的可用空间,那么我们至少要缩减这么多。要将ext4
上现有的/dev/sdxY
文件系统缩小到目前可能的最小值:
# 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 16M /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/System configuration#Unlocking in early userspace
- 更新 fstab 中的
/
条目,使用解锁卷的标识 (如使用 UUID).
重新加密现有的 LUKS 分区[编辑 | 编辑源代码]
在此示例中,现有的 LUKS 设备被重新加密。
若要使用 LUKS2/1 默认加密选项重新加密设备:
# cryptsetup reencrypt /dev/sdxY
使用不同的密码和/或散列重新加密设备时,会保留现有密钥。另一个用例是重新加密具有非当前加密选项的 LUKS 设备。除了上述关于正确指定选项的警告外,更改 LUKS 头的能力也可能受到其大小的限制。例如,如果设备最初使用 CBC 模式密码和 128 位密钥大小加密,则 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
虽然可以升级这种设备的加密,但目前只能分两步进行。首先,使用相同的加密选项重新加密,但使用--reduce-device-size
选项为更大的 LUKS 头腾出更多空间。其次,使用所需的密码再次重新加密整个设备。由于这个原因以及在任何情况下都应该创建备份的事实,创建一个新的、全新的加密设备来恢复始终是更快的选择。
从LUKS1到LUKS2的转换和回退[编辑 | 编辑源代码]
cryptsetup包包具有convert
选项,用于在 LUKS1 和 LUKS2 容器类型之间进行转换。参数 --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.
If the container is using Argon2, it needs to be converted to PBKDF2 to be LUKS1-compatible.
# cryptsetup luksConvertKey --pbkdf pbkdf2 /dev/sdxY
调整加密设备的大小[编辑 | 编辑源代码]
如果将使用 dm-crypt 加密的存储设备(使用dd 之类的工具)克隆到另一个更大的设备,则必须调整底层 dm-crypt 设备的大小以使用整个空间。
在此示例中,目标设备是 /dev/sdX2,将使用与分区相邻的整个可用空间:
# cryptsetup luksOpen /dev/sdX2 sdX2 # cryptsetup resize sdX2
然后必须调整底层文件系统的大小。
回送文件系统[编辑 | 编辑源代码]
假设一个加密的回送文件系统存储在文件/bigsecret
中,循环到/dev/loop0
,映射到secret
并挂载在/mnt/secret
上,如 dm-crypt/Encrypting a non-root file system#File container中的例子。
如果容器文件当前已映射和/或挂载,请卸载 并且/或着 关闭它:
# umount /mnt/secret # cryptsetup close secret # losetup -d /dev/loop0
接下来,使用要添加的数据大小扩展容器文件。在本例中,文件将扩展 1M * 1024,即1G。
>
,而不是只有一个,否则你会覆盖文件,而不是追加到它。强烈建议在这一步骤之前做一个备份。# dd if=/dev/urandom bs=1M count=1024 iflag=fullblock >> /bigsecret
现在将容器映射到回送设备:
# 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
密钥文件[编辑 | 编辑源代码]
{{注意|本节描述使用明文密钥文件。如果你想加密你的密钥文件给你双重身份验证,请参阅使用 GPG、LUKS 或OpenSSL 加密密钥文件了解详细信息,但仍请阅读本节。]]
什么是密钥文件?
密钥文件是将其数据用作解锁加密卷的密码的文件。 这意味着如果此类文件丢失或更改,则可能无法再解密该卷。
为什么要使用密钥文件?
密钥文件有很多种。所使用的每种类型的密钥文件都有以下总结的优点和缺点:
密钥文件的类型[编辑 | 编辑源代码]
密码[编辑 | 编辑源代码]
这是一个包含简单密码的密钥文件。这种类型的密钥文件的好处是,如果文件丢失,它包含的数据是已知的,并且很容易被加密卷的所有者记住。然而,缺点是这不会增加在初始系统启动期间输入密码的任何安全性。
示例: 1234
{{注意|包含密码短语的密钥文件中不得有换行符。一种选择是使用以下方法来创建它:
# mkdir -m 700 /etc/cryptsetup-keys.d # echo -n 'your_passphrase' > /etc/cryptsetup-keys.d/keyfile.key # chmod 600 /etc/cryptsetup-keys.d/keyfile.key
如果文件包含特殊字符,例如反斜杠,而不是转义这些字符,建议直接编辑密钥文件,直接输入或粘贴密码,然后用方便的perl命令一行删除结尾换行:
# perl -pi -e 'chomp if eof' /etc/cryptsetup-keys.d/keyfile.key
随机文本[编辑 | 编辑源代码]
这是一个包含随机字符块的密钥文件。这种类型的密钥文件的好处是它比简单的密码更能抵抗字典攻击。在这种情况下,可以利用密钥文件的另一个优势,即所用数据的长度。由于这不是要由人记住以供输入的字符串,因此创建包含数千个随机字符作为密钥的文件是微不足道的。缺点是如果此文件丢失或更改,则很可能在没有备份密码的情况下无法访问加密卷。
示例: fjqweifj830149-57 819y4my1-38t1934yt8-91m 34co3;t8y;9p3y-
二进制[编辑 | 编辑源代码]
这是一个已被定义为密钥文件的二进制文件。在将文件识别为密钥文件的候选文件时,建议选择相对静态的文件,例如照片、音乐、视频剪辑。这些文件的好处是它们具有双重功能,使它们更难被识别为密钥文件。与具有大量随机文本的文本文件不同,这种密钥文件对于旁观者来说看起来就像一个普通的图像文件或音乐剪辑。缺点是如果此文件丢失或更改,则很可能在没有备份密码的情况下无法访问加密卷。此外,与随机生成的文本文件相比,理论上存在随机性损失。这是因为图像、视频和音乐在相邻数据位之间具有某种内在关系,而随机文本文件不存在这种关系。然而,这是有争议的,从未被公开利用。
示例: 图片、文本、视频……
使用随机字符创建密钥文件[编辑 | 编辑源代码]
将密钥文件存储在文件系统上[编辑 | 编辑源代码]
密钥文件可以是任意内容和大小。
这里 dd 用于生成 2048 个随机字节的密钥文件,将其存储在文件/etc/cryptsetup-keys.d/mykeyfile.key
中:
# mkdir -m 700 /etc/cryptsetup-keys.d # dd bs=512 count=4 if=/dev/random of=/etc/cryptsetup-keys.d/mykeyfile.key iflag=fullblock
如果你计划将密钥文件存储在一个外部设备上,也可以轻松将输出文件更改为对应的目录:
# dd bs=512 count=4 if=/dev/random of=/run/media/user/usbstick/mykeyfile.key iflag=fullblock
要拒绝除root
以外的其他用户的任何访问:
# chmod 600 /etc/cryptsetup-keys.d/mykeyfile.key
安全地覆盖存储的密钥文件[编辑 | 编辑源代码]
如果你把你的临时密钥文件存储在一个物理存储设备上,并想删除它,记住以后不要只是删除密钥文件,而是使用类似于
# shred --remove --zero mykeyfile
来安全地覆盖它。对于 FAT 或 ext2等过时的文件系统,这已经足够了,而在日志文件系统、闪存硬件和其他情况下,强烈建议擦除整个设备。
将密钥文件存储在 tmpfs 中(关闭 swap)[编辑 | 编辑源代码]
另外,您可以挂载一个 tmpfs(关闭 swap) 来临时存储密钥文件:
# 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内核参数以知道在哪里找到密钥文件。
下面介绍两种情况:
- 使用存储在外部介质(例如 U盘)上的密钥文件
- 使用嵌入在initramfs 中的密钥文件
使用存储在外部介质上的密钥文件[编辑 | 编辑源代码]
配置mkinitcpio[编辑 | 编辑源代码]
您必须将驱动器文件系统的内核模块添加到 /etc/mkinitcpio.conf
中的 MODULES array中。例如,如果文件系统是Ext4,则添加ext4
;如果是FAT,则添加vfat
:
MODULES=(vfat)
如果在启动时有关于坏的超级块和坏的码表的信息,那么你需要加载一个额外的码表模块。例如,你可能需要nls_iso8859-1
模块来加载iso8859-1
编码页。
配置内核参数[编辑 | 编辑源代码]
- 对于使用encrypt钩子的基于busybox的initramfs,参阅dm-crypt/System configuration#cryptkey。
- 对于使用sd-encrypt钩子的基于systemd的initramfs,参阅dm-crypt/System configuration#rd.luks.key。
在 initramfs 中嵌入密钥文件[编辑 | 编辑源代码]
- 在启动过程的早期使用某种形式的身份验证。否则会发生自动解密,完全违背块设备加密的目的。
/boot
已加密。否则,不同安装(包括实时环境(live environment))上的 root 可以从 initramfs 中提取您的密钥,并在没有任何其他身份验证的情况下解锁设备。
此方法允许使用一个特殊命名的密钥文件,该密钥文件将嵌入到 initramfs 中并由 encrypt
钩子拾取以自动解锁根文件系统( cryptdevice
)。在使用 GRUB 早期加密硬盘功能时应用它可能很有用,以避免在引导期间输入两个密码。
生成密钥文件,为其赋予适当的权限并将其添加为 LUKS 密钥:
# mkdir -m 700 /etc/cryptsetup-keys.d # dd bs=512 count=4 if=/dev/random of=/etc/cryptsetup-keys.d/root.key iflag=fullblock # chmod 600 /etc/cryptsetup-keys.d/root.key # cryptsetup luksAddKey /dev/sdX# /etc/cryptsetup-keys.d/root.key
600
,因此普通用户无法通过生成的 initramfs 读取密钥文件。在 mkinitcpio 的 FILES array中括入密钥:
/etc/mkinitcpio.conf
FILES=(/etc/cryptsetup-keys.d/root.key)
对于 encrypt
钩子,密钥文件由 cryptkey=
内核参数指定:在 initramfs,语法为 rootfs:/path/to/keyfile
。默认值为 /crypto_keyfile.bin
,如果 initramfs 包含此路径的有效密钥,则可以省略 cryptkey
。请参阅dm-crypt/System configuration#cryptkey。
对于上述示例,当在 busybox-based initramfs 中使用 encrypt
钩子时,设置内核参数:
cryptkey=rootfs:/etc/cryptsetup-keys.d/root.key
若以 sd-encrypt
钩子替代,密钥文件通过 rd.luks.key=
内核参数指定。默认值是 /etc/cryptsetup-keys.d/name.key
(其中 name
是用于 #使用 cryptsetup 加密设备 中解密的 dm_name)。且当 initramfs 包含此路径的有效密钥时, rd.luks.key
可省略。参阅 dm-crypt/System configuration#rd.luks.key。
在下一次重新启动时,您应该只需要输入一次容器解密密码。
(资料来源)