Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos: initrd/luks: allow to reuse passphrases, cleanup #29441

Merged
merged 6 commits into from Aug 8, 2018

Conversation

oxij
Copy link
Member

@oxij oxij commented Sep 15, 2017

Motivation for this change

I hate typing long passphrases multiple times.

Works for me, but marking this as [WIP] because

  • you should test it before merging, especially because nobody here has a Yubikey.
  • there were some arguments against using read -t 1 -rs like "What happens if you type a char exactly when read fails on timeout? Would it echo a piece of password?" I tested it extensively and couldn't make it echo anything, but feel free to voice your opinion on that.
Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option build-use-sandbox in nix.conf on non-NixOS)
  • Built on platform(s)
    • NixOS
    • macOS
    • Linux
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nox --run "nox-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Fits CONTRIBUTING.md.

/cc @primeos @Calrama @Phreedom @abbradar from git blame

@oxij oxij changed the title Nixos/luks [WIP] nixos: changes to initrd luks Sep 15, 2017
@rnhmjoj
Copy link
Contributor

rnhmjoj commented Mar 2, 2018

Status? This seems really useful.

@abbradar
Copy link
Member

abbradar commented Mar 2, 2018

I'd like to test this but I don't have a Yubikey (that was a problem for me too when I made my changes to this code). Is there someone with the hardware who uses LUKS and doesn't mind to get pinged if a test is needed?

@oxij
Copy link
Member Author

oxij commented Mar 2, 2018 via email

@oxij
Copy link
Member Author

oxij commented Mar 6, 2018

Rebased onto master.

@oxij oxij changed the title [WIP] nixos: changes to initrd luks nixos: changes to initrd luks Jun 11, 2018
@oxij
Copy link
Member Author

oxij commented Jun 11, 2018

I reworked the thing to resolve the input echo issue (turned out to be super-easy). I also added syntax checks to stage-1 build and checked that my Yubikey changes pass it. And tested booting, still works flawlessly for me.

I think this can now be merged, especially since I'm absolutely sure nobody uses that Yubikey code anyway because it is broken on master (see commit messages), hence unmarking this as [WIP].

@oxij oxij changed the title nixos: changes to initrd luks nixos: initrd/luks: allow to reuse passphrases, cleanup Jun 11, 2018
@oxij
Copy link
Member Author

oxij commented Jul 23, 2018

ping @matthewbauer @rnhmjoj @abbradar

@kalbasit
Copy link
Member

kalbasit commented Jul 24, 2018

@oxij is there a reason why you are not using keyfiles? I currently have a 3MB partition encrypted with LUKS and once it's unlocked, the corresponding /dev/mapper device is used for the rest of my partitions. Relevant hardware section:

  boot.initrd.luks.devices = {
    cryptkey = {
      device = "/dev/disk/by-uuid/3830842b-a589-45e0-a51d-4ed516472575";
    };

    cryptroot = {
      device = "/dev/disk/by-uuid/ec133595-6294-4756-95ea-3891297f6477";
      keyFile = "/dev/mapper/cryptkey";
    };

    cryptswap = {
      device = "/dev/disk/by-uuid/71aab354-3872-4316-a26c-b4090d73275c";
      keyFile = "/dev/mapper/cryptkey";
    };
  };

With this technique, I only have to type my password once and the rest of the devices use the shared partition as the key-file.

@oxij
Copy link
Member Author

oxij commented Jul 24, 2018 via email

@@ -243,6 +243,11 @@ let

isExecutable = true;

postInstall = ''
echo checking syntax
${pkgs.bash}/bin/sh -n $target

This comment was marked as resolved.

@samueldr
Copy link
Member

samueldr commented Aug 5, 2018

Just validated, works on my machine™ when applied on top of current nixos-unstable (even though there were no doubts about it).

My setup is:

  • One partition.
  • Passphrase for decryption.

@oxij
Copy link
Member Author

oxij commented Aug 5, 2018 via email

@samueldr
Copy link
Member

samueldr commented Aug 5, 2018

@oxij I first checked empirically whether ash verified with -n before adkin. It did, even though it's missing from its --help output.

Additionally, here's the part where it happens:

This is as described in The Open Group Base Specifications Issue 7, 2018 edition, as linked from the busybox shell's readme

-n

The shell shall read commands but does not execute them; this can be used to check for shell script syntax errors. An interactive shell may ignore this option.

@rnhmjoj
Copy link
Contributor

rnhmjoj commented Aug 6, 2018

@oxij I can't say much because I don't use LUKS but this is a really useful feature and it has been verified to work so yes, I would just merge this. 👍 from me for what it's worth.

@oxij
Copy link
Member Author

oxij commented Aug 6, 2018 via email

Copy link
Member

@samueldr samueldr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, a final review. Took more time to actually take in the changed code, and not just run it.

I'm asking for release notes as this removes options. I do understand that it is dubious they worked in the first place, so it may be hard to write such notes. If you can't write-up any notes, you can reply with everything you have in mind with those options and I'll try to write-up something appropriate.


I would like to add that I called for help for testing with yubikey hardware on the IRC channel, but I have low hopes about that. Searching on google finds about zero users having this option set within public configs. The only user having said option active is within a configuration untouched since 2014.

Finally, it looks like the previous maintainer of that code left around 2014, recently removed itself from maintainers. It's possible that changes between then and now caused the code to rot.

