I’m using disko and preOpenCommands coupled with postOpenCommands to load a keyfile from a usb stick, unlock LUKS partition and then delete the copied keys. While trying to switch to 26.05, I’ve gotten:
error:
Failed assertions:
- boot.initrd.luks.devices..preOpenCommands and postOpenCommands is not supported by systemd stage 1. Please bind a service to cryptsetup.target or cryptsetup-pre.target instead.
Here’s the relevant config part:
keyFile = "/keys/host.key";
preOpenCommands = ''
mkdir -m 0755 -p "/keys"
if waitDevice "/dev/disk/by-id/usb-_USB_DISK...-part1"; then
mount -n -t vfat -o ro "/dev/disk/by-id/usb-_USB_DISK...-part1" /keys
else
echo "USB key not found."
fi
'';
postOpenCommands = ''
mountpoint -q /keys && umount /keys
[ -d /keys ] && rm -rf /keys
'';
I’m not good with systemd. I kinda understand that it’s something like a load order of systemd core module, but how do I “bind a service to cryptsetup.target” by using nix, and, if possible, how do I put it in my disko config?
I removed the entirety of preOpenCommands and postOpenCommands from within disko config, and added:
boot.initrd.systemd.mounts = [
what = "ID=usb-_USB_DISK...-part1";
where = "/keys";
type = "vfat";
];
It allowed me to upgrade to 26.05, but the boot process throws:
Mounting /keys...
Failed to mount /keys.
Dependency failed for Cryptography Setup for cryptroot.
Dependency failed for /dev/mapper/cryptroot.
Dependency failed for Initrd Root Device.
Dependency failed for /sysroot/nix.
Dependency failed for Initrd File Systems.
Dependency failed for Find NixOS closure.
Dependency failed for /sysroot.
Dependency failed for /sysroot/run.
Dependency failed for Initrd Root File System.
Dependency failed for Local Encrypted Volumes.
And enters Emergency Mode. However, after reloading system manager configuration, the system mounts the USB, reads the key and unlocks properly.
I think it’s either because systemd.mounts is not bound to some .target service and a race occurs, or not all modules are loaded before systemd.mounts tries to mount the USB stick. Which is also a race condition, I guess?
Thus, how can mounting be “bound” to a .target? I found this guide by hyperparabolic, but boot.initrd.systemd.mounts template is completely different from the code snippets there. Should it be run as a new service with bash commands instead?
I don’t use disko, so I can’t comment on that part. I use mounts similarly for files I don’t want mounted in my host os that are needed for unlocking though.
Systemd mounts are systemd units. You can’t include service specific config, but all of the dependency management directives can be used in mounts as well, so things like after, before, bindsTo, conflicts, wantedBy, and wants can all be used in mount units too.
Nothing depends on your mount, so it’s just not being mounted. Systemd isn’t going to waste time mounting something that no other unit wants. You need to get that usb stick mounted before cryptsetup.target. It should be something like:
...
wantedBy = ["cryptsetup.target"];
after = ["cryptsetup.target"];
That might just work as is, but that systemd unit also depends on the usb stick actually being present, so it has a dependency on the hardware device. Systemd automatically creates .device units for all of your hardware devices that can be used as dependencies, you can browse yours with systemctl list-units --type=device --all. You’ll want to include that device unit in wants and after as well.
If you still need more info on this, check out man systemd.unit for details on dependency management, man systemd.special for information on standard units like targets, and man bootup covers dependencies between all of the special units during systemd stage1.
Thank you, that helped to bring the system to at least a manageable condition.
boot.luks.devices."cryptroot" = {
crypttabExtraOpts = [ "keyfile-timeout=10" ];
};
boot.initrd.systemd.mounts = [
wantedBy = [ "cryptsetup-pre.target" ];
what = "/dev/disk/by-id/usb-_USB_DISK...-part1";
where = "/keys";
type = "vfat";
options = "noauto";
];
With
boot.initrd.systemd.mounts = [
wantedBy = [ "cryptsetup-pre.target" ];
what = "/dev/disk/by-id/usb-_USB_DISK...-part1";
where = "/keys";
type = "vfat";
The USB stick is mounted and cryptsetup finds the keyfile which is set within disko, but, as you mentioned before, this mounting process becomes a dependency for cryptsetup.target. Thus, without the USB stick, it fails and goes back to Emergency Mode.
options = "noauto";
Helps with it not failing without the USB stick, and by some kind of black magic, crypttabExtraOpts = [ "keyfile-timeout=10" ]; works as if fallbackToPassword is set to true, which it “is implied by systemd boot stage 1”.
Now, there’s a new problem: it actually runs a job for mounting for the entire 90s, with no regards to:
boot.initrd.systemd.mounts = [
{
unitConfig = {
JobRunningTimeoutSec = 10; # or "10" as a string
};
}
];
@arclus, thanks for sharing your work. I was also using preOpenCommands in disko. This thread helped me upgrade my servers to 26.05. I also have the 90 seconds wait when I physically remove the keyfile USB device but at least it eventually asks for a password thanks to “noauto”…
Here is my disko config updated to use boot.initrd.systemd.mounts.