Yubikey fido2 and boot.initrd.systemd

Hello,

I have enabled boot.initrd.systemd.enable = true; and using yubikey fido pin to login as mentioned in the manual.

However, i have two separate partition one for root and other for swap. So i have to touch yubikey twice. Is it possible for the swap partition to be opened automatically after root is opened with a touch?

Here is my config,

  boot.initrd.systemd.enable = true;
  boot.initrd.luks.fido2Support = false;
  boot.initrd.luks.devices."cryptroot" = {
      device = "/dev/disk/by-uuid/e9d53425-fa6d-4158-b383-44db9e26b5e5";
      crypttabExtraOpts = ["fido2-device=auto"];  # cryptenroll
    };

  boot.initrd.luks.devices."cryptswap" = {
      device = "/dev/disk/by-uuid/2beb5e20-0114-4115-aec5-73e16200fbb6";
      crypttabExtraOpts = ["fido2-device=auto"];  # cryptenroll
    };

The way I typically approach this sort of thing is to have only one thing that’s encrypted with the token, and then that thing is used as the keyfile for other things. So you might have a third (very small) LUKS partition with fido2-device=auto, and the other partitions would use keyFile = "/dev/mapper/key"; (and maybe keyfile-size=4096 or something) instead of using FIDO2 themselves.

IIRC, you can even do something like keyFile = "/dev/mapper/key:/path/to/file"; and it’ll get a file out of a file system stored on /dev/mapper/key instead of using the block device itself as the key.

Very interesting. I do have a spare 1g partition which i can repurpose.

This is what i did.

$ cryptsetup luksFormat --cipher=aes-xts-plain64 --key-size=512 --hash=sha512  /dev/nvme0n1p5
$ systemd-cryptenroll --fido2-device=auto /dev/nvme0n1p5
$ cryptsetup open /dev/nvme0n1p5 key

$ cryptsetup luksAddKey --keyfile-size 4096 /dev/nvme0n1p7 --key-file /dev/mapper/key

Basically just something like this:

  boot.initrd.systemd.enable = true;
  boot.initrd.luks.devices."cryptroot" = {
    device = "/dev/disk/by-uuid/e9d53425-fa6d-4158-b383-44db9e26b5e5";
    keyFile = "/dev/mapper/keys";
    crypttabExtraOpts = [ "keyfile-size=4096" ];
  };

  boot.initrd.luks.devices."cryptswap" = {
    device = "/dev/disk/by-uuid/2beb5e20-0114-4115-aec5-73e16200fbb6";
    keyFile = "/dev/mapper/keys";
    crypttabExtraOpts = [ "keyfile-size=4096" ];
  };

  boot.initrd.luks.device."keys" = {
    device = "/dev/disk/by-uuid/asdf-asdf-asdf";
    crypttabExtraOpts = [ "fido2-device=auto" ];
  };

To format this, you would create a LUKS device for the keys device, with random data in its first 4096 bytes. I experimented with having this device contain a file system with separate keys for each partition, but systemd does the wrong thing when multiple LUKS devices want to mount the same file system for their keys with keyFile = "/path/to/key:/dev/mapper/keys";, so that would take extra effort.

cryptsetup luksFormat /dev/disk/by-uuid/keys-partition # use real uuid
systemd-cryptenroll --fido2-device=auto /dev/disk/by-uuid/asdf-asdf-asdf
cryptsetup open /dev/disk/by-uuid/asdf-asdf-asdf keys
dd if=/dev/urandom of=/dev/mapper/keys bs=4096 count=1

cryptsetup luksFormat --keyfile-size 4096 /dev/disk/by-uuid/cryptroot-partition /dev/mapper/keys # use real uuid
cryptsetup open --key-file /dev/mapper/keys --keyfile-size 4096 /dev/disk/by-uuid/cryptroot-partition cryptroot
cryptsetup luksFormat --keyfile-size 4096 /dev/disk/by-uuid/cryptswap-partition /dev/mapper/keys
cryptsetup open --key-file /dev/mapper/keys --keyfile-size 4096 /dev/disk/by-uuid/cryptswap-partition cryptswap

