diff options
author | Moritz Maxeiner <moritz@ucworks.org> | 2014-01-25 03:27:12 +0100 |
---|---|---|
committer | Moritz Maxeiner <moritz@ucworks.org> | 2014-01-25 03:33:09 +0100 |
commit | 333f5caaf91ce1c28586483774a60bf385e19a32 (patch) | |
tree | 48989adcd2a53d94a18f0354971005149370129c /nixos/modules/system/boot/luksroot.nix | |
parent | 8f9300fb0ee63c2e0ebc0080346c15dbb26a72ee (diff) | |
download | nixpkgs-333f5caaf91ce1c28586483774a60bf385e19a32.tar nixpkgs-333f5caaf91ce1c28586483774a60bf385e19a32.tar.gz nixpkgs-333f5caaf91ce1c28586483774a60bf385e19a32.tar.bz2 nixpkgs-333f5caaf91ce1c28586483774a60bf385e19a32.tar.lz nixpkgs-333f5caaf91ce1c28586483774a60bf385e19a32.tar.xz nixpkgs-333f5caaf91ce1c28586483774a60bf385e19a32.tar.zst nixpkgs-333f5caaf91ce1c28586483774a60bf385e19a32.zip |
Implement authentication for a LUKS device with a yubikey (HMAC-SHA1); supports simple challenge-response and two-factor authentication
Diffstat (limited to 'nixos/modules/system/boot/luksroot.nix')
-rw-r--r-- | nixos/modules/system/boot/luksroot.nix | 220 |
1 files changed, 218 insertions, 2 deletions
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix index ba357f5d2de..ca4a8c480f8 100644 --- a/nixos/modules/system/boot/luksroot.nix +++ b/nixos/modules/system/boot/luksroot.nix @@ -5,7 +5,7 @@ with pkgs.lib; let luks = config.boot.initrd.luks; - openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: '' + openCommand = { name, device, keyFile, keyFileSize, allowDiscards, yubikey, ... }: '' # Wait for luksRoot to appear, e.g. if on a usb drive. # XXX: copied and adapted from stage-1-init.sh - should be # available as a function. @@ -31,9 +31,141 @@ let fi ''} + ${optionalString (luks.yubikeySupport && (yubikey != null)) '' + mkdir -p ${yubikey.challenge.mountPoint} + mount -t ${yubikey.challenge.fsType} ${toString yubikey.challenge.device} ${yubikey.challenge.mountPoint} + response="$(ykchalresp -${toString yubikey.yubikeySlot} "`cat ${yubikey.challenge.mountPoint}${yubikey.challenge.file}`" 2>/dev/null || true)" + if [ -z "$response" ]; then + echo -n "waiting 10 seconds for yubikey to appear..." + for try in $(seq 10); do + sleep 1 + response="$(ykchalresp -${toString yubikey.yubikeySlot} "`cat ${yubikey.challenge.mountPoint}${yubikey.challenge.file}`" 2>/dev/null || true)" + if [ ! -z "$response" ]; then break; fi + echo -n . + done + echo "ok" + fi + + ${optionalString yubikey.twoFactor '' + if [ ! -z "$response" ]; then + echo -n "Enter two-factor passphrase: " + read -s passphrase + current_key="$passphrase$response" + fi + ''} + + ${optionalString (!yubikey.twoFactor) '' + if [ ! -z "$response" ]; then + current_key="$response" + fi + ''} + ''} + # open luksRoot and scan for logical volumes + ${optionalString ((!luks.yubikeySupport) || (yubikey == null)) '' cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \ ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"} + ''} + + ${optionalString (luks.yubikeySupport && (yubikey != null)) '' + if [ -z "$response" ]; then + cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \ + ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"} + else + echo $current_key | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=- + + if [ $? != "0" ]; then + for try in $(seq 3); do + + ${optionalString (!yubikey.twoFactor) '' + sleep 1 + ''} + + ${optionalString yubikey.twoFactor '' + echo -n "Enter two-factor passphrase: " + read -s passphrase + current_key="$passphrase$response" + ''} + + echo $current_key | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=- + if [ $? == "0" ]; then break; fi + echo -n . + done + fi + + mkdir -p ${yubikey.ramfsMountPoint} + # A ramfs is used here to ensure that the file used to update + # the key slot with cryptsetup will never get swapped out. + # Warning: Do NOT replace with tmpfs! + mount -t ramfs none ${yubikey.ramfsMountPoint} + + update_failed=false + old_challenge=$(cat ${yubikey.challenge.mountPoint}${yubikey.challenge.file}) + + new_challenge=$(uuidgen) + if [ $? != "0" ]; then + for try in $(seq 10); do + sleep 1 + new_challenge=$(uuidgen) + if [ $? == "0" ]; then break; fi + if [ $try -eq 10 ]; then update_failed=true; fi + done + fi + + if [ "$update_failed" == false ]; then + echo $new_challenge > ${yubikey.challenge.mountPoint}${yubikey.challenge.file} + response="$(ykchalresp -${toString yubikey.yubikeySlot} "`cat ${yubikey.challenge.mountPoint}${yubikey.challenge.file}`" 2>/dev/null || true)" + if [ -z "$response" ]; then + echo -n "waiting 10 seconds for yubikey to appear..." + for try in $(seq 10); do + sleep 1 + response="$(ykchalresp -${toString yubikey.yubikeySlot} "`cat ${yubikey.challenge.mountPoint}${yubikey.challenge.file}`" 2>/dev/null || true)" + if [ ! -z "$response" ]; then break; fi + echo -n . + done + echo "ok"; + fi + + if [ ! -z "$response" ]; then + ${optionalString yubikey.twoFactor '' + new_key="$passphrase$response" + ''} + + ${optionalString (!yubikey.twoFactor) '' + new_key="$response" + ''} + + echo $new_key > ${yubikey.ramfsMountPoint}/new_key + + echo $current_key | cryptsetup luksChangeKey ${device} --key-file=- --key-slot ${toString yubikey.luksKeySlot} ${yubikey.ramfsMountPoint}/new_key + if [ $? != "0" ]; then + for try in $(seq 10); do + sleep 1 + echo $current_key | cryptsetup luksChangeKey ${device} --key-file=- --key-slot ${toString yubikey.luksKeySlot} ${yubikey.ramfsMountPoint}/new_key + if [ $? == "0" ]; then break; fi + if [ $try -eq 10 ]; then update_failed=true; fi + done + + fi + + rm -f ${yubikey.ramfsMountPoint}/new_key + + if [ "$update_failed" == true ]; then + echo $old_challenge > ${yubikey.challenge.mountPoint}${yubikey.challenge.file} + echo "Warning: Could not update luks header with new key for ${device}, old challenge restored!" + fi + else + echo $old_challenge > ${yubikey.challenge.mountPoint}${yubikey.challenge.file} + echo "Warning: No yubikey present to challenge for ${device}, old challenge restored!" + fi + else + echo "Warning: New challenge could not be obtained for ${device}, old challenge persists!" + fi + + umount ${yubikey.ramfsMountPoint} + umount ${yubikey.challenge.mountPoint} + fi + ''} ''; isPreLVM = f: f.preLVM; @@ -139,10 +271,77 @@ in ''; }; - }; + yubikey = mkOption { + default = null; + type = types.nullOr types.optionSet; + description = "TODO"; + + options = { + twoFactor = mkOption { + default = false; + type = types.bool; + description = "TODO"; + }; + + yubikeySlot = mkOption { + default = 2; + type = types.int; + description = "TODO"; + }; + + luksKeySlot = mkOption { + default = 1; + type = types.int; + description = "TODO"; + }; + + challenge = mkOption { + type = types.optionSet; + description = "TODO"; + + options = { + device = mkOption { + default = /dev/sda1; + type = types.path; + description = "TODO"; + }; + + fsType = mkOption { + default = "vfat"; + type = types.string; + description = "TODO"; + }; + + mountPoint = mkOption { + default = "/crypt-challenge"; + type = types.string; + description = "TODO"; + }; + + file = mkOption { + default = "/crypt-challenge"; + type = types.string; + description = "TODO"; + }; + }; + }; + + ramfsMountPoint = mkOption { + default = "/crypt-update"; + type = types.string; + description = "TODO"; + }; + }; + }; + }; }; + boot.initrd.luks.yubikeySupport = mkOption { + default = false; + type = types.bool; + description = "TODO"; + }; }; config = mkIf (luks.devices != []) { @@ -162,10 +361,27 @@ in cp -pdvn $lib $out/lib cp -pvn $(readlink -f $lib) $out/lib done + ${optionalString luks.yubikeySupport '' + cp -pdv ${pkgs.utillinux}/bin/uuidgen $out/bin + for lib in $(ldd $out/bin/uuidgen |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do + cp -pdvn $lib $out/lib + cp -pvn $(readlink -f $lib) $out/lib + done + + cp -pdv ${pkgs.ykpers}/bin/ykchalresp $out/bin + for lib in $(ldd $out/bin/ykchalresp |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do + cp -pdvn $lib $out/lib + cp -pvn $(readlink -f $lib) $out/lib + done + ''} ''; boot.initrd.extraUtilsCommandsTest = '' $out/bin/cryptsetup --version + ${optionalString luks.yubikeySupport '' + $out/bin/uuidgen --version + $out/bin/ykchalresp -V + ''} ''; boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM; |