diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index fa14d86e253d..8c82b4bcf5db 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -140,24 +140,27 @@ let
umount /crypt-ramfs 2>/dev/null
'';
- openCommand = name': { name, device, header, keyFile, keyFileSize, keyFileOffset, allowDiscards, yubikey, gpgCard, fido2, fallbackToPassword, preOpenCommands, postOpenCommands,... }: assert name' == name;
+ openCommand = name: dev: assert name == dev.name;
let
- csopen = "cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} ${optionalString (header != null) "--header=${header}"}";
- cschange = "cryptsetup luksChangeKey ${device} ${optionalString (header != null) "--header=${header}"}";
+ csopen = "cryptsetup luksOpen ${dev.device} ${dev.name}"
+ + optionalString dev.allowDiscards " --allow-discards"
+ + optionalString dev.bypassWorkqueues " --perf-no_read_workqueue --perf-no_write_workqueue"
+ + optionalString (dev.header != null) " --header=${dev.header}";
+ cschange = "cryptsetup luksChangeKey ${dev.device} ${optionalString (dev.header != null) "--header=${dev.header}"}";
in ''
# Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g.
# if on a USB drive.
- wait_target "device" ${device} || die "${device} is unavailable"
+ wait_target "device" ${dev.device} || die "${dev.device} is unavailable"
- ${optionalString (header != null) ''
- wait_target "header" ${header} || die "${header} is unavailable"
+ ${optionalString (dev.header != null) ''
+ wait_target "header" ${dev.header} || die "${dev.header} is unavailable"
''}
do_open_passphrase() {
local passphrase
while true; do
- echo -n "Passphrase for ${device}: "
+ echo -n "Passphrase for ${dev.device}: "
passphrase=
while true; do
if [ -e /crypt-ramfs/passphrase ]; then
@@ -166,7 +169,7 @@ let
break
else
# ask cryptsetup-askpass
- echo -n "${device}" > /crypt-ramfs/device
+ echo -n "${dev.device}" > /crypt-ramfs/device
# and try reading it from /dev/console with a timeout
IFS= read -t 1 -r passphrase
@@ -182,7 +185,7 @@ let
fi
fi
done
- echo -n "Verifying passphrase for ${device}..."
+ echo -n "Verifying passphrase for ${dev.device}..."
echo -n "$passphrase" | ${csopen} --key-file=-
if [ $? == 0 ]; then
echo " - success"
@@ -202,13 +205,13 @@ let
# LUKS
open_normally() {
- ${if (keyFile != null) then ''
- if wait_target "key file" ${keyFile}; then
- ${csopen} --key-file=${keyFile} \
- ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"} \
- ${optionalString (keyFileOffset != null) "--keyfile-offset=${toString keyFileOffset}"}
+ ${if (dev.keyFile != null) then ''
+ if wait_target "key file" ${dev.keyFile}; then
+ ${csopen} --key-file=${dev.keyFile} \
+ ${optionalString (dev.keyFileSize != null) "--keyfile-size=${toString dev.keyFileSize}"} \
+ ${optionalString (dev.keyFileOffset != null) "--keyfile-offset=${toString dev.keyFileOffset}"}
else
- ${if fallbackToPassword then "echo" else "die"} "${keyFile} is unavailable"
+ ${if dev.fallbackToPassword then "echo" else "die"} "${dev.keyFile} is unavailable"
echo " - failing back to interactive password prompt"
do_open_passphrase
fi
@@ -217,7 +220,7 @@ let
''}
}
- ${optionalString (luks.yubikeySupport && (yubikey != null)) ''
+ ${optionalString (luks.yubikeySupport && (dev.yubikey != null)) ''
# YubiKey
rbtohex() {
( od -An -vtx1 | tr -d ' \n' )
@@ -243,16 +246,16 @@ let
local new_response
local new_k_luks
- mount -t ${yubikey.storage.fsType} ${yubikey.storage.device} /crypt-storage || \
+ mount -t ${dev.yubikey.storage.fsType} ${dev.yubikey.storage.device} /crypt-storage || \
die "Failed to mount YubiKey salt storage device"
- salt="$(cat /crypt-storage${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
- iterations="$(cat /crypt-storage${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
+ salt="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 1p | tr -d '\n')"
+ iterations="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 2p | tr -d '\n')"
challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
- response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
+ response="$(ykchalresp -${toString dev.yubikey.slot} -x $challenge 2>/dev/null)"
for try in $(seq 3); do
- ${optionalString yubikey.twoFactor ''
+ ${optionalString dev.yubikey.twoFactor ''
echo -n "Enter two-factor passphrase: "
k_user=
while true; do
@@ -278,9 +281,9 @@ let
''}
if [ ! -z "$k_user" ]; then
- k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+ k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)"
else
- k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+ k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)"
fi
echo -n "$k_luks" | hextorb | ${csopen} --key-file=-
@@ -302,7 +305,7 @@ let
[ "$opened" == false ] && die "Maximum authentication errors reached"
echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
- for i in $(seq ${toString yubikey.saltLength}); do
+ for i in $(seq ${toString dev.yubikey.saltLength}); do
byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
new_salt="$new_salt$byte";
echo -n .
@@ -310,25 +313,25 @@ let
echo "ok"
new_iterations="$iterations"
- ${optionalString (yubikey.iterationStep > 0) ''
- new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
+ ${optionalString (dev.yubikey.iterationStep > 0) ''
+ new_iterations="$(($new_iterations + ${toString dev.yubikey.iterationStep}))"
''}
new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
- new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
+ new_response="$(ykchalresp -${toString dev.yubikey.slot} -x $new_challenge 2>/dev/null)"
if [ ! -z "$k_user" ]; then
- new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+ new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
else
- new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+ new_k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
fi
echo -n "$new_k_luks" | hextorb > /crypt-ramfs/new_key
echo -n "$k_luks" | hextorb | ${cschange} --key-file=- /crypt-ramfs/new_key
if [ $? == 0 ]; then
- echo -ne "$new_salt\n$new_iterations" > /crypt-storage${yubikey.storage.path}
+ echo -ne "$new_salt\n$new_iterations" > /crypt-storage${dev.yubikey.storage.path}
else
echo "Warning: Could not update LUKS key, current challenge persists!"
fi
@@ -338,7 +341,7 @@ let
}
open_with_hardware() {
- if wait_yubikey ${toString yubikey.gracePeriod}; then
+ if wait_yubikey ${toString dev.yubikey.gracePeriod}; then
do_open_yubikey
else
echo "No YubiKey found, falling back to non-YubiKey open procedure"
@@ -347,7 +350,7 @@ let
}
''}
- ${optionalString (luks.gpgSupport && (gpgCard != null)) ''
+ ${optionalString (luks.gpgSupport && (dev.gpgCard != null)) ''
do_open_gpg_card() {
# Make all of these local to this function
@@ -355,12 +358,12 @@ let
local pin
local opened
- gpg --import /gpg-keys/${device}/pubkey.asc > /dev/null 2> /dev/null
+ gpg --import /gpg-keys/${dev.device}/pubkey.asc > /dev/null 2> /dev/null
gpg --card-status > /dev/null 2> /dev/null
for try in $(seq 3); do
- echo -n "PIN for GPG Card associated with device ${device}: "
+ echo -n "PIN for GPG Card associated with device ${dev.device}: "
pin=
while true; do
if [ -e /crypt-ramfs/passphrase ]; then
@@ -382,8 +385,8 @@ let
fi
fi
done
- echo -n "Verifying passphrase for ${device}..."
- echo -n "$pin" | gpg -q --batch --passphrase-fd 0 --pinentry-mode loopback -d /gpg-keys/${device}/cryptkey.gpg 2> /dev/null | ${csopen} --key-file=- > /dev/null 2> /dev/null
+ echo -n "Verifying passphrase for ${dev.device}..."
+ 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
if [ $? == 0 ]; then
echo " - success"
${if luks.reusePassphrases then ''
@@ -403,7 +406,7 @@ let
}
open_with_hardware() {
- if wait_gpgcard ${toString gpgCard.gracePeriod}; then
+ if wait_gpgcard ${toString dev.gpgCard.gracePeriod}; then
do_open_gpg_card
else
echo "No GPG Card found, falling back to normal open procedure"
@@ -412,15 +415,15 @@ let
}
''}
- ${optionalString (luks.fido2Support && (fido2.credential != null)) ''
+ ${optionalString (luks.fido2Support && (dev.fido2.credential != null)) ''
open_with_hardware() {
local passsphrase
- ${if fido2.passwordLess then ''
+ ${if dev.fido2.passwordLess then ''
export passphrase=""
'' else ''
- read -rsp "FIDO2 salt for ${device}: " passphrase
+ read -rsp "FIDO2 salt for ${dev.device}: " passphrase
echo
''}
${optionalString (lib.versionOlder kernelPackages.kernel.version "5.4") ''
@@ -428,7 +431,7 @@ let
echo "Please move your mouse to create needed randomness."
''}
echo "Waiting for your FIDO2 device..."
- fido2luks open ${device} ${name} ${fido2.credential} --await-dev ${toString fido2.gracePeriod} --salt string:$passphrase
+ fido2luks open ${dev.device} ${dev.name} ${dev.fido2.credential} --await-dev ${toString dev.fido2.gracePeriod} --salt string:$passphrase
if [ $? -ne 0 ]; then
echo "No FIDO2 key found, falling back to normal open procedure"
open_normally
@@ -437,16 +440,16 @@ let
''}
# commands to run right before we mount our device
- ${preOpenCommands}
+ ${dev.preOpenCommands}
- ${if (luks.yubikeySupport && (yubikey != null)) || (luks.gpgSupport && (gpgCard != null)) || (luks.fido2Support && (fido2.credential != null)) then ''
+ ${if (luks.yubikeySupport && (dev.yubikey != null)) || (luks.gpgSupport && (dev.gpgCard != null)) || (luks.fido2Support && (dev.fido2.credential != null)) then ''
open_with_hardware
'' else ''
open_normally
''}
# commands to run right after we mounted our device
- ${postOpenCommands}
+ ${dev.postOpenCommands}
'';
askPass = pkgs.writeScriptBin "cryptsetup-askpass" ''
@@ -621,6 +624,17 @@ in
'';
};
+ bypassWorkqueues = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to bypass dm-crypt's internal read and write workqueues.
+ Enabling this should improve performance on SSDs; see
+ here
+ for more information. Needs Linux 5.9 or later.
+ '';
+ };
+
fallbackToPassword = mkOption {
default = false;
type = types.bool;
@@ -833,6 +847,11 @@ in
{ assertion = !(luks.fido2Support && luks.yubikeySupport);
message = "FIDO2 and YubiKey may not be used at the same time.";
}
+
+ { assertion = any (dev: dev.bypassWorkqueues) (attrValues luks.devices)
+ -> versionAtLeast kernelPackages.kernel.version "5.9";
+ message = "boot.initrd.luks.devices..bypassWorkqueues is not supported for kernels older than 5.9";
+ }
];
# actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested