at 22.05-pre 33 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 luks = config.boot.initrd.luks; 7 kernelPackages = config.boot.kernelPackages; 8 9 commonFunctions = '' 10 die() { 11 echo "$@" >&2 12 exit 1 13 } 14 15 dev_exist() { 16 local target="$1" 17 if [ -e $target ]; then 18 return 0 19 else 20 local uuid=$(echo -n $target | sed -e 's,UUID=\(.*\),\1,g') 21 blkid --uuid $uuid >/dev/null 22 return $? 23 fi 24 } 25 26 wait_target() { 27 local name="$1" 28 local target="$2" 29 local secs="''${3:-10}" 30 local desc="''${4:-$name $target to appear}" 31 32 if ! dev_exist $target; then 33 echo -n "Waiting $secs seconds for $desc..." 34 local success=false; 35 for try in $(seq $secs); do 36 echo -n "." 37 sleep 1 38 if dev_exist $target; then 39 success=true 40 break 41 fi 42 done 43 if [ $success == true ]; then 44 echo " - success"; 45 return 0 46 else 47 echo " - failure"; 48 return 1 49 fi 50 fi 51 return 0 52 } 53 54 wait_yubikey() { 55 local secs="''${1:-10}" 56 57 ykinfo -v 1>/dev/null 2>&1 58 if [ $? != 0 ]; then 59 echo -n "Waiting $secs seconds for YubiKey to appear..." 60 local success=false 61 for try in $(seq $secs); do 62 echo -n . 63 sleep 1 64 ykinfo -v 1>/dev/null 2>&1 65 if [ $? == 0 ]; then 66 success=true 67 break 68 fi 69 done 70 if [ $success == true ]; then 71 echo " - success"; 72 return 0 73 else 74 echo " - failure"; 75 return 1 76 fi 77 fi 78 return 0 79 } 80 81 wait_gpgcard() { 82 local secs="''${1:-10}" 83 84 gpg --card-status > /dev/null 2> /dev/null 85 if [ $? != 0 ]; then 86 echo -n "Waiting $secs seconds for GPG Card to appear" 87 local success=false 88 for try in $(seq $secs); do 89 echo -n . 90 sleep 1 91 gpg --card-status > /dev/null 2> /dev/null 92 if [ $? == 0 ]; then 93 success=true 94 break 95 fi 96 done 97 if [ $success == true ]; then 98 echo " - success"; 99 return 0 100 else 101 echo " - failure"; 102 return 1 103 fi 104 fi 105 return 0 106 } 107 ''; 108 109 preCommands = '' 110 # A place to store crypto things 111 112 # A ramfs is used here to ensure that the file used to update 113 # the key slot with cryptsetup will never get swapped out. 114 # Warning: Do NOT replace with tmpfs! 115 mkdir -p /crypt-ramfs 116 mount -t ramfs none /crypt-ramfs 117 118 # Cryptsetup locking directory 119 mkdir -p /run/cryptsetup 120 121 # For YubiKey salt storage 122 mkdir -p /crypt-storage 123 124 ${optionalString luks.gpgSupport '' 125 export GPG_TTY=$(tty) 126 export GNUPGHOME=/crypt-ramfs/.gnupg 127 128 gpg-agent --daemon --scdaemon-program $out/bin/scdaemon > /dev/null 2> /dev/null 129 ''} 130 131 # Disable all input echo for the whole stage. We could use read -s 132 # instead but that would ocasionally leak characters between read 133 # invocations. 134 stty -echo 135 ''; 136 137 postCommands = '' 138 stty echo 139 umount /crypt-storage 2>/dev/null 140 umount /crypt-ramfs 2>/dev/null 141 ''; 142 143 openCommand = name: dev: assert name == dev.name; 144 let 145 csopen = "cryptsetup luksOpen ${dev.device} ${dev.name}" 146 + optionalString dev.allowDiscards " --allow-discards" 147 + optionalString dev.bypassWorkqueues " --perf-no_read_workqueue --perf-no_write_workqueue" 148 + optionalString (dev.header != null) " --header=${dev.header}"; 149 cschange = "cryptsetup luksChangeKey ${dev.device} ${optionalString (dev.header != null) "--header=${dev.header}"}"; 150 in '' 151 # Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g. 152 # if on a USB drive. 153 wait_target "device" ${dev.device} || die "${dev.device} is unavailable" 154 155 ${optionalString (dev.header != null) '' 156 wait_target "header" ${dev.header} || die "${dev.header} is unavailable" 157 ''} 158 159 do_open_passphrase() { 160 local passphrase 161 162 while true; do 163 echo -n "Passphrase for ${dev.device}: " 164 passphrase= 165 while true; do 166 if [ -e /crypt-ramfs/passphrase ]; then 167 echo "reused" 168 passphrase=$(cat /crypt-ramfs/passphrase) 169 break 170 else 171 # ask cryptsetup-askpass 172 echo -n "${dev.device}" > /crypt-ramfs/device 173 174 # and try reading it from /dev/console with a timeout 175 IFS= read -t 1 -r passphrase 176 if [ -n "$passphrase" ]; then 177 ${if luks.reusePassphrases then '' 178 # remember it for the next device 179 echo -n "$passphrase" > /crypt-ramfs/passphrase 180 '' else '' 181 # Don't save it to ramfs. We are very paranoid 182 ''} 183 echo 184 break 185 fi 186 fi 187 done 188 echo -n "Verifying passphrase for ${dev.device}..." 189 echo -n "$passphrase" | ${csopen} --key-file=- 190 if [ $? == 0 ]; then 191 echo " - success" 192 ${if luks.reusePassphrases then '' 193 # we don't rm here because we might reuse it for the next device 194 '' else '' 195 rm -f /crypt-ramfs/passphrase 196 ''} 197 break 198 else 199 echo " - failure" 200 # ask for a different one 201 rm -f /crypt-ramfs/passphrase 202 fi 203 done 204 } 205 206 # LUKS 207 open_normally() { 208 ${if (dev.keyFile != null) then '' 209 if wait_target "key file" ${dev.keyFile}; then 210 ${csopen} --key-file=${dev.keyFile} \ 211 ${optionalString (dev.keyFileSize != null) "--keyfile-size=${toString dev.keyFileSize}"} \ 212 ${optionalString (dev.keyFileOffset != null) "--keyfile-offset=${toString dev.keyFileOffset}"} 213 else 214 ${if dev.fallbackToPassword then "echo" else "die"} "${dev.keyFile} is unavailable" 215 echo " - failing back to interactive password prompt" 216 do_open_passphrase 217 fi 218 '' else '' 219 do_open_passphrase 220 ''} 221 } 222 223 ${optionalString (luks.yubikeySupport && (dev.yubikey != null)) '' 224 # YubiKey 225 rbtohex() { 226 ( od -An -vtx1 | tr -d ' \n' ) 227 } 228 229 hextorb() { 230 ( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf ) 231 } 232 233 do_open_yubikey() { 234 # Make all of these local to this function 235 # to prevent their values being leaked 236 local salt 237 local iterations 238 local k_user 239 local challenge 240 local response 241 local k_luks 242 local opened 243 local new_salt 244 local new_iterations 245 local new_challenge 246 local new_response 247 local new_k_luks 248 249 mount -t ${dev.yubikey.storage.fsType} ${dev.yubikey.storage.device} /crypt-storage || \ 250 die "Failed to mount YubiKey salt storage device" 251 252 salt="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 1p | tr -d '\n')" 253 iterations="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 2p | tr -d '\n')" 254 challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)" 255 response="$(ykchalresp -${toString dev.yubikey.slot} -x $challenge 2>/dev/null)" 256 257 for try in $(seq 3); do 258 ${optionalString dev.yubikey.twoFactor '' 259 echo -n "Enter two-factor passphrase: " 260 k_user= 261 while true; do 262 if [ -e /crypt-ramfs/passphrase ]; then 263 echo "reused" 264 k_user=$(cat /crypt-ramfs/passphrase) 265 break 266 else 267 # Try reading it from /dev/console with a timeout 268 IFS= read -t 1 -r k_user 269 if [ -n "$k_user" ]; then 270 ${if luks.reusePassphrases then '' 271 # Remember it for the next device 272 echo -n "$k_user" > /crypt-ramfs/passphrase 273 '' else '' 274 # Don't save it to ramfs. We are very paranoid 275 ''} 276 echo 277 break 278 fi 279 fi 280 done 281 ''} 282 283 if [ ! -z "$k_user" ]; then 284 k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)" 285 else 286 k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)" 287 fi 288 289 echo -n "$k_luks" | hextorb | ${csopen} --key-file=- 290 291 if [ $? == 0 ]; then 292 opened=true 293 ${if luks.reusePassphrases then '' 294 # We don't rm here because we might reuse it for the next device 295 '' else '' 296 rm -f /crypt-ramfs/passphrase 297 ''} 298 break 299 else 300 opened=false 301 echo "Authentication failed!" 302 fi 303 done 304 305 [ "$opened" == false ] && die "Maximum authentication errors reached" 306 307 echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..." 308 for i in $(seq ${toString dev.yubikey.saltLength}); do 309 byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)"; 310 new_salt="$new_salt$byte"; 311 echo -n . 312 done; 313 echo "ok" 314 315 new_iterations="$iterations" 316 ${optionalString (dev.yubikey.iterationStep > 0) '' 317 new_iterations="$(($new_iterations + ${toString dev.yubikey.iterationStep}))" 318 ''} 319 320 new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)" 321 322 new_response="$(ykchalresp -${toString dev.yubikey.slot} -x $new_challenge 2>/dev/null)" 323 324 if [ ! -z "$k_user" ]; then 325 new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)" 326 else 327 new_k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)" 328 fi 329 330 echo -n "$new_k_luks" | hextorb > /crypt-ramfs/new_key 331 echo -n "$k_luks" | hextorb | ${cschange} --key-file=- /crypt-ramfs/new_key 332 333 if [ $? == 0 ]; then 334 echo -ne "$new_salt\n$new_iterations" > /crypt-storage${dev.yubikey.storage.path} 335 sync /crypt-storage${dev.yubikey.storage.path} 336 else 337 echo "Warning: Could not update LUKS key, current challenge persists!" 338 fi 339 340 rm -f /crypt-ramfs/new_key 341 umount /crypt-storage 342 } 343 344 open_with_hardware() { 345 if wait_yubikey ${toString dev.yubikey.gracePeriod}; then 346 do_open_yubikey 347 else 348 echo "No YubiKey found, falling back to non-YubiKey open procedure" 349 open_normally 350 fi 351 } 352 ''} 353 354 ${optionalString (luks.gpgSupport && (dev.gpgCard != null)) '' 355 356 do_open_gpg_card() { 357 # Make all of these local to this function 358 # to prevent their values being leaked 359 local pin 360 local opened 361 362 gpg --import /gpg-keys/${dev.device}/pubkey.asc > /dev/null 2> /dev/null 363 364 gpg --card-status > /dev/null 2> /dev/null 365 366 for try in $(seq 3); do 367 echo -n "PIN for GPG Card associated with device ${dev.device}: " 368 pin= 369 while true; do 370 if [ -e /crypt-ramfs/passphrase ]; then 371 echo "reused" 372 pin=$(cat /crypt-ramfs/passphrase) 373 break 374 else 375 # and try reading it from /dev/console with a timeout 376 IFS= read -t 1 -r pin 377 if [ -n "$pin" ]; then 378 ${if luks.reusePassphrases then '' 379 # remember it for the next device 380 echo -n "$pin" > /crypt-ramfs/passphrase 381 '' else '' 382 # Don't save it to ramfs. We are very paranoid 383 ''} 384 echo 385 break 386 fi 387 fi 388 done 389 echo -n "Verifying passphrase for ${dev.device}..." 390 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 391 if [ $? == 0 ]; then 392 echo " - success" 393 ${if luks.reusePassphrases then '' 394 # we don't rm here because we might reuse it for the next device 395 '' else '' 396 rm -f /crypt-ramfs/passphrase 397 ''} 398 break 399 else 400 echo " - failure" 401 # ask for a different one 402 rm -f /crypt-ramfs/passphrase 403 fi 404 done 405 406 [ "$opened" == false ] && die "Maximum authentication errors reached" 407 } 408 409 open_with_hardware() { 410 if wait_gpgcard ${toString dev.gpgCard.gracePeriod}; then 411 do_open_gpg_card 412 else 413 echo "No GPG Card found, falling back to normal open procedure" 414 open_normally 415 fi 416 } 417 ''} 418 419 ${optionalString (luks.fido2Support && (dev.fido2.credential != null)) '' 420 421 open_with_hardware() { 422 local passsphrase 423 424 ${if dev.fido2.passwordLess then '' 425 export passphrase="" 426 '' else '' 427 read -rsp "FIDO2 salt for ${dev.device}: " passphrase 428 echo 429 ''} 430 ${optionalString (lib.versionOlder kernelPackages.kernel.version "5.4") '' 431 echo "On systems with Linux Kernel < 5.4, it might take a while to initialize the CRNG, you might want to use linuxPackages_latest." 432 echo "Please move your mouse to create needed randomness." 433 ''} 434 echo "Waiting for your FIDO2 device..." 435 fido2luks open ${dev.device} ${dev.name} ${dev.fido2.credential} --await-dev ${toString dev.fido2.gracePeriod} --salt string:$passphrase 436 if [ $? -ne 0 ]; then 437 echo "No FIDO2 key found, falling back to normal open procedure" 438 open_normally 439 fi 440 } 441 ''} 442 443 # commands to run right before we mount our device 444 ${dev.preOpenCommands} 445 446 ${if (luks.yubikeySupport && (dev.yubikey != null)) || (luks.gpgSupport && (dev.gpgCard != null)) || (luks.fido2Support && (dev.fido2.credential != null)) then '' 447 open_with_hardware 448 '' else '' 449 open_normally 450 ''} 451 452 # commands to run right after we mounted our device 453 ${dev.postOpenCommands} 454 ''; 455 456 askPass = pkgs.writeScriptBin "cryptsetup-askpass" '' 457 #!/bin/sh 458 459 ${commonFunctions} 460 461 while true; do 462 wait_target "luks" /crypt-ramfs/device 10 "LUKS to request a passphrase" || die "Passphrase is not requested now" 463 device=$(cat /crypt-ramfs/device) 464 465 echo -n "Passphrase for $device: " 466 IFS= read -rs passphrase 467 echo 468 469 rm /crypt-ramfs/device 470 echo -n "$passphrase" > /crypt-ramfs/passphrase 471 done 472 ''; 473 474 preLVM = filterAttrs (n: v: v.preLVM) luks.devices; 475 postLVM = filterAttrs (n: v: !v.preLVM) luks.devices; 476 477in 478{ 479 imports = [ 480 (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "") 481 ]; 482 483 options = { 484 485 boot.initrd.luks.mitigateDMAAttacks = mkOption { 486 type = types.bool; 487 default = true; 488 description = '' 489 Unless enabled, encryption keys can be easily recovered by an attacker with physical 490 access to any machine with PCMCIA, ExpressCard, ThunderBolt or FireWire port. 491 More information is available at <link xlink:href="http://en.wikipedia.org/wiki/DMA_attack"/>. 492 493 This option blacklists FireWire drivers, but doesn't remove them. You can manually 494 load the drivers if you need to use a FireWire device, but don't forget to unload them! 495 ''; 496 }; 497 498 boot.initrd.luks.cryptoModules = mkOption { 499 type = types.listOf types.str; 500 default = 501 [ "aes" "aes_generic" "blowfish" "twofish" 502 "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512" 503 "af_alg" "algif_skcipher" 504 ]; 505 description = '' 506 A list of cryptographic kernel modules needed to decrypt the root device(s). 507 The default includes all common modules. 508 ''; 509 }; 510 511 boot.initrd.luks.forceLuksSupportInInitrd = mkOption { 512 type = types.bool; 513 default = false; 514 internal = true; 515 description = '' 516 Whether to configure luks support in the initrd, when no luks 517 devices are configured. 518 ''; 519 }; 520 521 boot.initrd.luks.reusePassphrases = mkOption { 522 type = types.bool; 523 default = true; 524 description = '' 525 When opening a new LUKS device try reusing last successful 526 passphrase. 527 528 Useful for mounting a number of devices that use the same 529 passphrase without retyping it several times. 530 531 Such setup can be useful if you use <command>cryptsetup 532 luksSuspend</command>. Different LUKS devices will still have 533 different master keys even when using the same passphrase. 534 ''; 535 }; 536 537 boot.initrd.luks.devices = mkOption { 538 default = { }; 539 example = { luksroot.device = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; }; 540 description = '' 541 The encrypted disk that should be opened before the root 542 filesystem is mounted. Both LVM-over-LUKS and LUKS-over-LVM 543 setups are supported. The unencrypted devices can be accessed as 544 <filename>/dev/mapper/<replaceable>name</replaceable></filename>. 545 ''; 546 547 type = with types; attrsOf (submodule ( 548 { name, ... }: { options = { 549 550 name = mkOption { 551 visible = false; 552 default = name; 553 example = "luksroot"; 554 type = types.str; 555 description = "Name of the unencrypted device in <filename>/dev/mapper</filename>."; 556 }; 557 558 device = mkOption { 559 example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; 560 type = types.str; 561 description = "Path of the underlying encrypted block device."; 562 }; 563 564 header = mkOption { 565 default = null; 566 example = "/root/header.img"; 567 type = types.nullOr types.str; 568 description = '' 569 The name of the file or block device that 570 should be used as header for the encrypted device. 571 ''; 572 }; 573 574 keyFile = mkOption { 575 default = null; 576 example = "/dev/sdb1"; 577 type = types.nullOr types.str; 578 description = '' 579 The name of the file (can be a raw device or a partition) that 580 should be used as the decryption key for the encrypted device. If 581 not specified, you will be prompted for a passphrase instead. 582 ''; 583 }; 584 585 keyFileSize = mkOption { 586 default = null; 587 example = 4096; 588 type = types.nullOr types.int; 589 description = '' 590 The size of the key file. Use this if only the beginning of the 591 key file should be used as a key (often the case if a raw device 592 or partition is used as key file). If not specified, the whole 593 <literal>keyFile</literal> will be used decryption, instead of just 594 the first <literal>keyFileSize</literal> bytes. 595 ''; 596 }; 597 598 keyFileOffset = mkOption { 599 default = null; 600 example = 4096; 601 type = types.nullOr types.int; 602 description = '' 603 The offset of the key file. Use this in combination with 604 <literal>keyFileSize</literal> to use part of a file as key file 605 (often the case if a raw device or partition is used as a key file). 606 If not specified, the key begins at the first byte of 607 <literal>keyFile</literal>. 608 ''; 609 }; 610 611 # FIXME: get rid of this option. 612 preLVM = mkOption { 613 default = true; 614 type = types.bool; 615 description = "Whether the luksOpen will be attempted before LVM scan or after it."; 616 }; 617 618 allowDiscards = mkOption { 619 default = false; 620 type = types.bool; 621 description = '' 622 Whether to allow TRIM requests to the underlying device. This option 623 has security implications; please read the LUKS documentation before 624 activating it. 625 This option is incompatible with authenticated encryption (dm-crypt 626 stacked over dm-integrity). 627 ''; 628 }; 629 630 bypassWorkqueues = mkOption { 631 default = false; 632 type = types.bool; 633 description = '' 634 Whether to bypass dm-crypt's internal read and write workqueues. 635 Enabling this should improve performance on SSDs; see 636 <link xlink:href="https://wiki.archlinux.org/index.php/Dm-crypt/Specialties#Disable_workqueue_for_increased_solid_state_drive_(SSD)_performance">here</link> 637 for more information. Needs Linux 5.9 or later. 638 ''; 639 }; 640 641 fallbackToPassword = mkOption { 642 default = false; 643 type = types.bool; 644 description = '' 645 Whether to fallback to interactive passphrase prompt if the keyfile 646 cannot be found. This will prevent unattended boot should the keyfile 647 go missing. 648 ''; 649 }; 650 651 gpgCard = mkOption { 652 default = null; 653 description = '' 654 The option to use this LUKS device with a GPG encrypted luks password by the GPG Smartcard. 655 If null (the default), GPG-Smartcard will be disabled for this device. 656 ''; 657 658 type = with types; nullOr (submodule { 659 options = { 660 gracePeriod = mkOption { 661 default = 10; 662 type = types.int; 663 description = "Time in seconds to wait for the GPG Smartcard."; 664 }; 665 666 encryptedPass = mkOption { 667 type = types.path; 668 description = "Path to the GPG encrypted passphrase."; 669 }; 670 671 publicKey = mkOption { 672 type = types.path; 673 description = "Path to the Public Key."; 674 }; 675 }; 676 }); 677 }; 678 679 fido2 = { 680 credential = mkOption { 681 default = null; 682 example = "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2"; 683 type = types.nullOr types.str; 684 description = "The FIDO2 credential ID."; 685 }; 686 687 gracePeriod = mkOption { 688 default = 10; 689 type = types.int; 690 description = "Time in seconds to wait for the FIDO2 key."; 691 }; 692 693 passwordLess = mkOption { 694 default = false; 695 type = types.bool; 696 description = '' 697 Defines whatever to use an empty string as a default salt. 698 699 Enable only when your device is PIN protected, such as <link xlink:href="https://trezor.io/">Trezor</link>. 700 ''; 701 }; 702 }; 703 704 yubikey = mkOption { 705 default = null; 706 description = '' 707 The options to use for this LUKS device in YubiKey-PBA. 708 If null (the default), YubiKey-PBA will be disabled for this device. 709 ''; 710 711 type = with types; nullOr (submodule { 712 options = { 713 twoFactor = mkOption { 714 default = true; 715 type = types.bool; 716 description = "Whether to use a passphrase and a YubiKey (true), or only a YubiKey (false)."; 717 }; 718 719 slot = mkOption { 720 default = 2; 721 type = types.int; 722 description = "Which slot on the YubiKey to challenge."; 723 }; 724 725 saltLength = mkOption { 726 default = 16; 727 type = types.int; 728 description = "Length of the new salt in byte (64 is the effective maximum)."; 729 }; 730 731 keyLength = mkOption { 732 default = 64; 733 type = types.int; 734 description = "Length of the LUKS slot key derived with PBKDF2 in byte."; 735 }; 736 737 iterationStep = mkOption { 738 default = 0; 739 type = types.int; 740 description = "How much the iteration count for PBKDF2 is increased at each successful authentication."; 741 }; 742 743 gracePeriod = mkOption { 744 default = 10; 745 type = types.int; 746 description = "Time in seconds to wait for the YubiKey."; 747 }; 748 749 /* TODO: Add to the documentation of the current module: 750 751 Options related to the storing the salt. 752 */ 753 storage = { 754 device = mkOption { 755 default = "/dev/sda1"; 756 type = types.path; 757 description = '' 758 An unencrypted device that will temporarily be mounted in stage-1. 759 Must contain the current salt to create the challenge for this LUKS device. 760 ''; 761 }; 762 763 fsType = mkOption { 764 default = "vfat"; 765 type = types.str; 766 description = "The filesystem of the unencrypted device."; 767 }; 768 769 path = mkOption { 770 default = "/crypt-storage/default"; 771 type = types.str; 772 description = '' 773 Absolute path of the salt on the unencrypted device with 774 that device's root directory as "/". 775 ''; 776 }; 777 }; 778 }; 779 }); 780 }; 781 782 preOpenCommands = mkOption { 783 type = types.lines; 784 default = ""; 785 example = '' 786 mkdir -p /tmp/persistent 787 mount -t zfs rpool/safe/persistent /tmp/persistent 788 ''; 789 description = '' 790 Commands that should be run right before we try to mount our LUKS device. 791 This can be useful, if the keys needed to open the drive is on another partion. 792 ''; 793 }; 794 795 postOpenCommands = mkOption { 796 type = types.lines; 797 default = ""; 798 example = '' 799 umount /tmp/persistent 800 ''; 801 description = '' 802 Commands that should be run right after we have mounted our LUKS device. 803 ''; 804 }; 805 }; 806 })); 807 }; 808 809 boot.initrd.luks.gpgSupport = mkOption { 810 default = false; 811 type = types.bool; 812 description = '' 813 Enables support for authenticating with a GPG encrypted password. 814 ''; 815 }; 816 817 boot.initrd.luks.yubikeySupport = mkOption { 818 default = false; 819 type = types.bool; 820 description = '' 821 Enables support for authenticating with a YubiKey on LUKS devices. 822 See the NixOS wiki for information on how to properly setup a LUKS device 823 and a YubiKey to work with this feature. 824 ''; 825 }; 826 827 boot.initrd.luks.fido2Support = mkOption { 828 default = false; 829 type = types.bool; 830 description = '' 831 Enables support for authenticating with FIDO2 devices. 832 ''; 833 }; 834 835 }; 836 837 config = mkIf (luks.devices != {} || luks.forceLuksSupportInInitrd) { 838 839 assertions = 840 [ { assertion = !(luks.gpgSupport && luks.yubikeySupport); 841 message = "YubiKey and GPG Card may not be used at the same time."; 842 } 843 844 { assertion = !(luks.gpgSupport && luks.fido2Support); 845 message = "FIDO2 and GPG Card may not be used at the same time."; 846 } 847 848 { assertion = !(luks.fido2Support && luks.yubikeySupport); 849 message = "FIDO2 and YubiKey may not be used at the same time."; 850 } 851 852 { assertion = any (dev: dev.bypassWorkqueues) (attrValues luks.devices) 853 -> versionAtLeast kernelPackages.kernel.version "5.9"; 854 message = "boot.initrd.luks.devices.<name>.bypassWorkqueues is not supported for kernels older than 5.9"; 855 } 856 ]; 857 858 # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested 859 boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks 860 ["firewire_ohci" "firewire_core" "firewire_sbp2"]; 861 862 # Some modules that may be needed for mounting anything ciphered 863 boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" "input_leds" ] 864 ++ luks.cryptoModules 865 # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged 866 # remove once 'modprobe --show-depends xts' shows ecb as a dependency 867 ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []); 868 869 # copy the cryptsetup binary and it's dependencies 870 boot.initrd.extraUtilsCommands = '' 871 copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup 872 copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass 873 sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass 874 875 ${optionalString luks.yubikeySupport '' 876 copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp 877 copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykinfo 878 copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl 879 880 cc -O3 -I${pkgs.openssl.dev}/include -L${pkgs.openssl.out}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto 881 strip -s pbkdf2-sha512 882 copy_bin_and_libs pbkdf2-sha512 883 884 mkdir -p $out/etc/ssl 885 cp -pdv ${pkgs.openssl.out}/etc/ssl/openssl.cnf $out/etc/ssl 886 887 cat > $out/bin/openssl-wrap <<EOF 888 #!$out/bin/sh 889 export OPENSSL_CONF=$out/etc/ssl/openssl.cnf 890 $out/bin/openssl "\$@" 891 EOF 892 chmod +x $out/bin/openssl-wrap 893 ''} 894 895 ${optionalString luks.fido2Support '' 896 copy_bin_and_libs ${pkgs.fido2luks}/bin/fido2luks 897 ''} 898 899 900 ${optionalString luks.gpgSupport '' 901 copy_bin_and_libs ${pkgs.gnupg}/bin/gpg 902 copy_bin_and_libs ${pkgs.gnupg}/bin/gpg-agent 903 copy_bin_and_libs ${pkgs.gnupg}/libexec/scdaemon 904 905 ${concatMapStringsSep "\n" (x: 906 if x.gpgCard != null then 907 '' 908 mkdir -p $out/secrets/gpg-keys/${x.device} 909 cp -a ${x.gpgCard.encryptedPass} $out/secrets/gpg-keys/${x.device}/cryptkey.gpg 910 cp -a ${x.gpgCard.publicKey} $out/secrets/gpg-keys/${x.device}/pubkey.asc 911 '' 912 else "" 913 ) (attrValues luks.devices) 914 } 915 ''} 916 ''; 917 918 boot.initrd.extraUtilsCommandsTest = '' 919 $out/bin/cryptsetup --version 920 ${optionalString luks.yubikeySupport '' 921 $out/bin/ykchalresp -V 922 $out/bin/ykinfo -V 923 $out/bin/openssl-wrap version 924 ''} 925 ${optionalString luks.gpgSupport '' 926 $out/bin/gpg --version 927 $out/bin/gpg-agent --version 928 $out/bin/scdaemon --version 929 ''} 930 ${optionalString luks.fido2Support '' 931 $out/bin/fido2luks --version 932 ''} 933 ''; 934 935 boot.initrd.preFailCommands = postCommands; 936 boot.initrd.preLVMCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands; 937 boot.initrd.postDeviceCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands; 938 939 environment.systemPackages = [ pkgs.cryptsetup ]; 940 }; 941}