wzyboy’s blog

wzyboy's blog

马上订阅 wzyboy’s blog RSS 更新: https://wzyboy.im/feed.xml

从 LUKS 迁移到 LUKS + LVM

2024年5月10日 23:46

本文记录一下我最近从 LUKS 迁移到 LUKS + LVM 的过程。整理是最好的复习!

背景

Device mapper 是 Linux 里将块设备映射成虚拟块设备的框架。

dm-crypt 是用 DM 进行透明加密的组件。例如:将 /dev/sda2 映射成 /dev/mapper/cryptsda2,则往 /dev/mapper/cryptsda2 这个块设备写入的数据会被加密后实际写入下层的 /dev/sda2 块设备里。

LUKS 是以 dm-crypt 为基础,增加了密钥管理功能的加密实现。

我的笔记本电脑是这样的分区结构:

NAME             MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
nvme0n1          259:0    0 476.9G  0 disk
├─nvme0n1p1      259:1    0   512M  0 part  /boot
├─nvme0n1p2      259:2    0 450.0G  0 crypt /
└─nvme0n1p3      259:3    0  26.4G  0 crypt [SWAP]

一个引导分区(p1),一个主分区(p2),一个交换分区(p3)。其中主分区是用 LUKS 加密的,需要我每次开机时输入密码进行解锁;交换分区是以 plain dm-crypt 加密的,其密钥来自 /dev/urandom 提供的随机数据。交换分区以随机密钥进行加密是一种常见做法,密钥只存在于 RAM 里,关机之后交换分区就完全无法解密了,防止 RAM 里的敏感数据留在交换分区里被读取——尤其是在异常关机的时候。

休眠、但是醒不来

这样的加密交换分区有一个问题:无法休眠,确切地讲是能休眠,但是永远醒不来——因为断电之后密钥已经被丢弃了,所以重新开机的时候系统无法读取交换分区里的数据,所以无法恢复到之前的状态。

由于我的笔记本电脑几乎一直是插着电用,所以十几年来很少用到休眠的功能(更别提以前 Linux 休眠醒来之后网卡、扬声器等容易出 bug);偶尔要带出门的话,短时间我就睡眠,长时间我就直接关机。直到最近,由于一连串巧合,我的笔记本电脑没有插电,电池耗尽,系统自动尝试进入休眠状态——成功了,然后就再也醒不来了。

更糟糕的是,systemd 在系统启动时会等待交换分区出现,但因为永远等不到,所以会浪费两分钟等待直到超时。由于「从休眠中醒来」这个任务没有完成,所以下次重启电脑的时候,systemd 还会再等两分钟超时,周而复始。

我实在是不知道如何清除掉这个 flag 让它不要再等。最终我想了个解决方案:systemd 是根据 UUID 去找交换分区的,那我新建一个喂给它不就行了?于是我从日志里找到 systemd 苦等的 UUID,再用 mkswap -U that_uuid 建立一个 swap,再重启一次,果然它就不再等了。事成之后要记得 wipefs 擦掉分区签名,否则以后就一直是明文交换分区了。

结果这样的事情又发生了几次,每次我都要重启几次来修复。痛定思痛,我决定一劳永逸地解决这个问题,不然每次(不小心)触发休眠就会很麻烦。

思路

我调研了多种方法,在虚拟机里尝试了一番,发现最简单的方法是用 LUKS + LVM

LVM 也是基于 DM 的组件,主要用于在块设备上映射出多个逻辑卷(LV),这些 LV 类似于分区,但是不受分区表的限制,可以灵活地调整,甚至可以提供快照等功能。由于我的某些执念,我一直不愿意将两个 DM 套娃使用,因此我笔记本电脑上只有 LUKS,而 NAS 上只有 LVM。实际在虚拟机里测试了一下,发现 DM 叠叠乐也没什么大不了的,因此也就接受了。至于是将 LVM 叠在 LUKS 上,还是将 LUKS 叠在 LVM 上——当然是前者,因为后者和我现在的处境没什么太大的区别,并不能解决加密交换分区的问题。

所以我的分区调整计划是(对照前文的分区结构):

  1. 将 450 GiB 的 p2 和 26.4 GiB 的 p3 两个分区删除,建立一个新的 476.4 GiB 的 p2 取而代之;
  2. 将新的 p2 作为 LUKS 容器,但不在里面直接创建文件系统,而是再叠一层 LVM,在里面创建两个 LV,一个作为主分区(root),一个作为交换分区(swap)。

根据我在虚拟机里的试验,这样调整过后,开机时系统会先解开 LUKS,然后按需求(全新启动还是从休眠中恢复)读取 root 和/或 swap。

实施

在折腾这些分区之前得先备份数据。我的主分区是 450 GiB,但只有 235 GiB 数据在里面。我有块 500 GB 的 SSD 移动硬盘可以暂存数据,这块移动硬盘里最大的一个分区是 377 GiB,倒是够存;但这分区没有加密,而且是 exFAT 文件系统,因此不适合把笔记本电脑里的数据直接复制进去。

那我把整个 LUKS 容器给 dd 进去?这就又太大了,而且由于加密数据块的信息熵极高,所以再怎么压缩也是塞不下的。要不在移动硬盘里创建一个 LUKS 容器然后把文件系统整个倒进去?但是我的笔记本电脑用的是 Ext4 文件系统,似乎并没有像 xfs_copy 那样只复制有用数据块的工具。我灵机一动,想到可以用 BorgBackup 直接直接读取 LUKS 解密后的明文块设备,由于 Borg repo 是加密的,所以可以存到不加密的移动硬盘里。

BorgBackup 甚至专门有个文档解释了这种用法:

  1. 首先用...

剩余内容已隐藏

查看完整文章以阅读更多