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