Merge pull request #189676 from zhaofengli/cryptenroll
systemd: Fix systemd-{cryptenroll,cryptsetup} TPM2 and FIDO2 support (attempt #3)
This commit is contained in:
commit
3ff0a8f840
|
@ -905,9 +905,11 @@ in
|
||||||
{ assertion = config.boot.initrd.systemd.enable -> !luks.gpgSupport;
|
{ assertion = config.boot.initrd.systemd.enable -> !luks.gpgSupport;
|
||||||
message = "systemd stage 1 does not support GPG smartcards yet.";
|
message = "systemd stage 1 does not support GPG smartcards yet.";
|
||||||
}
|
}
|
||||||
# TODO
|
|
||||||
{ assertion = config.boot.initrd.systemd.enable -> !luks.fido2Support;
|
{ assertion = config.boot.initrd.systemd.enable -> !luks.fido2Support;
|
||||||
message = "systemd stage 1 does not support FIDO2 yet.";
|
message = ''
|
||||||
|
systemd stage 1 does not support configuring FIDO2 unlocking through `boot.initrd.luks.devices.<name>.fido2`.
|
||||||
|
Use systemd-cryptenroll(1) to configure FIDO2 support.
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
# TODO
|
# TODO
|
||||||
{ assertion = config.boot.initrd.systemd.enable -> !luks.yubikeySupport;
|
{ assertion = config.boot.initrd.systemd.enable -> !luks.yubikeySupport;
|
||||||
|
|
|
@ -332,7 +332,10 @@ in {
|
||||||
config = mkIf (config.boot.initrd.enable && cfg.enable) {
|
config = mkIf (config.boot.initrd.enable && cfg.enable) {
|
||||||
system.build = { inherit initialRamdisk; };
|
system.build = { inherit initialRamdisk; };
|
||||||
|
|
||||||
boot.initrd.availableKernelModules = [ "autofs4" ]; # systemd needs this for some features
|
boot.initrd.availableKernelModules = [
|
||||||
|
"autofs4" # systemd needs this for some features
|
||||||
|
"tpm-tis" "tpm-crb" # systemd-cryptenroll
|
||||||
|
];
|
||||||
|
|
||||||
boot.initrd.systemd = {
|
boot.initrd.systemd = {
|
||||||
initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package] ++ config.system.fsPackages;
|
initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package] ++ config.system.fsPackages;
|
||||||
|
@ -403,6 +406,17 @@ in {
|
||||||
|
|
||||||
# so NSS can look up usernames
|
# so NSS can look up usernames
|
||||||
"${pkgs.glibc}/lib/libnss_files.so.2"
|
"${pkgs.glibc}/lib/libnss_files.so.2"
|
||||||
|
] ++ optionals cfg.package.withCryptsetup [
|
||||||
|
# tpm2 support
|
||||||
|
"${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so"
|
||||||
|
pkgs.tpm2-tss
|
||||||
|
|
||||||
|
# fido2 support
|
||||||
|
"${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-fido2.so"
|
||||||
|
"${pkgs.libfido2}/lib/libfido2.so.1"
|
||||||
|
|
||||||
|
# the unwrapped systemd-cryptsetup executable
|
||||||
|
"${cfg.package}/lib/systemd/.systemd-cryptsetup-wrapped"
|
||||||
] ++ jobScripts;
|
] ++ jobScripts;
|
||||||
|
|
||||||
targets.initrd.aliases = ["default.target"];
|
targets.initrd.aliases = ["default.target"];
|
||||||
|
|
|
@ -596,8 +596,10 @@ in {
|
||||||
systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {};
|
systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {};
|
||||||
systemd-escaping = handleTest ./systemd-escaping.nix {};
|
systemd-escaping = handleTest ./systemd-escaping.nix {};
|
||||||
systemd-initrd-btrfs-raid = handleTest ./systemd-initrd-btrfs-raid.nix {};
|
systemd-initrd-btrfs-raid = handleTest ./systemd-initrd-btrfs-raid.nix {};
|
||||||
|
systemd-initrd-luks-fido2 = handleTest ./systemd-initrd-luks-fido2.nix {};
|
||||||
systemd-initrd-luks-keyfile = handleTest ./systemd-initrd-luks-keyfile.nix {};
|
systemd-initrd-luks-keyfile = handleTest ./systemd-initrd-luks-keyfile.nix {};
|
||||||
systemd-initrd-luks-password = handleTest ./systemd-initrd-luks-password.nix {};
|
systemd-initrd-luks-password = handleTest ./systemd-initrd-luks-password.nix {};
|
||||||
|
systemd-initrd-luks-tpm2 = handleTest ./systemd-initrd-luks-tpm2.nix {};
|
||||||
systemd-initrd-modprobe = handleTest ./systemd-initrd-modprobe.nix {};
|
systemd-initrd-modprobe = handleTest ./systemd-initrd-modprobe.nix {};
|
||||||
systemd-initrd-shutdown = handleTest ./systemd-shutdown.nix { systemdStage1 = true; };
|
systemd-initrd-shutdown = handleTest ./systemd-shutdown.nix { systemdStage1 = true; };
|
||||||
systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {};
|
systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {};
|
||||||
|
|
45
nixos/tests/systemd-initrd-luks-fido2.nix
Normal file
45
nixos/tests/systemd-initrd-luks-fido2.nix
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||||
|
name = "systemd-initrd-luks-fido2";
|
||||||
|
|
||||||
|
nodes.machine = { pkgs, config, ... }: {
|
||||||
|
# Use systemd-boot
|
||||||
|
virtualisation = {
|
||||||
|
emptyDiskImages = [ 512 ];
|
||||||
|
useBootLoader = true;
|
||||||
|
useEFIBoot = true;
|
||||||
|
qemu.package = lib.mkForce (pkgs.qemu_test.override { canokeySupport = true; });
|
||||||
|
qemu.options = [ "-device canokey,file=/tmp/canokey-file" ];
|
||||||
|
};
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
|
||||||
|
boot.initrd.systemd.enable = true;
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [ cryptsetup ];
|
||||||
|
|
||||||
|
specialisation.boot-luks.configuration = {
|
||||||
|
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||||
|
cryptroot = {
|
||||||
|
device = "/dev/vdc";
|
||||||
|
crypttabExtraOpts = [ "fido2-device=auto" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
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("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --fido2-device=auto /dev/vdc |& systemd-cat")
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
'';
|
||||||
|
})
|
72
nixos/tests/systemd-initrd-luks-tpm2.nix
Normal file
72
nixos/tests/systemd-initrd-luks-tpm2.nix
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||||
|
name = "systemd-initrd-luks-tpm2";
|
||||||
|
|
||||||
|
nodes.machine = { pkgs, ... }: {
|
||||||
|
# Use systemd-boot
|
||||||
|
virtualisation = {
|
||||||
|
emptyDiskImages = [ 512 ];
|
||||||
|
useBootLoader = true;
|
||||||
|
useEFIBoot = true;
|
||||||
|
qemu.options = ["-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"];
|
||||||
|
};
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [ "tpm_tis" ];
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [ cryptsetup ];
|
||||||
|
boot.initrd.systemd = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
specialisation.boot-luks.configuration = {
|
||||||
|
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||||
|
cryptroot = {
|
||||||
|
device = "/dev/vdc";
|
||||||
|
crypttabExtraOpts = [ "tpm2-device=auto" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class Tpm:
|
||||||
|
def __init__(self):
|
||||||
|
os.mkdir("/tmp/mytpm1")
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", "socket", "--tpmstate", "dir=/tmp/mytpm1", "--ctrl", "type=unixio,path=/tmp/mytpm1/swtpm-sock", "--log", "level=20", "--tpm2"])
|
||||||
|
|
||||||
|
def wait_for_death_then_restart(self):
|
||||||
|
while self.proc.poll() is None:
|
||||||
|
print("waiting for tpm to die")
|
||||||
|
time.sleep(1)
|
||||||
|
assert self.proc.returncode == 0
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
tpm = Tpm()
|
||||||
|
|
||||||
|
|
||||||
|
# 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("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --tpm2-pcrs= --tpm2-device=auto /dev/vdc |& systemd-cat")
|
||||||
|
|
||||||
|
# Boot from the encrypted disk
|
||||||
|
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||||
|
machine.succeed("sync")
|
||||||
|
machine.crash()
|
||||||
|
|
||||||
|
tpm.wait_for_death_then_restart()
|
||||||
|
|
||||||
|
# Boot and decrypt the disk
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount")
|
||||||
|
'';
|
||||||
|
})
|
|
@ -20,6 +20,11 @@ stdenv.mkDerivation rec {
|
||||||
sha256 = "sha256-kYSm672c5+shEVLn90GmyC8tHMDiSoTsnFKTnu4PBUI=";
|
sha256 = "sha256-kYSm672c5+shEVLn90GmyC8tHMDiSoTsnFKTnu4PBUI=";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
patches = [
|
||||||
|
# Allow reading tokens from a relative path, see #167994
|
||||||
|
./relative-token-path.patch
|
||||||
|
];
|
||||||
|
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
patchShebangs tests
|
patchShebangs tests
|
||||||
|
|
||||||
|
|
50
pkgs/os-specific/linux/cryptsetup/relative-token-path.patch
Normal file
50
pkgs/os-specific/linux/cryptsetup/relative-token-path.patch
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
From 4f95ab1f8110a8ab9d7b0e192731ce467f6e5c26 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Janne=20He=C3=9F?= <janne@hess.ooo>
|
||||||
|
Date: Sun, 4 Sep 2022 11:15:02 -0600
|
||||||
|
Subject: [PATCH] Allow loading token handlers from the default search path
|
||||||
|
|
||||||
|
Since [1] landed in cryptsetup, token handlers (libcryptsetup-token-*.so)
|
||||||
|
are loaded from a fixed path defined at compile-time. This is
|
||||||
|
problematic with NixOS since it introduces a dependency cycle
|
||||||
|
between cryptsetup and systemd.
|
||||||
|
|
||||||
|
This downstream patch [2] allows loading token plugins from the
|
||||||
|
default library search path. This approach is not accepted upstream [3]
|
||||||
|
due to security concerns, but the potential attack vectors require
|
||||||
|
root access and they are sufficiently addressed:
|
||||||
|
|
||||||
|
* cryptsetup could be used as a setuid binary (not used in NixOS).
|
||||||
|
In this case, LD_LIBRARY_PATH is ignored because of secure-execution
|
||||||
|
mode.
|
||||||
|
* cryptsetup running as root could lead to a malicious token handler
|
||||||
|
being loaded through LD_LIBRARY_PATH. However, fixing the path
|
||||||
|
doesn't prevent the same malicious .so being loaded through LD_PRELOAD.
|
||||||
|
|
||||||
|
[1] https://gitlab.com/cryptsetup/cryptsetup/-/commit/5b9e98f94178d3cd179d9f6e2a0a68c7d9eb6507
|
||||||
|
[2] https://github.com/NixOS/nixpkgs/issues/167994#issuecomment-1094249369
|
||||||
|
[3] https://gitlab.com/cryptsetup/cryptsetup/-/issues/733
|
||||||
|
---
|
||||||
|
lib/luks2/luks2_token.c | 4 +---
|
||||||
|
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c
|
||||||
|
index 26467253..6f8329f0 100644
|
||||||
|
--- a/lib/luks2/luks2_token.c
|
||||||
|
+++ b/lib/luks2/luks2_token.c
|
||||||
|
@@ -151,12 +151,10 @@ crypt_token_load_external(struct crypt_device *cd, const char *name, struct cryp
|
||||||
|
|
||||||
|
token = &ret->u.v2;
|
||||||
|
|
||||||
|
- r = snprintf(buf, sizeof(buf), "%s/libcryptsetup-token-%s.so", crypt_token_external_path(), name);
|
||||||
|
+ r = snprintf(buf, sizeof(buf), "libcryptsetup-token-%s.so", name);
|
||||||
|
if (r < 0 || (size_t)r >= sizeof(buf))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
- assert(*buf == '/');
|
||||||
|
-
|
||||||
|
log_dbg(cd, "Trying to load %s.", buf);
|
||||||
|
|
||||||
|
h = dlopen(buf, RTLD_LAZY);
|
||||||
|
--
|
||||||
|
2.37.2
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
, fetchpatch
|
, fetchpatch
|
||||||
, fetchzip
|
, fetchzip
|
||||||
, buildPackages
|
, buildPackages
|
||||||
|
, makeBinaryWrapper
|
||||||
, ninja
|
, ninja
|
||||||
, meson
|
, meson
|
||||||
, m4
|
, m4
|
||||||
|
@ -333,6 +334,7 @@ stdenv.mkDerivation {
|
||||||
nativeBuildInputs =
|
nativeBuildInputs =
|
||||||
[
|
[
|
||||||
pkg-config
|
pkg-config
|
||||||
|
makeBinaryWrapper
|
||||||
gperf
|
gperf
|
||||||
ninja
|
ninja
|
||||||
meson
|
meson
|
||||||
|
@ -668,7 +670,14 @@ stdenv.mkDerivation {
|
||||||
preFixup = lib.optionalString withEfi ''
|
preFixup = lib.optionalString withEfi ''
|
||||||
mv $out/lib/systemd/boot/efi $out/dont-strip-me
|
mv $out/lib/systemd/boot/efi $out/dont-strip-me
|
||||||
'';
|
'';
|
||||||
postFixup = lib.optionalString withEfi ''
|
|
||||||
|
# Wrap in the correct path for LUKS2 tokens.
|
||||||
|
postFixup = lib.optionalString withCryptsetup ''
|
||||||
|
for f in lib/systemd/systemd-cryptsetup bin/systemd-cryptenroll; do
|
||||||
|
# This needs to be in LD_LIBRARY_PATH because rpath on a binary is not propagated to libraries using dlopen, in this case `libcryptsetup.so`
|
||||||
|
wrapProgram $out/$f --prefix LD_LIBRARY_PATH : ${placeholder "out"}/lib/cryptsetup
|
||||||
|
done
|
||||||
|
'' + lib.optionalString withEfi ''
|
||||||
mv $out/dont-strip-me $out/lib/systemd/boot/efi
|
mv $out/dont-strip-me $out/lib/systemd/boot/efi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue