如何手动触发 systemd 对加密分区/驱动器的“自动挂载”?

如何手动触发 systemd 对加密分区/驱动器的“自动挂载”?

我设置了两个 LUKS 加密驱动器以在系统启动时自动挂载(使用crypttabLUKS 文件密钥)。它工作正常,但不幸的是,如果我断开其中一个驱动器的连接,当我再次将它们连接到我的电脑(运行 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....devicecryptsetup

/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/generatorsystemd-cryptsetup@<luksvolume>.servicedev-mapper-<luksvolume>.devicex-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/fooand 类似,它会按预期工作。

答案3

另外两个答案非常有用,提供了大量信息和指示。我认为缺少一个部分 - 定义服务.mountsystemd-cryptsetup生成的服务之间的依赖关系。

您可以通过创建符号链接来实现这一点,/etc如下所示。有关单元依赖关系和符号链接的更多信息,请参阅man 系统单元

为了完整起见,以下是整个设置。

因为我通过 USB 机箱将驱动器挂接在远程服务器上,所以即使驱动器或机箱出现故障,我也希望服务器能够启动。如果不需要,可以跳过automountnoauto选项。但我认为无论哪种方式都很好,因为无论如何在没有安装驱动器的情况下都无法访问安装点。这为设置提供了更多的弹性。

  • /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 更直接。

华泰

相关内容