![如何手动触发 systemd 对加密分区/驱动器的“自动挂载”?](https://linux22.com/image/211292/%E5%A6%82%E4%BD%95%E6%89%8B%E5%8A%A8%E8%A7%A6%E5%8F%91%20systemd%20%E5%AF%B9%E5%8A%A0%E5%AF%86%E5%88%86%E5%8C%BA%2F%E9%A9%B1%E5%8A%A8%E5%99%A8%E7%9A%84%E2%80%9C%E8%87%AA%E5%8A%A8%E6%8C%82%E8%BD%BD%E2%80%9D%EF%BC%9F.png)
我设置了两个 LUKS 加密驱动器以在系统启动时自动挂载(使用crypttab
LUKS 文件密钥)。它工作正常,但不幸的是,如果我断开其中一个驱动器的连接,当我再次将它们连接到我的电脑(运行 Fedora)时,“自动解密”过程不会运行,并且我必须在卸载后手动进行安装。我可以使用什么命令来触发 systemd 在系统启动时触发的相同进程,以便在提示时自动挂载和解密驱动器,而无需我进一步参与?或者,如果做不到这一点,我怎样才能让 systemd 在这种情况下自动解密并安装它们?
作为参考,这是我用来设置驱动器的教程:https://www.golinuxcloud.com/mount-luks-encrypted-disk-partition-linux/
答案1
中的每个条目/etc/crypttab
都会在启动时或运行时由 systemd 自动转换
systemd-cryptsetup-generator
为单位sudo systemctl daemon-reload
。例如,假设 LUKS 文件系统的 UUID 为1111...
(我不会完整显示)条目
mytest /dev/disk/by-uuid/1111... /etc/luks/mykeyfile luks
将生成文件 ,以及依赖项等等。当 UUID 出现在新磁盘上时,该单元就会运行。/run/systemd/generator/[email protected]
BindsTo=dev-disk-by\x2duuid-1111....device
cryptsetup
/etc/fstab
类似地, systemd 中的每个条目都会自动转换systemd-fstab-generator
为一个单位。例如,条目
/dev/mapper/mytest /mnt/mytest ext4 defaults
将生成文件
/run/systemd/generator/mnt-mytest.mount
,该文件(可能通过 udev)将在文件出现时执行挂载/dev/mapper/mytest
(它将由cryptsetup
)创建。
您可以使用以下命令检查这两个单元的状态
systemctl status systemd-cryptsetup@mytest mnt-mytest.mount
通常,当解密和挂载成功完成后,它们将分别显示为
Active: active (exited)
Active: active (mounted)
要干净地删除磁盘,首先给出命令
sudo systemctl stop mnt-mytest.mount
sudo systemctl stop systemd-cryptsetup@mytest
当再次插入该磁盘时,它将自动安装。
如果在未执行此操作的情况下移除已安装的磁盘,则单元可能会处于故障状态。跟踪 systemd 日志以journalctl -f
查看消息。
有时,在拔出而不卸载时,内核会发出有关文件系统上的 I/O 错误的消息,但它会成功卸载文件系统,并且 crypt 分离会成功关闭设备。在这种情况下,当设备重新插入时,它应该自动成功安装,无需干预。
然而,有时,在发生 i/o 错误后,内核决定以只读方式重新挂载文件系统。这会导致 crypt detach 命令出现问题,该命令会失败,因为设备正忙(已挂载,而本应由 systemd 卸载)。似乎存在竞争问题,因为文件系统通常最终会被卸载。当设备重新插入时,解密表明卷已经处于活动状态,因此似乎不会触发安装。
在这种情况下,似乎有效的方法是清除停止作业的失败状态,并手动运行分离命令。当设备再次插入时,解密机制会干净地启动,并且安装完成。命令是
sudo systemctl reset-failed systemd-cryptsetup@mytest
sudo /usr/lib/systemd/systemd-cryptsetup detach mytest
还有一个dev-mapper-mytest.device
可以检查状态的单元。如果分离失败,它将保持活动状态,但在执行上述手动分离命令后将变为非活动状态。
答案2
(有关涵盖 LUKS 之上的附加 LVM 层的相关答案,请参阅:https://serverfault.com/a/1120163/582319)
问题是默认的安装机制没有为任何类型的“动态”连接/拆卸设备设置,虽然 systemd
(与udev
)提供支持此类功能所需的所有机制......
工具
systemctl show <unit>
,systemctl status <unit>
和udevadm info /device/file
一起journalctl -e
对于理解/验证单元如何相互连接是有用/必要的。
问题
systemctl show systemd-crptsetup@<luksvolume>.service
表明默认情况下激活的服务单元luksvolume
只是系统启动过程中的WantedBy=
标准cryptsetup.target
,仅此而已。
(顺便说一句:我建议始终noauto,nofail
为“动态”luks 卷添加 crypttab 选项,因为我们直接负责连接所有部分)。
一个办法
通过 udev 将服务连接到底层设备
udev
我们想要的是每次发现设备被(重新)连接时触发服务单元。这可以通过如下规则来完成udev
:
ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="your-UUID", ENV{SYSTEMD_WANTS}+="systemd-cryptsetup@<luksvolume>.service"
位于/etc/udev/rules.d/
目录中,例如将其命名为99-usbactivation.rules
.重要的是使用设备特定的环境变量和/或属性值(通过查询它们udevadm info /device/name
)。请记住,这==
是一个匹配,而=
(and +=
) 是一个赋值。
一个怪癖(至少在我的设置上)是,systemctl show
甚至udevadm info
经常不显示新创建的wants
关系,但如果/当自动luksvolume
出现在lsblk
...中时,您就会知道事情是否有效。
对设备的进一步检查sytemd-cryptsetup@<luksvolume>.service
表明它是BindsTo=
底层设备,这意味着,只要设备消失,设备就会自动停止,我们无需在这方面采取任何特殊步骤。
通过以下方式将服务连接到挂载点/etc/fstab
fstab
挂载点例如通过 systemd早期通过生成器/mnt/foo
转换为.mount
单位。mnt-foo.mount
翻译的单元本身是可以在其中找到/run/systemd/generator/
(或仅用于systemctl cat <unit>
显示内容)的实际文件。所有 systemd 单元都可以具有相同的基本属性,例如Before=
、After=
、 或Requires=
、Wants=
属性,包括.mount
、.device
、.target
等,并且可以通过常用的systemctl show
等工具进行检查。
对 的任何更改/etc/fstab
,就像对 systemd 单元文件的任何更改一样(例如,通过调用systemctl edit <unit>
或直接编辑文件,/etc/systemd/sytem/
仅在调用后由 systemd 拾取systemctl daemon-reload
。在挂载点的情况下,这将完全重新创建/run/systemd/generator
目录及其包含的文件。
相关的挂载点将/etc/fstab
类似于:
/dev/mapper/<luksvolume> /mnt/foo ext4 defaults 0 2
作为挂载单元的 systemd 文档声明,这通常将单元与足以模拟普通静态/用户触发安装和卸载的 systemd 所需的和排序的属性BindsTo=
连接RequiresMountsFor=
起来After=
。Before=
我们必须添加特殊的 systemd 挂载属性,x-systemd.after=systemd-cryptsetup@<luksvolume>.service
以确保挂载单元等待 cryptsetup 完成其工作。
最后,我们继续由 udev 规则启动的“想要链”(我的术语),以使 cryptsetup 服务“想要启动”我们的挂载单元。这是通过 完成的,它是节元素x-systemd.wanted-by=
的 fstab 等效项,这会导致创建一个带有挂载单元文件符号链接的目录,如 中所示。因为单元的存在只是为了导致单元的创建,所以我们也可以对这个单元进行寻址,例如通过。[Install]
WantedBy=<unit>
<unit>.wants
/run/systemd/generator
systemd-cryptsetup@<luksvolume>.service
dev-mapper-<luksvolume>.device
x-systemd.wanted-by=dev-mapper-<luksvolume>.device
这一切都会产生fstab
如下条目:
/dev/mapper/<luksvolume> /mnt/foo ext4 defaults,x-systemd.wanted-by=dev-mapper-<luksvolume>.device,x-systemd.after=systemd-cryptsetup@<luksvolume>.service 0 2
现在一切都应该“正常工作”,甚至拆卸驱动器也应该导致 systemd 单元有序关闭。
“卸载”
因为所有相关单元都是其底层单元从“自下而上的方向”“想要”的,并且因为单元生命周期通过“BindsTo=”关系(显式或隐式)绑定到底层单元,所以我们可以停用systemd-cryptsetup@<luksvolume>.service
通过systemctl stop
这应该足够了。
另一种可能性是创建并启动自定义“自上而下”[email protected]
单元,其全部目的是Conflicts=
使用相关单元进行现场声明,这意味着如果用户启动目标,则冲突的单元将停止。这是必要的,因为仅仅停用例如安装单元才不是停止 cryptsetup 服务(我们可以通过修改服务单元来实现这一点,例如通过sytemctl edit
并添加BindsTo=
属性)。然而,由于我们的相关单元已经彼此有序地运行并运行,因此可以通过启动目标单元来保证这些单元有序地停止。
这样的目标单元看起来像:
[Unit]
Conflicts=mnt-foo.mount systemd-cryptsetup@<luksvolume>.service
After=mnt-foo.mount systemd-cryptsetup@<luksvolume>.service
变体:直接连接.mount
设备
在我研究如何添加额外的 LVM 层期间(再次参见服务器故障答案),我最终重新连接了我的个人(仅 LUKS,无 LVM 层)设置以直接连接该.mount
单元udev
而不是该[email protected]
单元。 udev 规则现在看起来像:
ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="your-UUID", ENV{SYSTEMD_WANTS}+="mnt-foo.mount"
入口/etc/crypttab
:
<luksvolume> UUID=<uuid> /dir/keyfile luks,nofail,noauto
这使我们能够摆脱正常的/etc/fstab
安装点定义,而不需要任何特殊的x-systemd.<opt>
安装选项。
/dev/mapper/<luksvolume> /mnt/foo ext4 defaults,nofail,noauto 0 2
唯一的烦恼是我们需要附加额外的配置systemd-cryptsetup@<luksvolume>.service
,例如通过调用systemctl edit systemd-cryptsetup@<luksvolume>.service
,默认情况下会生成一个新的conf文件/etc/systemd/system/systemd-cryptsetup@<luksvolume>.service.d/override.conf
,我们在其中添加:
[Unit]
BindTo=mnt-foo.mount
Before=mnt-foo.mount
现在 cryptsetup 服务的生命周期都绑定到底层设备单元也如果支持设备消失,则挂载点的生命周期或者安装点停止(例如根据用户请求),服务也将停止(并且以正确的顺序)。
从概念上讲,这种方法的优点是用户可以忽略 cryptsetup 单元并仅启动/停止安装点。不幸的是,经典mount
命令由于某种原因不起作用(umount
确实),至少在我的发行版上是这样。我建议始终使用systemctl start/stop /mnt/foo
and 类似,它会按预期工作。
答案3
另外两个答案非常有用,提供了大量信息和指示。我认为缺少一个部分 - 定义服务.mount
和systemd-cryptsetup
生成的服务之间的依赖关系。
您可以通过创建符号链接来实现这一点,/etc
如下所示。有关单元依赖关系和符号链接的更多信息,请参阅man 系统单元。
为了完整起见,以下是整个设置。
因为我通过 USB 机箱将驱动器挂接在远程服务器上,所以即使驱动器或机箱出现故障,我也希望服务器能够启动。如果不需要,可以跳过automount
和noauto
选项。但我认为无论哪种方式都很好,因为无论如何在没有安装驱动器的情况下都无法访问安装点。这为设置提供了更多的弹性。
- /etc/fstab(注意:UUID 是在 LV 或 LUKS 卷顶部创建的文件系统的 UUID,具体取决于您如何堆叠设置)
UUID=42a1f3eb-ba45-4e00-996c-a6c492fe345a /media/mymount xfs defaults,x-systemd.automount,nofail 0 0
- /etc/crypttab (注意:UUID 是我们加密的 LV 的;如果您加密的是原始设备,则可能需要指定其路径
/dev/disks/by-*
)
mycryptdevice UUID=3682cae2-cf73-4a5e-8904-b83a9b4c2e47 /etc/luks-keyfile nofail,noauto
* 魔法就在这里 ↓ ↓ ↓ 现在链接服务
sudo mkdir /etc/systemd/system/media-mymount.mount.requires
sudo ln -s /run/systemd/generator/[email protected] /etc/systemd/system/media-mymount.mount.requires/
- 重新加载 systemd 单元和 initrd
sudo systemctl daemon-reload
sudo dracut -f # or whatever your init system requires
创建分区、LVM 逻辑卷、加密分区或 LV、检索 UUID 超出了本问题的范围。只是为了指针,一些有用的命令。
(umask 077 && dd if=/dev/random bs=64 count=1 of=/etc/luks-keyfile)
vgcreate VGName /dev/sdc1 /dev/sdc2
lvcreate --size 200GiB -n lvname
mkfs.xfs -L fslabel /dev/mapper/VGName
cryptsetup luksFormat/luksAddKey/luksUUID/luksDump/open/close
您可以在 LUKS 设备之上创建 VG 或使用 LUKS/cryptsetup 加密 LV。默认情况下,LV 应通过 udev 规则自动激活,因此无需为此创建服务。
对于我的 LV 将跨越多个原始设备的设置,我发现加密 LV 而不是我创建的每个单独的 PV 更直接。
华泰