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 if [ $ret -ne 0 ]; then
508 die "End of file reached. Exiting shell."
511 rm /crypt-ramfs/device
512 echo -n "$passphrase" > /crypt-ramfs/passphrase
516 preLVM = filterAttrs (n: v: v.preLVM) luks.devices;
517 postLVM = filterAttrs (n: v: !v.preLVM) luks.devices;
520 stage1Crypttab = pkgs.writeText "initrd-crypttab" (lib.concatLines (lib.mapAttrsToList (n: v: let
521 opts = v.crypttabExtraOpts
522 ++ optional v.allowDiscards "discard"
523 ++ optionals v.bypassWorkqueues [ "no-read-workqueue" "no-write-workqueue" ]
524 ++ optional (v.header != null) "header=${v.header}"
525 ++ optional (v.keyFileOffset != null) "keyfile-offset=${toString v.keyFileOffset}"
526 ++ optional (v.keyFileSize != null) "keyfile-size=${toString v.keyFileSize}"
527 ++ optional (v.keyFileTimeout != null) "keyfile-timeout=${builtins.toString v.keyFileTimeout}s"
528 ++ optional (v.tryEmptyPassphrase) "try-empty-password=true"
530 in "${n} ${v.device} ${if v.keyFile == null then "-" else v.keyFile} ${lib.concatStringsSep "," opts}") luks.devices));
535 (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
540 boot.initrd.luks.mitigateDMAAttacks = mkOption {
544 Unless enabled, encryption keys can be easily recovered by an attacker with physical
545 access to any machine with PCMCIA, ExpressCard, ThunderBolt or FireWire port.
546 More information is available at <https://en.wikipedia.org/wiki/DMA_attack>.
548 This option blacklists FireWire drivers, but doesn't remove them. You can manually
549 load the drivers if you need to use a FireWire device, but don't forget to unload them!
553 boot.initrd.luks.cryptoModules = mkOption {
554 type = types.listOf types.str;
556 [ "aes" "aes_generic" "blowfish" "twofish"
557 "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
558 "af_alg" "algif_skcipher"
561 A list of cryptographic kernel modules needed to decrypt the root device(s).
562 The default includes all common modules.
566 boot.initrd.luks.forceLuksSupportInInitrd = mkOption {
571 Whether to configure luks support in the initrd, when no luks
572 devices are configured.
576 boot.initrd.luks.reusePassphrases = mkOption {
580 When opening a new LUKS device try reusing last successful
583 Useful for mounting a number of devices that use the same
584 passphrase without retyping it several times.
586 Such setup can be useful if you use {command}`cryptsetup luksSuspend`.
587 Different LUKS devices will still have
588 different master keys even when using the same passphrase.
592 boot.initrd.luks.devices = mkOption {
594 example = { luksroot.device = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; };
596 The encrypted disk that should be opened before the root
597 filesystem is mounted. Both LVM-over-LUKS and LUKS-over-LVM
598 setups are supported. The unencrypted devices can be accessed as
599 {file}`/dev/mapper/«name»`.
602 type = with types; attrsOf (submodule (
603 { config, name, ... }: { options = {
608 example = "luksroot";
610 description = "Name of the unencrypted device in {file}`/dev/mapper`.";
614 example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08";
616 description = "Path of the underlying encrypted block device.";
621 example = "/root/header.img";
622 type = types.nullOr types.str;
624 The name of the file or block device that
625 should be used as header for the encrypted device.
631 example = "/dev/sdb1";
632 type = types.nullOr types.str;
634 The name of the file (can be a raw device or a partition) that
635 should be used as the decryption key for the encrypted device. If
636 not specified, you will be prompted for a passphrase instead.
640 tryEmptyPassphrase = mkOption {
644 If keyFile fails then try an empty passphrase first before
645 prompting for password.
649 keyFileTimeout = mkOption {
652 type = types.nullOr types.int;
654 The amount of time in seconds for a keyFile to appear before
655 timing out and trying passwords.
659 keyFileSize = mkOption {
662 type = types.nullOr types.int;
664 The size of the key file. Use this if only the beginning of the
665 key file should be used as a key (often the case if a raw device
666 or partition is used as key file). If not specified, the whole
667 `keyFile` will be used decryption, instead of just
668 the first `keyFileSize` bytes.
672 keyFileOffset = mkOption {
675 type = types.nullOr types.int;
677 The offset of the key file. Use this in combination with
678 `keyFileSize` to use part of a file as key file
679 (often the case if a raw device or partition is used as a key file).
680 If not specified, the key begins at the first byte of
685 # FIXME: get rid of this option.
689 description = "Whether the luksOpen will be attempted before LVM scan or after it.";
692 allowDiscards = mkOption {
696 Whether to allow TRIM requests to the underlying device. This option
697 has security implications; please read the LUKS documentation before
699 This option is incompatible with authenticated encryption (dm-crypt
700 stacked over dm-integrity).
704 bypassWorkqueues = mkOption {
708 Whether to bypass dm-crypt's internal read and write workqueues.
709 Enabling this should improve performance on SSDs; see
710 [here](https://wiki.archlinux.org/index.php/Dm-crypt/Specialties#Disable_workqueue_for_increased_solid_state_drive_(SSD)_performance)
711 for more information. Needs Linux 5.9 or later.
715 fallbackToPassword = mkOption {
719 Whether to fallback to interactive passphrase prompt if the keyfile
720 cannot be found. This will prevent unattended boot should the keyfile
728 The option to use this LUKS device with a GPG encrypted luks password by the GPG Smartcard.
729 If null (the default), GPG-Smartcard will be disabled for this device.
732 type = with types; nullOr (submodule {
734 gracePeriod = mkOption {
737 description = "Time in seconds to wait for the GPG Smartcard.";
740 encryptedPass = mkOption {
742 description = "Path to the GPG encrypted passphrase.";
745 publicKey = mkOption {
747 description = "Path to the Public Key.";
754 credential = mkOption {
756 example = "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2";
757 type = types.nullOr types.str;
758 description = "The FIDO2 credential ID.";
761 credentials = mkOption {
763 example = [ "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2" ];
764 type = types.listOf types.str;
766 List of FIDO2 credential IDs.
768 Use this if you have multiple FIDO2 keys you want to use for the same luks device.
772 gracePeriod = mkOption {
775 description = "Time in seconds to wait for the FIDO2 key.";
778 passwordLess = mkOption {
782 Defines whatever to use an empty string as a default salt.
784 Enable only when your device is PIN protected, such as [Trezor](https://trezor.io/).
792 The options to use for this LUKS device in YubiKey-PBA.
793 If null (the default), YubiKey-PBA will be disabled for this device.
796 type = with types; nullOr (submodule {
798 twoFactor = mkOption {
801 description = "Whether to use a passphrase and a YubiKey (true), or only a YubiKey (false).";
807 description = "Which slot on the YubiKey to challenge.";
810 saltLength = mkOption {
813 description = "Length of the new salt in byte (64 is the effective maximum).";
816 keyLength = mkOption {
819 description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
822 iterationStep = mkOption {
825 description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
828 gracePeriod = mkOption {
831 description = "Time in seconds to wait for the YubiKey.";
834 /* TODO: Add to the documentation of the current module:
836 Options related to the storing the salt.
840 default = "/dev/sda1";
843 An unencrypted device that will temporarily be mounted in stage-1.
844 Must contain the current salt to create the challenge for this LUKS device.
851 description = "The filesystem of the unencrypted device.";
855 default = "/crypt-storage/default";
858 Absolute path of the salt on the unencrypted device with
859 that device's root directory as "/".
867 preOpenCommands = mkOption {
871 mkdir -p /tmp/persistent
872 mount -t zfs rpool/safe/persistent /tmp/persistent
875 Commands that should be run right before we try to mount our LUKS device.
876 This can be useful, if the keys needed to open the drive is on another partition.
880 postOpenCommands = mkOption {
884 umount /tmp/persistent
887 Commands that should be run right after we have mounted our LUKS device.
891 crypttabExtraOpts = mkOption {
892 type = with types; listOf singleLineStr;
894 example = [ "_netdev" ];
897 Only used with systemd stage 1.
899 Extra options to append to the last column of the generated crypttab file.
904 config = mkIf (clevis.enable && (hasAttr name clevis.devices)) {
905 preOpenCommands = mkIf (!systemd.enable) ''
906 mkdir -p /clevis-${name}
907 mount -t ramfs none /clevis-${name}
908 clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted
910 keyFile = "/clevis-${name}/decrypted";
911 fallbackToPassword = !systemd.enable;
912 postOpenCommands = mkIf (!systemd.enable) ''
913 umount /clevis-${name}
919 boot.initrd.luks.gpgSupport = mkOption {
923 Enables support for authenticating with a GPG encrypted password.
927 boot.initrd.luks.yubikeySupport = mkOption {
931 Enables support for authenticating with a YubiKey on LUKS devices.
932 See the NixOS wiki for information on how to properly setup a LUKS device
933 and a YubiKey to work with this feature.
937 boot.initrd.luks.fido2Support = mkOption {
941 Enables support for authenticating with FIDO2 devices.
947 config = mkIf (luks.devices != {} || luks.forceLuksSupportInInitrd) {
950 [ { assertion = !(luks.gpgSupport && luks.yubikeySupport);
951 message = "YubiKey and GPG Card may not be used at the same time.";
954 { assertion = !(luks.gpgSupport && luks.fido2Support);
955 message = "FIDO2 and GPG Card may not be used at the same time.";
958 { assertion = !(luks.fido2Support && luks.yubikeySupport);
959 message = "FIDO2 and YubiKey may not be used at the same time.";
962 { assertion = any (dev: dev.bypassWorkqueues) (attrValues luks.devices)
963 -> versionAtLeast kernelPackages.kernel.version "5.9";
964 message = "boot.initrd.luks.devices.<name>.bypassWorkqueues is not supported for kernels older than 5.9";
967 { assertion = !config.boot.initrd.systemd.enable -> all (x: x.keyFileTimeout == null) (attrValues luks.devices);
968 message = "boot.initrd.luks.devices.<name>.keyFileTimeout is only supported for systemd initrd";
971 { assertion = config.boot.initrd.systemd.enable -> all (dev: !dev.fallbackToPassword) (attrValues luks.devices);
972 message = "boot.initrd.luks.devices.<name>.fallbackToPassword is implied by systemd stage 1.";
974 { assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preLVM) (attrValues luks.devices);
975 message = "boot.initrd.luks.devices.<name>.preLVM is not used by systemd stage 1.";
977 { assertion = config.boot.initrd.systemd.enable -> options.boot.initrd.luks.reusePassphrases.highestPrio == defaultPrio;
978 message = "boot.initrd.luks.reusePassphrases has no effect with systemd stage 1.";
980 { assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preOpenCommands == "" && dev.postOpenCommands == "") (attrValues luks.devices);
981 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.";
984 { assertion = config.boot.initrd.systemd.enable -> !luks.gpgSupport;
985 message = "systemd stage 1 does not support GPG smartcards yet.";
987 { assertion = config.boot.initrd.systemd.enable -> !luks.fido2Support;
989 systemd stage 1 does not support configuring FIDO2 unlocking through `boot.initrd.luks.fido2Support`.
990 Use systemd-cryptenroll(1) to configure FIDO2 support, and set
991 `boot.initrd.luks.devices.''${DEVICE}.crypttabExtraOpts` as appropriate per crypttab(5)
992 (e.g. `fido2-device=auto`).
996 { assertion = config.boot.initrd.systemd.enable -> !luks.yubikeySupport;
997 message = "systemd stage 1 does not support Yubikeys yet.";
1001 # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested
1002 boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks
1003 ["firewire_ohci" "firewire_core" "firewire_sbp2"];
1005 # Some modules that may be needed for mounting anything ciphered
1006 boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" "input_leds" ]
1007 ++ luks.cryptoModules
1008 # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged
1009 # remove once 'modprobe --show-depends xts' shows ecb as a dependency
1010 ++ (optional (builtins.elem "xts" luks.cryptoModules) "ecb");
1012 # copy the cryptsetup binary and it's dependencies
1013 boot.initrd.extraUtilsCommands = let
1014 pbkdf2-sha512 = pkgs.runCommandCC "pbkdf2-sha512" { buildInputs = [ pkgs.openssl ]; } ''
1016 cc -O3 -lcrypto ${./pbkdf2-sha512.c} -o "$out/bin/pbkdf2-sha512"
1017 strip -s "$out/bin/pbkdf2-sha512"
1020 mkIf (!config.boot.initrd.systemd.enable) ''
1021 copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup
1022 copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass
1023 sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass
1025 ${optionalString luks.yubikeySupport ''
1026 copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp
1027 copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykinfo
1028 copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl
1030 copy_bin_and_libs ${pbkdf2-sha512}/bin/pbkdf2-sha512
1032 mkdir -p $out/etc/ssl
1033 cp -pdv ${pkgs.openssl.out}/etc/ssl/openssl.cnf $out/etc/ssl
1035 cat > $out/bin/openssl-wrap <<EOF
1037 export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
1038 $out/bin/openssl "\$@"
1040 chmod +x $out/bin/openssl-wrap
1043 ${optionalString luks.fido2Support ''
1044 copy_bin_and_libs ${pkgs.fido2luks}/bin/fido2luks
1048 ${optionalString luks.gpgSupport ''
1049 copy_bin_and_libs ${pkgs.gnupg}/bin/gpg
1050 copy_bin_and_libs ${pkgs.gnupg}/bin/gpg-agent
1051 copy_bin_and_libs ${pkgs.gnupg}/libexec/scdaemon
1053 ${concatMapStringsSep "\n" (x:
1054 optionalString (x.gpgCard != null)
1056 mkdir -p $out/secrets/gpg-keys/${x.device}
1057 cp -a ${x.gpgCard.encryptedPass} $out/secrets/gpg-keys/${x.device}/cryptkey.gpg
1058 cp -a ${x.gpgCard.publicKey} $out/secrets/gpg-keys/${x.device}/pubkey.asc
1060 ) (attrValues luks.devices)
1065 boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
1066 $out/bin/cryptsetup --version
1067 ${optionalString luks.yubikeySupport ''
1068 $out/bin/ykchalresp -V
1070 $out/bin/openssl-wrap version
1072 ${optionalString luks.gpgSupport ''
1073 $out/bin/gpg --version
1074 $out/bin/gpg-agent --version
1075 $out/bin/scdaemon --version
1077 ${optionalString luks.fido2Support ''
1078 $out/bin/fido2luks --version
1082 boot.initrd.systemd = {
1083 contents."/etc/crypttab".source = stage1Crypttab;
1085 extraBin.systemd-cryptsetup = "${config.boot.initrd.systemd.package}/bin/systemd-cryptsetup";
1087 additionalUpstreamUnits = [
1088 "cryptsetup-pre.target"
1090 "remote-cryptsetup.target"
1093 "${config.boot.initrd.systemd.package}/bin/systemd-cryptsetup"
1094 "${config.boot.initrd.systemd.package}/lib/systemd/system-generators/systemd-cryptsetup-generator"
1095 ] ++ lib.optionals config.boot.initrd.systemd.tpm2.enable [
1096 "${config.boot.initrd.systemd.package}/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so"
1100 # We do this because we need the udev rules from the package
1101 services.lvm.enable = true;
1102 boot.initrd.services.lvm.enable = true;
1104 boot.initrd.preFailCommands = mkIf (!config.boot.initrd.systemd.enable) postCommands;
1105 boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands);
1106 boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands);
1108 boot.initrd.systemd.services = let devicesWithClevis = filterAttrs (device: _: (hasAttr device clevis.devices)) luks.devices; in
1109 mkIf (clevis.enable && systemd.enable) (
1111 (name: _: nameValuePair "cryptsetup-clevis-${name}" {
1112 wantedBy = [ "systemd-cryptsetup@${utils.escapeSystemdPath name}.service" ];
1114 "systemd-cryptsetup@${utils.escapeSystemdPath name}.service"
1115 "initrd-switch-root.target"
1118 wants = [ "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target";
1119 after = [ "systemd-modules-load.service" "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target";
1121 mkdir -p /clevis-${name}
1122 mount -t ramfs none /clevis-${name}
1124 clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted
1126 conflicts = [ "initrd-switch-root.target" "shutdown.target" ];
1127 unitConfig.DefaultDependencies = "no";
1130 RemainAfterExit = true;
1131 ExecStop = "${config.boot.initrd.systemd.package.util-linux}/bin/umount /clevis-${name}";
1137 environment.systemPackages = [ pkgs.cryptsetup ];