EDIT: Oh, you figured it out :stuck_out_tongue:

I simply added the key with luksAddKey instead of using a luksformat for the root partition. However i did format the swap partition as you suggested.

I am presented with Fido option at bootup. However i then get a prompt for a passphrase for cryptroot!. Once i give the passphrase it moves along i.e. cryptswap works fine.

I suspect the slots have something to this with this. Do i need to delete slots and add keyfile to the root partition?

This my config,

  boot.initrd.systemd.enable = true;
  boot.initrd.luks.fido2Support = false;
  boot.initrd.luks.devices."key" = {
    device = "/dev/disk/by-uuid/e82700e3-4f03-48bf-8a91-1a2a95ba7868";
    crypttabExtraOpts = ["fido2-device=auto"];  # cryptenroll
};
  boot.initrd.luks.devices."cryptroot" = {
      device = "/dev/disk/by-uuid/e9d53425-fa6d-4158-b383-44db9e26b5e5";
      keyFile = "/dev/mapper/key";
      crypttabExtraOpts = [ "keyfile-size=4096" ];
    };

  boot.initrd.luks.devices."cryptswap" = {
      device = "/dev/disk/by-uuid/65f418c3-db0b-4aa8-948b-936b1df5f12f";
      keyFile = "/dev/mapper/key";
      crypttabExtraOpts = [ "keyfile-size=4096" ];
    };

You should delete the fido2 slot you had on the root partition before, if you haven’t already. Other than that, just adding the key-file based slot should have been sufficient.

I did delete the fido2 keys.

I dont understand what i am doing wrong here,

# cryptsetup luksAddKey --key-file /dev/mapper/key --keyfile-size 4096 /dev/disk/by-uuid/e9d53425-fa6d-4158-b383-44db9e26b5e5 
Enter new passphrase for key slot: 
Verify passphrase: 


# cryptsetup open --key-file /dev/mapper/key --keyfile-size 4096 --test-passphrase /dev/disk/by-uuid/e9d53425-fa6d-4158-b383-44db9e26b5e5
No key available with this passphrase.

With luksAddKey, the --key-file option specifies the key file for an existing slot, not the new key to enroll. The second positional argument (or alternatively --new-keyfile) is for the new key file. Similarly, --keyfile-size is for the existing key, and --new-keyfile-size is for the new one.

cryptsetup luksAddKey --new-keyfile /dev/mapper/key --new-keyfile-size 4096 /dev/disk/by-uuid/e9d53425-fa6d-4158-b383-44db9e26b5e5

Thank you, that was it. I did not know about the ‘new’ options.

1 Like

@ElvishJerricco Is it possible to enter a password if the yubikey is not present? It would be nice to have a 1 minute timeout on yubikey and then ask for a password. There could be a time when i am not carrying a yubikey with me.

I’m pretty sure it’ll timeout by default after 30 seconds, which should be configurable with the token-timeout= crypttab option. Then it should fallback to password, assuming you have a password slot on the drive.

1 Like

I specified the following,

boot.initrd.luks.devices."key" = {
    device = "/dev/disk/by-uuid/75fafce7-606f-4825-b085-a951f373e854";
    crypttabExtraOpts = ["fido2-device=auto" "token-timeout=30"];  # cryptenroll
};

Rebuilt, took out the yubikey and rebooted. The system booted and asked for the Fido2 pin. Gave it more than 30 seconds, and it continued to ask for the pin. Is it supposed to fail by itself and ask a passphrase?

Also, can i enroll a second yubikey to the Luks drive?

Looks like if you enroll a yubikey with its pin (which is optional, btw), then systemd always prompts for the pin, even if no key is plugged in. It doesn’t look for and timeout on the token until after the pin is entered. IMO this should be considered a systemd bug, because that’s obviously undesirable behavior