dm-crypt/交换分区加密 - Arch Linux 中文维基

跳转到内容

# dm-crypt/交换分区加密

来自 Arch Linux 中文维基
< Dm-crypt

翻译状态：

- 本文（或部分内容）译自 Dm-crypt/Swap encryption，最近一次同步于 2025-07-25，若英文版本有所更改，则您可以帮助同步与翻译更改的内容。
- 您可以在 ArchWiki 的对应页面找到本文翻译的原始修订历史。
- 本文可能与英文原文存在出入。

本文介绍了多种加密交换分区的方法，可满足不同的需求。若在每次重启时都重新对交换分区进行加密（使用不同密钥）的方式，可避免换出到交换分区上的敏感文件片段因未被覆盖而长期存在，从而更好地保护数据。然而，若使用重新加密，则无法使用休眠功能。

## 无需休眠支持

若无需使用休眠功能，则可以通过配置`/etc/crypttab`，在每次启动时以随机密码，使用dm-crypt plain模式进行解密。在关机时，随机密码将被丢弃，设备上只含有加密的、无法访问的交换数据。

要使用该配置，只需要取消注释`/etc/crypttab`中以`swap`开头的一列，并修改“设备”（device）参数指向交换分区，例如：

    /etc/crypttab

    # <name>   <device>            <password>          <options>
    swap      /dev/sdX#    /dev/urandom   swap,cipher=aes-xts-plain64,size=512,sector-size=4096

上述配置将解密`/dev/sdX#`，并映射到`/dev/mapper/swap`。之后，像普通的swap分区一样，将`/dev/mapper/swap`添加到`/etc/fstab`中，即可完成交换分区配置。若之前已经配置了非加密的交换分区，需要将其禁用（或直接将其在fstab中的条目指向`/dev/mapper/swap`）。上面给出的选项应当足以应对大多数场景。若需要了解每个选项的详细解释，可参见crypttab(5)、point cryptsetup FAQ 2.3。

警告：上述配置中“设备”参数对应的设备的数据将被永久清除。不要使用内核的简单命名（例如，`/dev/sda`, `/dev/sdb`）指定设备，简单命名的顺序可能在每次启动时都不同，导致设备数据被意外清除。建议使用如下方式指定：

- 使用`by-id`、`by-path`。然而，当出现硬件变化时，两者仍可能会改变，详见块设备持久化命名#通过_id_和_通过路径。
- 使用PARTLABEL、PARTUUID。
- 使用LVM逻辑卷名称。
- 使用#文件系统UUID和文件系统标签中介绍的方法。由于每次启动时，都将重新加密设备，并使用`mkswap`重新格式化swap分区， 不能直接使用文件系统标签和文件系统UUID，参见cryptsetup FAQ。但可通过间接手段达成目的。

注意：有时，交换分区配置可能失败，参见systemd issue 10179。

若要使用`by-id`持久化命名，首先，找到swap分区对应的设备：

    # find -L /dev/disk -samefile /dev/sdaX

    /dev/disk/by-id/ata-WDC_WD2500BEVT-22ZCT0_WD-WXE908VF0470-partX
    /dev/disk/by-id/wwn-0x60015ee0000b237f-partX

之后，修改配置文件，使用持久化引用（若上述命令返回多个结果，任选一个即可）：

    /etc/crypttab

    # <name>  <device>                                                         <password>     <options>
    swap      /dev/disk/by-id/ata-WDC_WD2500BEVT-22ZCT0_WD-WXE908VF0470-partX  /dev/urandom   swap,cipher=aes-xts-plain64,size=512,sector-size=4096

重启并激活加密交换后，使用`swapon -s`命令可以看到交换分区对应映射后的设备（例如，`/dev/dm-1`）；使用`lsblk`命令，可看到交换分区的`FSTYPE`为crypt。由于每次启动时都重新加密并格式化交换分区，`/dev/mapper/swap`的文件系统UUID会在每次启动时变化。

注意：若swap分区之前曾用作LUKS加密卷，则crypttab不会覆盖该LUKS加密卷以创建加密的swap分区。这是一项安全措施，用于避免在crypttab中错误指定swap分区对应的设备时，造成数据丢失。若确定要使用之前的LUKS加密卷作为swap分区，必须先覆盖LUKS头。

### 文件系统UUID和文件系统标签