description = "Time in seconds to wait before attempting to find the Yubikey.";
};

ramfsMountPoint = mkOption {

This comment was marked as resolved.

@@ -414,12 +509,6 @@ in
description = "The filesystem of the unencrypted device.";
};

mountPoint = mkOption {

This comment was marked as resolved.

type = types.str;
description = "Path where the unencrypted device will be mounted during early boot.";
};

path = mkOption {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: (nothing to change here)

The path is written as if an absolute path, bit this seems to be relative to the root of the yubikey. The semantics of the option did not change with the updated code. The only difference I can see is that the mount point is now hard-coded to /crypt-storage instead of being an option, but *the code will look at /crypt-storage/crypt-storage/default using the default options, both previously and now.

I'm leaving this note as I was about to ask for changes in the confusion this caused.

@oxij
Copy link
Member Author

oxij commented Aug 6, 2018 via email

@oxij
Copy link
Member Author

oxij commented Aug 6, 2018

Also fixed a typo and a tiny bug. LGTM.

@samueldr
Copy link
Member

samueldr commented Aug 7, 2018

Aww, I hate to be the bearer of bad news:

Two PRs were merged (one fixing an issue in the other :/) today, and the first one added a feature.

I don't think the merge will be too hard. Tell me if you won't or can't merge.

I was about to merge this to master since seemingly nobody will be able to test the Yubikey support (tried getting in touch with a possible user, they didn't reply).

Once updated, I'll do one last build+test to confirm everything's in working order and merge.

@infinisil
Copy link
Member

Sorry for that merge! I didn't see this PR until @samueldr told me about it.

Here's a patch for the feature in master, adapted to this PR:

diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index f44ff0bd3cc..27c1f891f48 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -91,7 +91,7 @@ let
     umount /crypt-ramfs 2>/dev/null
   '';
 
-  openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, fallbackToPassword, ... }: assert name' == name;
+  openCommand = name': { name, device, header, keyFile, keyFileSize, keyFileOffset, allowDiscards, yubikey, fallbackToPassword, ... }: assert name' == 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}"}";
@@ -155,7 +155,9 @@ let
     open_normally() {
         ${if (keyFile != null) then ''
         if wait_target "key file" ${keyFile}; then
-            ${csopen} --key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}
+            ${csopen} --key-file=${keyFile} \
+              ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"} \
+              ${optionalString (keyFileOffset != null) "--keyfile-offset=${toString keyFileOffset}"}
         else
             ${if fallbackToPassword then "echo" else "die"} "${keyFile} is unavailable"
             echo " - failing back to interactive password prompt"
@@ -417,6 +419,19 @@ in
             '';
           };
 
+          keyFileOffset = mkOption {
+            default = null;
+            example = 4096;
+            type = types.nullOr types.int;
+            description = ''
+              The offset of the key file. Use this in combination with
+              <literal>keyFileSize</literal> to use part of a file as key file
+              (often the case if a raw device or partition is used as a key file).
+              If not specified, the key begins at the first byte of
+              <literal>keyFile</literal>.
+            '';
+          };
+
           # FIXME: get rid of this option.
           preLVM = mkOption {
             default = true;

Also reuse common cryptsetup invocation subexpressions.

- Passphrase reading is done via the shell now, not by cryptsetup.
  This way the same passphrase can be reused between cryptsetup
  invocations, which this module now tries to do by default (can be
  disabled).
- Number of retries is now infinity, it makes no sense to make users
  reboot when they fail to type in their passphrase.
From reading the source I'm pretty sure it doesn't support multiple Yubikeys, hence
those options are useless.

Also, I'm pretty sure nobody actually uses this feature, because enabling it causes
extra utils' checks to fail (even before applying any patches of this branch).

As I don't have the hardware to test this, I'm too lazy to fix the utils, but
I did test that with extra utils checks commented out and Yubikey
enabled the resulting script still passes the syntax check.
@oxij
Copy link
Member Author

oxij commented Aug 8, 2018 via email

@oxij
Copy link
Member Author

oxij commented Aug 8, 2018 via email

@samueldr
Copy link
Member

samueldr commented Aug 8, 2018

I confirm, built overnight on top of master and worked.

@samueldr samueldr merged commit 27c6bf0 into NixOS:master Aug 8, 2018
@oxij
Copy link
Member Author

oxij commented Aug 8, 2018 via email

aszlig added a commit that referenced this pull request Aug 10, 2018
Since a9d69a7, the passphrase prompt
now no longer starts with "Enter passphrase for" but now it's just
"Passphrase for", which causes the luksroot installer test to fail.

I've tested this on a x86_64-linux machine and the test now succeeds.

Signed-off-by: aszlig <aszlig@nix.build>
Cc: @oxij, @samueldr
Issue: #29441
if [ -n "$passphrase" ]; then
${if luks.reusePassphrases then ''
# remember it for the next device
echo -n "$passphrase" > /crypt-ramfs/passphrase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oxij: Keep in mind that this might leave artifacts of the passphrase in memory. In cryptsetup, the memory for the passphrase is explicitly zeroed - here it is not - plus I wouldn't trust the shell to actually zero the memory even if you unset or overwrite the passphrase variable.

@oxij
Copy link
Member Author

oxij commented Aug 10, 2018 via email

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/disk-encryption-on-nixos-servers-how-when-to-unlock/5030/20

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants