1 { config, options, lib, utils, pkgs, ... }:
6 luks = config.boot.initrd.luks;
7 clevis = config.boot.initrd.clevis;
8 systemd = config.boot.initrd.systemd;
9 kernelPackages = config.boot.kernelPackages;
10 defaultPrio = (mkOptionDefault {}).priority;
20 if [ -e $target ]; then
23 local uuid=$(echo -n $target | sed -e 's,UUID=\(.*\),\1,g')
24 blkid --uuid $uuid >/dev/null
32 local secs="''${3:-10}"
33 local desc="''${4:-$name $target to appear}"
35 if ! dev_exist $target; then
36 echo -n "Waiting $secs seconds for $desc..."
38 for try in $(seq $secs); do
41 if dev_exist $target; then
46 if [ $success == true ]; then
58 local secs="''${1:-10}"
60 ykinfo -v 1>/dev/null 2>&1
62 echo -n "Waiting $secs seconds for YubiKey to appear..."
64 for try in $(seq $secs); do
67 ykinfo -v 1>/dev/null 2>&1
73 if [ $success == true ]; then
85 local secs="''${1:-10}"
87 gpg --card-status > /dev/null 2> /dev/null
89 echo -n "Waiting $secs seconds for GPG Card to appear"
91 for try in $(seq $secs); do
94 gpg --card-status > /dev/null 2> /dev/null
100 if [ $success == true ]; then
113 # A place to store crypto things
115 # A ramfs is used here to ensure that the file used to update
116 # the key slot with cryptsetup will never get swapped out.
117 # Warning: Do NOT replace with tmpfs!
118 mkdir -p /crypt-ramfs
119 mount -t ramfs none /crypt-ramfs
121 # Cryptsetup locking directory
122 mkdir -p /run/cryptsetup
124 # For YubiKey salt storage
125 mkdir -p /crypt-storage
127 ${optionalString luks.gpgSupport ''
128 export GPG_TTY=$(tty)
129 export GNUPGHOME=/crypt-ramfs/.gnupg
131 gpg-agent --daemon --scdaemon-program $out/bin/scdaemon > /dev/null 2> /dev/null
134 # Disable all input echo for the whole stage. We could use read -s
135 # instead but that would occasionally leak characters between read
142 umount /crypt-storage 2>/dev/null
143 umount /crypt-ramfs 2>/dev/null
146 openCommand = name: dev: assert name == dev.name;
148 csopen = "cryptsetup luksOpen ${dev.device} ${dev.name}"
149 + optionalString dev.allowDiscards " --allow-discards"
150 + optionalString dev.bypassWorkqueues " --perf-no_read_workqueue --perf-no_write_workqueue"
151 + optionalString (dev.header != null) " --header=${dev.header}";
152 cschange = "cryptsetup luksChangeKey ${dev.device} ${optionalString (dev.header != null) "--header=${dev.header}"}";
153 fido2luksCredentials = dev.fido2.credentials ++ optional (dev.fido2.credential != null) dev.fido2.credential;
155 # Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g.
157 wait_target "device" ${dev.device} || die "${dev.device} is unavailable"
159 ${optionalString (dev.header != null) ''
160 wait_target "header" ${dev.header} || die "${dev.header} is unavailable"
163 try_empty_passphrase() {
164 ${if dev.tryEmptyPassphrase then ''
165 echo "Trying empty passphrase!"
168 if [ $cs_status -eq 0 ]; then
177 do_open_passphrase() {
181 echo -n "Passphrase for ${dev.device}: "
184 if [ -e /crypt-ramfs/passphrase ]; then
186 passphrase=$(cat /crypt-ramfs/passphrase)
189 # ask cryptsetup-askpass
190 echo -n "${dev.device}" > /crypt-ramfs/device
192 # and try reading it from /dev/console with a timeout
193 IFS= read -t 1 -r passphrase
194 if [ -n "$passphrase" ]; then
195 ${if luks.reusePassphrases then ''
196 # remember it for the next device
197 echo -n "$passphrase" > /crypt-ramfs/passphrase
199 # Don't save it to ramfs. We are very paranoid
206 echo -n "Verifying passphrase for ${dev.device}..."
207 echo -n "$passphrase" | ${csopen} --key-file=-
210 ${if luks.reusePassphrases then ''
211 # we don't rm here because we might reuse it for the next device
213 rm -f /crypt-ramfs/passphrase
218 # ask for a different one
219 rm -f /crypt-ramfs/passphrase
226 ${if (dev.keyFile != null) then ''
227 if wait_target "key file" ${dev.keyFile}; then
228 ${csopen} --key-file=${dev.keyFile} \
229 ${optionalString (dev.keyFileSize != null) "--keyfile-size=${toString dev.keyFileSize}"} \
230 ${optionalString (dev.keyFileOffset != null) "--keyfile-offset=${toString dev.keyFileOffset}"}
232 if [ $cs_status -ne 0 ]; then
233 echo "Key File ${dev.keyFile} failed!"
234 if ! try_empty_passphrase; then
235 ${if dev.fallbackToPassword then "echo" else "die"} "${dev.keyFile} is unavailable"
236 echo " - failing back to interactive password prompt"
241 # If the key file never shows up we should also try the empty passphrase
242 if ! try_empty_passphrase; then
243 ${if dev.fallbackToPassword then "echo" else "die"} "${dev.keyFile} is unavailable"
244 echo " - failing back to interactive password prompt"
249 if ! try_empty_passphrase; then
255 ${optionalString (luks.yubikeySupport && (dev.yubikey != null)) ''
258 ( od -An -vtx1 | tr -d ' \n' )
262 ( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
266 # Make all of these local to this function
267 # to prevent their values being leaked
281 mount -t ${dev.yubikey.storage.fsType} ${dev.yubikey.storage.device} /crypt-storage || \
282 die "Failed to mount YubiKey salt storage device"
284 salt="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 1p | tr -d '\n')"
285 iterations="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 2p | tr -d '\n')"
286 challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
287 response="$(ykchalresp -${toString dev.yubikey.slot} -x $challenge 2>/dev/null)"
289 for try in $(seq 3); do
290 ${optionalString dev.yubikey.twoFactor ''
291 echo -n "Enter two-factor passphrase: "
294 if [ -e /crypt-ramfs/passphrase ]; then
296 k_user=$(cat /crypt-ramfs/passphrase)
299 # Try reading it from /dev/console with a timeout
300 IFS= read -t 1 -r k_user
301 if [ -n "$k_user" ]; then
302 ${if luks.reusePassphrases then ''
303 # Remember it for the next device
304 echo -n "$k_user" > /crypt-ramfs/passphrase
306 # Don't save it to ramfs. We are very paranoid
315 if [ ! -z "$k_user" ]; then
316 k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)"
318 k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)"
321 echo -n "$k_luks" | hextorb | ${csopen} --key-file=-
325 ${if luks.reusePassphrases then ''
326 # We don't rm here because we might reuse it for the next device
328 rm -f /crypt-ramfs/passphrase
333 echo "Authentication failed!"
337 [ "$opened" == false ] && die "Maximum authentication errors reached"
339 echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
340 for i in $(seq ${toString dev.yubikey.saltLength}); do
341 byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
342 new_salt="$new_salt$byte";
347 new_iterations="$iterations"
348 ${optionalString (dev.yubikey.iterationStep > 0) ''
349 new_iterations="$(($new_iterations + ${toString dev.yubikey.iterationStep}))"
352 new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
354 new_response="$(ykchalresp -${toString dev.yubikey.slot} -x $new_challenge 2>/dev/null)"
356 if [ -z "$new_response" ]; then
357 echo "Warning: Unable to generate new challenge response, current challenge persists!"
358 umount /crypt-storage
362 if [ ! -z "$k_user" ]; then
363 new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
365 new_k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
368 echo -n "$new_k_luks" | hextorb > /crypt-ramfs/new_key
369 echo -n "$k_luks" | hextorb | ${cschange} --key-file=- /crypt-ramfs/new_key
372 echo -ne "$new_salt\n$new_iterations" > /crypt-storage${dev.yubikey.storage.path}
373 sync /crypt-storage${dev.yubikey.storage.path}
375 echo "Warning: Could not update LUKS key, current challenge persists!"
378 rm -f /crypt-ramfs/new_key
379 umount /crypt-storage
382 open_with_hardware() {
383 if wait_yubikey ${toString dev.yubikey.gracePeriod}; then
386 echo "No YubiKey found, falling back to non-YubiKey open procedure"
392 ${optionalString (luks.gpgSupport && (dev.gpgCard != null)) ''
395 # Make all of these local to this function
396 # to prevent their values being leaked
400 gpg --import /gpg-keys/${dev.device}/pubkey.asc > /dev/null 2> /dev/null
402 gpg --card-status > /dev/null 2> /dev/null
404 for try in $(seq 3); do
405 echo -n "PIN for GPG Card associated with device ${dev.device}: "
408 if [ -e /crypt-ramfs/passphrase ]; then
410 pin=$(cat /crypt-ramfs/passphrase)
413 # and try reading it from /dev/console with a timeout
414 IFS= read -t 1 -r pin
415 if [ -n "$pin" ]; then
416 ${if luks.reusePassphrases then ''
417 # remember it for the next device
418 echo -n "$pin" > /crypt-ramfs/passphrase
420 # Don't save it to ramfs. We are very paranoid
427 echo -n "Verifying passphrase for ${dev.device}..."
428 echo -n "$pin" | gpg -q --batch --passphrase-fd 0 --pinentry-mode loopback -d /gpg-keys/${dev.device}/cryptkey.gpg 2> /dev/null | ${csopen} --key-file=- > /dev/null 2> /dev/null
431 ${if luks.reusePassphrases then ''
432 # we don't rm here because we might reuse it for the next device
434 rm -f /crypt-ramfs/passphrase
439 # ask for a different one
440 rm -f /crypt-ramfs/passphrase
444 [ "$opened" == false ] && die "Maximum authentication errors reached"
447 open_with_hardware() {
448 if wait_gpgcard ${toString dev.gpgCard.gracePeriod}; then
451 echo "No GPG Card found, falling back to normal open procedure"
457 ${optionalString (luks.fido2Support && fido2luksCredentials != []) ''
459 open_with_hardware() {
462 ${if dev.fido2.passwordLess then ''
465 read -rsp "FIDO2 salt for ${dev.device}: " passphrase
468 ${optionalString (lib.versionOlder kernelPackages.kernel.version "5.4") ''
469 echo "On systems with Linux Kernel < 5.4, it might take a while to initialize the CRNG, you might want to use linuxPackages_latest."
470 echo "Please move your mouse to create needed randomness."
472 echo "Waiting for your FIDO2 device..."
473 fido2luks open${optionalString dev.allowDiscards " --allow-discards"} ${dev.device} ${dev.name} "${builtins.concatStringsSep "," fido2luksCredentials}" --await-dev ${toString dev.fido2.gracePeriod} --salt string:$passphrase
474 if [ $? -ne 0 ]; then
475 echo "No FIDO2 key found, falling back to normal open procedure"
481 # commands to run right before we mount our device
482 ${dev.preOpenCommands}
484 ${if (luks.yubikeySupport && (dev.yubikey != null)) || (luks.gpgSupport && (dev.gpgCard != null)) || (luks.fido2Support && fido2luksCredentials != []) then ''
490 # commands to run right after we mounted our device
491 ${dev.postOpenCommands}
494 askPass = pkgs.writeScriptBin "cryptsetup-askpass" ''
500 wait_target "luks" /crypt-ramfs/device 10 "LUKS to request a passphrase" || die "Passphrase is not requested now"
501 device=$(cat /crypt-ramfs/device)
503 echo -n "Passphrase for $device: "
504 IFS= read -rs passphrase
507 rm /crypt-ramfs/device
508 echo -n "$passphrase" > /crypt-ramfs/passphrase
512 preLVM = filterAttrs (n: v: v.preLVM) luks.devices;
513 postLVM = filterAttrs (n: v: !v.preLVM) luks.devices;
516 stage1Crypttab = pkgs.writeText "initrd-crypttab" (lib.concatLines (lib.mapAttrsToList (n: v: let
517 opts = v.crypttabExtraOpts
518 ++ optional v.allowDiscards "discard"
519 ++ optionals v.bypassWorkqueues [ "no-read-workqueue" "no-write-workqueue" ]
520 ++ optional (v.header != null) "header=${v.header}"
521 ++ optional (v.keyFileOffset != null) "keyfile-offset=${toString v.keyFileOffset}"
522 ++ optional (v.keyFileSize != null) "keyfile-size=${toString v.keyFileSize}"
523 ++ optional (v.keyFileTimeout != null) "keyfile-timeout=${builtins.toString v.keyFileTimeout}s"
524 ++ optional (v.tryEmptyPassphrase) "try-empty-password=true"
526 in "${n} ${v.device} ${if v.keyFile == null then "-" else v.keyFile} ${lib.concatStringsSep "," opts}") luks.devices));
531 (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
536 boot.initrd.luks.mitigateDMAAttacks = mkOption {
540 Unless enabled, encryption keys can be easily recovered by an attacker with physical
541 access to any machine with PCMCIA, ExpressCard, ThunderBolt or FireWire port.
542 More information is available at <https://en.wikipedia.org/wiki/DMA_attack>.
544 This option blacklists FireWire drivers, but doesn't remove them. You can manually
545 load the drivers if you need to use a FireWire device, but don't forget to unload them!
549 boot.initrd.luks.cryptoModules = mkOption {
550 type = types.listOf types.str;
552 [ "aes" "aes_generic" "blowfish" "twofish"
553 "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
554 "af_alg" "algif_skcipher"
557 A list of cryptographic kernel modules needed to decrypt the root device(s).
558 The default includes all common modules.
562 boot.initrd.luks.forceLuksSupportInInitrd = mkOption {
567 Whether to configure luks support in the initrd, when no luks
568 devices are configured.
572 boot.initrd.luks.reusePassphrases = mkOption {
576 When opening a new LUKS device try reusing last successful
579 Useful for mounting a number of devices that use the same
580 passphrase without retyping it several times.
582 Such setup can be useful if you use {command}`cryptsetup luksSuspend`.
583 Different LUKS devices will still have
584 different master keys even when using the same passphrase.
588 boot.initrd.luks.devices = mkOption {
590 example = { luksroot.device = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; };
592 The encrypted disk that should be opened before the root
593 filesystem is mounted. Both LVM-over-LUKS and LUKS-over-LVM
594 setups are supported. The unencrypted devices can be accessed as
595 {file}`/dev/mapper/«name»`.
598 type = with types; attrsOf (submodule (
599 { config, name, ... }: { options = {
604 example = "luksroot";
606 description = "Name of the unencrypted device in {file}`/dev/mapper`.";
610 example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08";
612 description = "Path of the underlying encrypted block device.";
617 example = "/root/header.img";
618 type = types.nullOr types.str;
620 The name of the file or block device that
621 should be used as header for the encrypted device.
627 example = "/dev/sdb1";
628 type = types.nullOr types.str;
630 The name of the file (can be a raw device or a partition) that
631 should be used as the decryption key for the encrypted device. If
632 not specified, you will be prompted for a passphrase instead.
636 tryEmptyPassphrase = mkOption {
640 If keyFile fails then try an empty passphrase first before
641 prompting for password.
645 keyFileTimeout = mkOption {
648 type = types.nullOr types.int;
650 The amount of time in seconds for a keyFile to appear before
651 timing out and trying passwords.
655 keyFileSize = mkOption {
658 type = types.nullOr types.int;
660 The size of the key file. Use this if only the beginning of the
661 key file should be used as a key (often the case if a raw device
662 or partition is used as key file). If not specified, the whole
663 `keyFile` will be used decryption, instead of just
664 the first `keyFileSize` bytes.
668 keyFileOffset = mkOption {
671 type = types.nullOr types.int;
673 The offset of the key file. Use this in combination with
674 `keyFileSize` to use part of a file as key file
675 (often the case if a raw device or partition is used as a key file).
676 If not specified, the key begins at the first byte of
681 # FIXME: get rid of this option.
685 description = "Whether the luksOpen will be attempted before LVM scan or after it.";
688 allowDiscards = mkOption {
692 Whether to allow TRIM requests to the underlying device. This option
693 has security implications; please read the LUKS documentation before
695 This option is incompatible with authenticated encryption (dm-crypt
696 stacked over dm-integrity).
700 bypassWorkqueues = mkOption {
704 Whether to bypass dm-crypt's internal read and write workqueues.
705 Enabling this should improve performance on SSDs; see
706 [here](https://wiki.archlinux.org/index.php/Dm-crypt/Specialties#Disable_workqueue_for_increased_solid_state_drive_(SSD)_performance)
707 for more information. Needs Linux 5.9 or later.
711 fallbackToPassword = mkOption {
715 Whether to fallback to interactive passphrase prompt if the keyfile
716 cannot be found. This will prevent unattended boot should the keyfile
724 The option to use this LUKS device with a GPG encrypted luks password by the GPG Smartcard.
725 If null (the default), GPG-Smartcard will be disabled for this device.
728 type = with types; nullOr (submodule {
730 gracePeriod = mkOption {
733 description = "Time in seconds to wait for the GPG Smartcard.";
736 encryptedPass = mkOption {
738 description = "Path to the GPG encrypted passphrase.";
741 publicKey = mkOption {
743 description = "Path to the Public Key.";
750 credential = mkOption {
752 example = "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2";
753 type = types.nullOr types.str;
754 description = "The FIDO2 credential ID.";
757 credentials = mkOption {
759 example = [ "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2" ];
760 type = types.listOf types.str;
762 List of FIDO2 credential IDs.
764 Use this if you have multiple FIDO2 keys you want to use for the same luks device.
768 gracePeriod = mkOption {
771 description = "Time in seconds to wait for the FIDO2 key.";
774 passwordLess = mkOption {
778 Defines whatever to use an empty string as a default salt.
780 Enable only when your device is PIN protected, such as [Trezor](https://trezor.io/).
788 The options to use for this LUKS device in YubiKey-PBA.
789 If null (the default), YubiKey-PBA will be disabled for this device.
792 type = with types; nullOr (submodule {
794 twoFactor = mkOption {
797 description = "Whether to use a passphrase and a YubiKey (true), or only a YubiKey (false).";
803 description = "Which slot on the YubiKey to challenge.";
806 saltLength = mkOption {
809 description = "Length of the new salt in byte (64 is the effective maximum).";
812 keyLength = mkOption {
815 description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
818 iterationStep = mkOption {
821 description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
824 gracePeriod = mkOption {
827 description = "Time in seconds to wait for the YubiKey.";
830 /* TODO: Add to the documentation of the current module:
832 Options related to the storing the salt.
836 default = "/dev/sda1";
839 An unencrypted device that will temporarily be mounted in stage-1.
840 Must contain the current salt to create the challenge for this LUKS device.
847 description = "The filesystem of the unencrypted device.";
851 default = "/crypt-storage/default";
854 Absolute path of the salt on the unencrypted device with
855 that device's root directory as "/".
863 preOpenCommands = mkOption {
867 mkdir -p /tmp/persistent
868 mount -t zfs rpool/safe/persistent /tmp/persistent
871 Commands that should be run right before we try to mount our LUKS device.
872 This can be useful, if the keys needed to open the drive is on another partition.
876 postOpenCommands = mkOption {
880 umount /tmp/persistent
883 Commands that should be run right after we have mounted our LUKS device.
887 crypttabExtraOpts = mkOption {
888 type = with types; listOf singleLineStr;
890 example = [ "_netdev" ];
893 Only used with systemd stage 1.
895 Extra options to append to the last column of the generated crypttab file.
900 config = mkIf (clevis.enable && (hasAttr name clevis.devices)) {
901 preOpenCommands = mkIf (!systemd.enable) ''
902 mkdir -p /clevis-${name}
903 mount -t ramfs none /clevis-${name}
904 clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted
906 keyFile = "/clevis-${name}/decrypted";
907 fallbackToPassword = !systemd.enable;
908 postOpenCommands = mkIf (!systemd.enable) ''
909 umount /clevis-${name}
915 boot.initrd.luks.gpgSupport = mkOption {
919 Enables support for authenticating with a GPG encrypted password.
923 boot.initrd.luks.yubikeySupport = mkOption {
927 Enables support for authenticating with a YubiKey on LUKS devices.
928 See the NixOS wiki for information on how to properly setup a LUKS device
929 and a YubiKey to work with this feature.
933 boot.initrd.luks.fido2Support = mkOption {
937 Enables support for authenticating with FIDO2 devices.
943 config = mkIf (luks.devices != {} || luks.forceLuksSupportInInitrd) {
946 [ { assertion = !(luks.gpgSupport && luks.yubikeySupport);
947 message = "YubiKey and GPG Card may not be used at the same time.";
950 { assertion = !(luks.gpgSupport && luks.fido2Support);
951 message = "FIDO2 and GPG Card may not be used at the same time.";
954 { assertion = !(luks.fido2Support && luks.yubikeySupport);
955 message = "FIDO2 and YubiKey may not be used at the same time.";
958 { assertion = any (dev: dev.bypassWorkqueues) (attrValues luks.devices)
959 -> versionAtLeast kernelPackages.kernel.version "5.9";
960 message = "boot.initrd.luks.devices.<name>.bypassWorkqueues is not supported for kernels older than 5.9";
963 { assertion = !config.boot.initrd.systemd.enable -> all (x: x.keyFileTimeout == null) (attrValues luks.devices);
964 message = "boot.initrd.luks.devices.<name>.keyFileTimeout is only supported for systemd initrd";
967 { assertion = config.boot.initrd.systemd.enable -> all (dev: !dev.fallbackToPassword) (attrValues luks.devices);
968 message = "boot.initrd.luks.devices.<name>.fallbackToPassword is implied by systemd stage 1.";
970 { assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preLVM) (attrValues luks.devices);
971 message = "boot.initrd.luks.devices.<name>.preLVM is not used by systemd stage 1.";
973 { assertion = config.boot.initrd.systemd.enable -> options.boot.initrd.luks.reusePassphrases.highestPrio == defaultPrio;
974 message = "boot.initrd.luks.reusePassphrases has no effect with systemd stage 1.";
976 { assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preOpenCommands == "" && dev.postOpenCommands == "") (attrValues luks.devices);
977 message = "boot.initrd.luks.devices.<name>.preOpenCommands and postOpenCommands is not supported by systemd stage 1. Please bind a service to cryptsetup.target or cryptsetup-pre.target instead.";
980 { assertion = config.boot.initrd.systemd.enable -> !luks.gpgSupport;
981 message = "systemd stage 1 does not support GPG smartcards yet.";
983 { assertion = config.boot.initrd.systemd.enable -> !luks.fido2Support;
985 systemd stage 1 does not support configuring FIDO2 unlocking through `boot.initrd.luks.fido2Support`.
986 Use systemd-cryptenroll(1) to configure FIDO2 support, and set
987 `boot.initrd.luks.devices.''${DEVICE}.crypttabExtraOpts` as appropriate per crypttab(5)
988 (e.g. `fido2-device=auto`).
992 { assertion = config.boot.initrd.systemd.enable -> !luks.yubikeySupport;
993 message = "systemd stage 1 does not support Yubikeys yet.";
997 # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested
998 boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks
999 ["firewire_ohci" "firewire_core" "firewire_sbp2"];
1001 # Some modules that may be needed for mounting anything ciphered
1002 boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" "input_leds" ]
1003 ++ luks.cryptoModules
1004 # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged
1005 # remove once 'modprobe --show-depends xts' shows ecb as a dependency
1006 ++ (optional (builtins.elem "xts" luks.cryptoModules) "ecb");
1008 # copy the cryptsetup binary and it's dependencies
1009 boot.initrd.extraUtilsCommands = let
1010 pbkdf2-sha512 = pkgs.runCommandCC "pbkdf2-sha512" { buildInputs = [ pkgs.openssl ]; } ''
1012 cc -O3 -lcrypto ${./pbkdf2-sha512.c} -o "$out/bin/pbkdf2-sha512"
1013 strip -s "$out/bin/pbkdf2-sha512"
1016 mkIf (!config.boot.initrd.systemd.enable) ''
1017 copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup
1018 copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass
1019 sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass
1021 ${optionalString luks.yubikeySupport ''
1022 copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp
1023 copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykinfo
1024 copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl
1026 copy_bin_and_libs ${pbkdf2-sha512}/bin/pbkdf2-sha512
1028 mkdir -p $out/etc/ssl
1029 cp -pdv ${pkgs.openssl.out}/etc/ssl/openssl.cnf $out/etc/ssl
1031 cat > $out/bin/openssl-wrap <<EOF
1033 export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
1034 $out/bin/openssl "\$@"
1036 chmod +x $out/bin/openssl-wrap
1039 ${optionalString luks.fido2Support ''
1040 copy_bin_and_libs ${pkgs.fido2luks}/bin/fido2luks
1044 ${optionalString luks.gpgSupport ''
1045 copy_bin_and_libs ${pkgs.gnupg}/bin/gpg
1046 copy_bin_and_libs ${pkgs.gnupg}/bin/gpg-agent
1047 copy_bin_and_libs ${pkgs.gnupg}/libexec/scdaemon
1049 ${concatMapStringsSep "\n" (x:
1050 optionalString (x.gpgCard != null)
1052 mkdir -p $out/secrets/gpg-keys/${x.device}
1053 cp -a ${x.gpgCard.encryptedPass} $out/secrets/gpg-keys/${x.device}/cryptkey.gpg
1054 cp -a ${x.gpgCard.publicKey} $out/secrets/gpg-keys/${x.device}/pubkey.asc
1056 ) (attrValues luks.devices)
1061 boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
1062 $out/bin/cryptsetup --version
1063 ${optionalString luks.yubikeySupport ''
1064 $out/bin/ykchalresp -V
1066 $out/bin/openssl-wrap version
1068 ${optionalString luks.gpgSupport ''
1069 $out/bin/gpg --version
1070 $out/bin/gpg-agent --version
1071 $out/bin/scdaemon --version
1073 ${optionalString luks.fido2Support ''
1074 $out/bin/fido2luks --version
1078 boot.initrd.systemd = {
1079 contents."/etc/crypttab".source = stage1Crypttab;
1081 extraBin.systemd-cryptsetup = "${config.boot.initrd.systemd.package}/bin/systemd-cryptsetup";
1083 additionalUpstreamUnits = [
1084 "cryptsetup-pre.target"
1086 "remote-cryptsetup.target"
1089 "${config.boot.initrd.systemd.package}/bin/systemd-cryptsetup"
1090 "${config.boot.initrd.systemd.package}/lib/systemd/system-generators/systemd-cryptsetup-generator"
1091 ] ++ lib.optionals config.boot.initrd.systemd.tpm2.enable [
1092 "${config.boot.initrd.systemd.package}/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so"
1096 # We do this because we need the udev rules from the package
1097 boot.initrd.services.lvm.enable = true;
1099 boot.initrd.preFailCommands = mkIf (!config.boot.initrd.systemd.enable) postCommands;
1100 boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands);
1101 boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands);
1103 boot.initrd.systemd.services = let devicesWithClevis = filterAttrs (device: _: (hasAttr device clevis.devices)) luks.devices; in
1104 mkIf (clevis.enable && systemd.enable) (
1106 (name: _: nameValuePair "cryptsetup-clevis-${name}" {
1107 wantedBy = [ "systemd-cryptsetup@${utils.escapeSystemdPath name}.service" ];
1109 "systemd-cryptsetup@${utils.escapeSystemdPath name}.service"
1110 "initrd-switch-root.target"
1113 wants = [ "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target";
1114 after = [ "systemd-modules-load.service" "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target";
1116 mkdir -p /clevis-${name}
1117 mount -t ramfs none /clevis-${name}
1119 clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted
1121 conflicts = [ "initrd-switch-root.target" "shutdown.target" ];
1122 unitConfig.DefaultDependencies = "no";
1125 RemainAfterExit = true;
1126 ExecStop = "${config.boot.initrd.systemd.package.util-linux}/bin/umount /clevis-${name}";
1132 environment.systemPackages = [ pkgs.cryptsetup ];