在crypttab中使用内核简单命名（例如，`/dev/sdX#`）、ID命名（例如，`/dev/disk/by-id/ata-SERIAL-partX`）指定加密交换分区的目标设备有较大风险，设备名称、分区结构的细微变化都可能导致指向错误的设备，从而造成数据丢失。使用PARTLABEL、PARTUUID（与分区，而不是文件系统、设备绑定）的风险相对较小，但若打算将目标设备用作其它用途，而忘记移除crypttab中的条目，数据仍可能被覆盖（文件系统改变，分区不改变）。

更可靠的方法是使用文件系统标签或文件系统UUID。但在默认情况下，每次重启时，dm-crypt和`mkswap`都将覆盖目标分区上的内容，导致文件系统标签和文件系统UUID被移除。不过，可以指定swap分区的起始偏移量，以在设备开始处放置另一个空的、容量较小的文件系统。该文件系统仅用于提供持久性的文件系统UUID和文件系统标签。

使用指定文件系统标签创建文件系统：

    # mkfs.ext2 -L cryptswap /dev/sdX# 1M

设备名后的`1M`参数限制文件系统的大小为1MiB，为之后加密的交换分区留出空间。

    # blkid /dev/sdX#

    /dev/sdX#: LABEL="cryptswap" UUID="b72c384e-bd3c-49aa-b7a7-a28ea81a2605" TYPE="ext2"

这样，无论设备名称、分区数量如何变化，始终可以使用文件系统UUID或文件系统标签指向`/dev/sdX#`。

接下来，配置`/etc/crypttab`和`/etc/fstab`条目即可：

    /etc/crypttab

    # <name> <device>         <password>    <options>
    swap     LABEL=cryptswap  /dev/urandom  swap,offset=2048,cipher=aes-xts-plain64,size=512,sector-size=4096

注意偏移量的指定（`offset=2048`），偏移量的单位为512字节的扇区（不受dm-crypt扇区大小的影响），因此`offset=2048`相当于1MiB，跳过了提供UUID的文件系统的区域，因此不影响文件系统UUID和文件系统标签，并正确处理了数据对齐。

    /etc/fstab

    # <filesystem>    <dir>  <type>  <options>  <dump>  <pass>
    /dev/mapper/swap  none   swap    defaults   0       0

在本配置中，只会尝试使用特定文件系统标签的设备作为加密交换分区，而不受设备名称影响。若打算将加密交换分区用作其它用途，当格式化时，文件系统标签被覆盖，因此`/etc/crypttab`不会在下次启动时意外覆盖新的文件系统及数据。

### 在桌面环境中关闭休眠

桌面环境可能无法自动检测到swap分区已被随机密码加密，不能用作休眠，仍错误显示休眠按钮。

若使用Xfce，可通过如下命令隐藏“休眠”、“混合睡眠”按钮：

    $ xfconf-query -c xfce4-session -np /shutdown/ShowHibernate -t bool -s false
    $ xfconf-query -c xfce4-session -np /shutdown/ShowHybridSleep -t bool -s false

## 需要休眠支持

下面介绍在需要进行休眠的情况下，设置加密swap的三种方法。若已经使用其中一种方法完成设置，需要注意系统换出的敏感数据可能在交换分区/交换文件中保留较长时间，直到被覆盖。若要降低相关风险，可以设置系统任务，在特定时机（例如，系统关机时）重新加密交换分区/重置交换文件。

### 使用交换文件

交换文件可放置在加密设备的文件系统中。依照Swap#交换文件中的方法创建交换文件，并完成休眠设置即可。

若采用加密根文件系统的配置，并使用基于systemd的initrd，则配置加密交换文件非常简单、灵活。在完成配置加密的根文件系统后，只需要按常规方式创建并激活交换文件，并将其添加到`/etc/fstab`中即可，无需额外配置。

注意：在设置`resume=`内核参数时，注意其必须指向解密后产生的映射设备，该映射设备包含存储有交换文件的文件系统。

### 使用交换分区

注意：若要重用当前正在被系统使用的交换分区，必须先以root身份执行`swapoff /dev/device`将其禁用，并在/etc/crypttab中移除相关条目。

使用cryptsetup-luksFormat(8)创建容纳交换分区的加密容器：

    # cryptsetup luksFormat --label swap /dev/device

打开该容器，并映射到`/dev/mapper/swap`：

    # cryptsetup open /dev/disk/by-label/swap swap

在映射设备上创建swap文件系统：

    # mkswap /dev/mapper/swap

若不使用Systemd#GPT分区自动挂载，需要将映射设备加入`/etc/fstab`中：

    /dev/mapper/swap none swap defaults 0 0

要启用休眠，添加`resume=/dev/mapper/swap`内核参数。详见电源管理/挂起与休眠#传递休眠位置到_initramfs。

#### 使用TPM

通过在TPM中存储密钥，可自动完成交换分区的加密与解密。

可以使用systemd-cryptenroll将密钥注册到LUKS容器和TPM中。之后，清除之前创建的密码槽位：

    # systemd-cryptenroll --tpm2-device auto /dev/device
    # systemd-cryptenroll --wipe-slot password /dev/device

可通过如下命令验证结果：

    # systemd-cryptenroll /dev/device
    SLOT TYPE
       0 tpm2

#### 在initramfs中解密分区

若要从加密的交换分区中唤醒，必须在initramfs中解密该分区。

##### mkinitcpio

###### 基于systemd的initramfs

若使用基于systemd的inintramfs（使用sd-encrypt钩子），则可使用如下任意一个方案：

- 正确设置`rd.luks.uuid=`内核参数以解密交换分区；
- 编辑`crypttab.initramfs`，并重新生成 initramfs。

例如，若使用TPM加密交换分区：

    /etc/crypttab.initramfs

    swap UUID=56f8ee97-54b3-4a65-9282-688deb922527 none tpm2-device=auto

###### 基于busybox的initramfs

若使用基于busybox的initramfs（使用encrypt钩子），需进行如下操作。

若交换分区所在设备与根文件系统的不同，则其不会被`encrypt`钩子打开。由于从休眠中唤醒的过程在读取`/etc/crypttab`前进行，不能在`/etc/crypttab`中进行配置以打开加密的交换分区，而需要修改`/etc/mkinitcpio.conf`并创建自定义钩子，以在唤醒开始之前打开加密的交换分区。

注意：本节只适用于`encrypt`钩子，其只能解密单个设备（archlinux/mkinitcpio/mkinitcpio#231）；若使用`sd-encrypt`钩子，则可以解密多个设备（包括加密的交换分区），详见Dm-crypt/系统配置#systemd-gpt-auto-generator。

mkinitcpio-openswapAUR中包含了所需的钩子及配置文件。

警告：在系统休眠且还未恢复时，在initramfs中挂载文件系统非常危险且可能导致数据丢失。若使用密钥文件解密，密钥文件不应保存到任何休眠时已挂载的文件系统上。
注意：为了降低文件系统损坏的可能性，上述包中的脚本使用了`-o ro,noload`挂载选项。这些选项适用于ext4文件系统，若使用其它文件系统，需修改`openswap.conf`，使用作用相似的选项。

将`openswap`钩子添加到`/etc/mkinitcpio.conf`的`HOOKS`数组中，注意应当添加到`filesystem`钩子之前，`encrypt`钩子之后。再在`openswap`钩子之后添加`resume`钩子。

    HOOKS=(... encrypt openswap resume filesystems ...)

最后，重新生成 initramfs。

启动时，`openswap`钩子将打开加密的交换分区，以便内核从中唤醒。若使用自定义钩子进行唤醒操作，需确保在`HOOKS`数组中，相关钩子放置在`openswap`之后。注意，由于在initrd阶段便挂载了交换分区，`/etc/crypttab`中无需包含交换分区相关条目。

##### dracut

创建密钥文件：

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

将密钥文件添加到LUKS卷：

    # cryptsetup luksAddKey /dev/device /etc/cryptsetup-keys.d/swap.key

修改dracut配置，加入`resume`模块，并将`swap.key`文件加入到initramfs中（详见Dracut#休眠）：

    /etc/dracut.conf.d/resume-from-hibernate.conf

    add_dracutmodules+=" resume "
    install_items+=" /etc/cryptsetup-keys.d/swap.key "

重新生成 initramfs。

在内核参数中添加与交换分区对应的`rd.luks.name`、`rd.luks.key`参数。

修改后的内核命令行应当与下例相似：

    kernel /vmlinuz-linux cryptdevice=/dev/sda2:root root=/dev/mapper/root resume=/dev/mapper/swap rd.luks.name=fd839505-3213-4603-9a70-c5a96a24768f=swap rd.luks.key=/etc/cryptsetup-keys.d/swap.key ro

### 在LUKS上配置LVM

若交换分区位于LVM卷组内，且对应卷组在initramfs中被激活，只需要按照电源管理/挂起与休眠#休眠中的步骤进行配置即可。

## 已知问题

- 系统日志中出现形如`Stopped (with error) /dev/dm-1`的错误。详见systemd issue 1620。

分类：​

- 静态数据加密

dm-crypt/交换分区加密
添加话题