mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-23 15:33:13 +00:00
Merge pull request #168554 from helsinki-systems/feat/systemd-stage-1-luks
nixos/systemd-stage-1: Add basic LUKS support
This commit is contained in:
commit
b23ec41f14
@ -1,10 +1,11 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, options, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
luks = config.boot.initrd.luks;
|
||||
kernelPackages = config.boot.kernelPackages;
|
||||
defaultPrio = (mkOptionDefault {}).priority;
|
||||
|
||||
commonFunctions = ''
|
||||
die() {
|
||||
@ -474,6 +475,16 @@ let
|
||||
preLVM = filterAttrs (n: v: v.preLVM) luks.devices;
|
||||
postLVM = filterAttrs (n: v: !v.preLVM) luks.devices;
|
||||
|
||||
stage1Crypttab = pkgs.writeText "initrd-crypttab" (lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: let
|
||||
opts = v.crypttabExtraOpts
|
||||
++ optional v.allowDiscards "discard"
|
||||
++ optionals v.bypassWorkqueues [ "no-read-workqueue" "no-write-workqueue" ]
|
||||
++ optional (v.header != null) "header=${v.header}"
|
||||
++ optional (v.keyFileOffset != null) "keyfile-offset=${v.keyFileOffset}"
|
||||
++ optional (v.keyFileSize != null) "keyfile-size=${v.keyFileSize}"
|
||||
;
|
||||
in "${n} ${v.device} ${if v.keyFile == null then "-" else v.keyFile} ${lib.concatStringsSep "," opts}") luks.devices));
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@ -802,6 +813,18 @@ in
|
||||
Commands that should be run right after we have mounted our LUKS device.
|
||||
'';
|
||||
};
|
||||
|
||||
crypttabExtraOpts = mkOption {
|
||||
type = with types; listOf singleLineStr;
|
||||
default = [];
|
||||
example = [ "_netdev" ];
|
||||
visible = false;
|
||||
description = ''
|
||||
Only used with systemd stage 1.
|
||||
|
||||
Extra options to append to the last column of the generated crypttab file.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
@ -853,6 +876,31 @@ in
|
||||
-> versionAtLeast kernelPackages.kernel.version "5.9";
|
||||
message = "boot.initrd.luks.devices.<name>.bypassWorkqueues is not supported for kernels older than 5.9";
|
||||
}
|
||||
|
||||
{ assertion = config.boot.initrd.systemd.enable -> all (dev: !dev.fallbackToPassword) (attrValues luks.devices);
|
||||
message = "boot.initrd.luks.devices.<name>.fallbackToPassword is implied by systemd stage 1.";
|
||||
}
|
||||
{ assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preLVM) (attrValues luks.devices);
|
||||
message = "boot.initrd.luks.devices.<name>.preLVM is not used by systemd stage 1.";
|
||||
}
|
||||
{ assertion = config.boot.initrd.systemd.enable -> options.boot.initrd.luks.reusePassphrases.highestPrio == defaultPrio;
|
||||
message = "boot.initrd.luks.reusePassphrases has no effect with systemd stage 1.";
|
||||
}
|
||||
{ assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preOpenCommands == "" && dev.postOpenCommands == "") (attrValues luks.devices);
|
||||
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.";
|
||||
}
|
||||
# TODO
|
||||
{ assertion = config.boot.initrd.systemd.enable -> !luks.gpgSupport;
|
||||
message = "systemd stage 1 does not support GPG smartcards yet.";
|
||||
}
|
||||
# TODO
|
||||
{ assertion = config.boot.initrd.systemd.enable -> !luks.fido2Support;
|
||||
message = "systemd stage 1 does not support FIDO2 yet.";
|
||||
}
|
||||
# TODO
|
||||
{ assertion = config.boot.initrd.systemd.enable -> !luks.yubikeySupport;
|
||||
message = "systemd stage 1 does not support Yubikeys yet.";
|
||||
}
|
||||
];
|
||||
|
||||
# actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested
|
||||
@ -867,7 +915,7 @@ in
|
||||
++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []);
|
||||
|
||||
# copy the cryptsetup binary and it's dependencies
|
||||
boot.initrd.extraUtilsCommands = ''
|
||||
boot.initrd.extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup
|
||||
copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass
|
||||
sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass
|
||||
@ -915,7 +963,7 @@ in
|
||||
''}
|
||||
'';
|
||||
|
||||
boot.initrd.extraUtilsCommandsTest = ''
|
||||
boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
|
||||
$out/bin/cryptsetup --version
|
||||
${optionalString luks.yubikeySupport ''
|
||||
$out/bin/ykchalresp -V
|
||||
@ -932,9 +980,27 @@ in
|
||||
''}
|
||||
'';
|
||||
|
||||
boot.initrd.preFailCommands = postCommands;
|
||||
boot.initrd.preLVMCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands;
|
||||
boot.initrd.postDeviceCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands;
|
||||
boot.initrd.systemd = {
|
||||
contents."/etc/crypttab".source = stage1Crypttab;
|
||||
|
||||
extraBin.systemd-cryptsetup = "${config.boot.initrd.systemd.package}/lib/systemd/systemd-cryptsetup";
|
||||
|
||||
additionalUpstreamUnits = [
|
||||
"cryptsetup-pre.target"
|
||||
"cryptsetup.target"
|
||||
"remote-cryptsetup.target"
|
||||
];
|
||||
storePaths = [
|
||||
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-cryptsetup"
|
||||
];
|
||||
|
||||
};
|
||||
# We do this because we need the udev rules from the package
|
||||
boot.initrd.services.lvm.enable = true;
|
||||
|
||||
boot.initrd.preFailCommands = mkIf (!config.boot.initrd.systemd.enable) postCommands;
|
||||
boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands);
|
||||
boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands);
|
||||
|
||||
environment.systemPackages = [ pkgs.cryptsetup ];
|
||||
};
|
||||
|
@ -524,6 +524,8 @@ in
|
||||
systemd-confinement = handleTest ./systemd-confinement.nix {};
|
||||
systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {};
|
||||
systemd-escaping = handleTest ./systemd-escaping.nix {};
|
||||
systemd-initrd-luks-keyfile = handleTest ./systemd-initrd-luks-keyfile.nix {};
|
||||
systemd-initrd-luks-password = handleTest ./systemd-initrd-luks-password.nix {};
|
||||
systemd-initrd-shutdown = handleTest ./systemd-shutdown.nix { systemdStage1 = true; };
|
||||
systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {};
|
||||
systemd-initrd-swraid = handleTest ./systemd-initrd-swraid.nix {};
|
||||
|
53
nixos/tests/systemd-initrd-luks-keyfile.nix
Normal file
53
nixos/tests/systemd-initrd-luks-keyfile.nix
Normal file
@ -0,0 +1,53 @@
|
||||
import ./make-test-python.nix ({ lib, pkgs, ... }: let
|
||||
|
||||
keyfile = pkgs.writeText "luks-keyfile" ''
|
||||
MIGHAoGBAJ4rGTSo/ldyjQypd0kuS7k2OSsmQYzMH6TNj3nQ/vIUjDn7fqa3slt2
|
||||
gV6EK3TmTbGc4tzC1v4SWx2m+2Bjdtn4Fs4wiBwn1lbRdC6i5ZYCqasTWIntWn+6
|
||||
FllUkMD5oqjOR/YcboxG8Z3B5sJuvTP9llsF+gnuveWih9dpbBr7AgEC
|
||||
'';
|
||||
|
||||
in {
|
||||
name = "systemd-initrd-luks-keyfile";
|
||||
|
||||
nodes.machine = { pkgs, ... }: {
|
||||
# Use systemd-boot
|
||||
virtualisation = {
|
||||
emptyDiskImages = [ 512 ];
|
||||
useBootLoader = true;
|
||||
useEFIBoot = true;
|
||||
};
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
|
||||
environment.systemPackages = with pkgs; [ cryptsetup ];
|
||||
boot.initrd.systemd = {
|
||||
enable = true;
|
||||
emergencyAccess = true;
|
||||
};
|
||||
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
keyFile = "/etc/cryptroot.key";
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
boot.initrd.systemd.contents."/etc/cryptroot.key".source = keyfile;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdc")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
machine.succeed("sync")
|
||||
machine.crash()
|
||||
|
||||
# Boot and decrypt the disk
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount")
|
||||
'';
|
||||
})
|
48
nixos/tests/systemd-initrd-luks-password.nix
Normal file
48
nixos/tests/systemd-initrd-luks-password.nix
Normal file
@ -0,0 +1,48 @@
|
||||
import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
name = "systemd-initrd-luks-password";
|
||||
|
||||
nodes.machine = { pkgs, ... }: {
|
||||
# Use systemd-boot
|
||||
virtualisation = {
|
||||
emptyDiskImages = [ 512 512 ];
|
||||
useBootLoader = true;
|
||||
useEFIBoot = true;
|
||||
};
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
|
||||
environment.systemPackages = with pkgs; [ cryptsetup ];
|
||||
boot.initrd.systemd = {
|
||||
enable = true;
|
||||
emergencyAccess = true;
|
||||
};
|
||||
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
# We have two disks and only type one password - key reuse is in place
|
||||
cryptroot.device = "/dev/vdc";
|
||||
cryptroot2.device = "/dev/vdd";
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
machine.succeed("sync")
|
||||
machine.crash()
|
||||
|
||||
# Boot and decrypt the disk
|
||||
machine.start()
|
||||
machine.wait_for_console_text("Please enter passphrase for disk cryptroot")
|
||||
machine.send_console("supersecret\n")
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount")
|
||||
'';
|
||||
})
|
Loading…
Reference in New Issue
Block a user