clevis: add boot.initrd.clevisLuksAskpass module
Enable upstream clevis-luks-askpass systemd units in initrd for automatic LUKS unlocking using clevis bindings stored in LUKS headers.
This commit is contained in:
@@ -91,6 +91,8 @@
|
||||
|
||||
- [nohang](https://github.com/hakavlad/nohang), a daemon for Linux that prevents out of memory (OOM) situations from affecting system responsiveness. Available as [services.nohang](#opt-services.nohang.enable)
|
||||
|
||||
- [clevis-luks-askpass](https://github.com/latchset/clevis), automatic LUKS unlocking in initrd using clevis token bindings stored in LUKS headers. Available as [boot.initrd.clevisLuksAskpass](#opt-boot.initrd.clevisLuksAskpass.enable).
|
||||
|
||||
- [bentopdf](https://github.com/alam00000/bentopdf), a privacy-first PDF toolkit running completely in-browser. Available as [services.bentopdf](#opt-services.bentopdf.enable).
|
||||
|
||||
- [hyprwhspr-rs](https://github.com/better-slop/hyprwhspr-rs), a keybind activated speech-to-text voice dictation utility built for use with Hyprland. Available as `services.hyprwhspr-rs`
|
||||
|
||||
@@ -1892,6 +1892,7 @@
|
||||
./system/activation/switchable-system.nix
|
||||
./system/activation/top-level.nix
|
||||
./system/boot/binfmt.nix
|
||||
./system/boot/clevis-luks-askpass.nix
|
||||
./system/boot/clevis.nix
|
||||
./system/boot/emergency-mode.nix
|
||||
./system/boot/grow-partition.nix
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.boot.initrd.clevisLuksAskpass;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
boot.initrd.clevisLuksAskpass.enable = lib.mkEnableOption ''
|
||||
clevis-luks-askpass in initrd.
|
||||
|
||||
Watches for systemd password requests during boot and answers them
|
||||
using clevis tokens bound to LUKS headers. Runs in parallel with
|
||||
the interactive password prompt. If clevis cannot unlock a device
|
||||
(tang unreachable, no binding, etc.) the user can still type the
|
||||
passphrase.
|
||||
|
||||
Prerequisites:
|
||||
- Bind clevis to each LUKS device:
|
||||
clevis luks bind -d /dev/xxx tang '{"url":"..."}'
|
||||
- Configure networking in the initrd so tang servers are reachable
|
||||
'';
|
||||
|
||||
boot.initrd.clevisLuksAskpass.package = lib.mkPackageOption pkgs "clevis" { };
|
||||
|
||||
boot.initrd.clevisLuksAskpass.useTang = lib.mkOption {
|
||||
description = "Whether the Clevis headers used to decrypt the devices uses a Tang server as a pin.";
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = config.boot.initrd.systemd.enable;
|
||||
message = "clevis-luks-askpass requires boot.initrd.systemd.enable = true";
|
||||
}
|
||||
];
|
||||
|
||||
warnings =
|
||||
if
|
||||
cfg.useTang && !config.boot.initrd.network.enable && !config.boot.initrd.systemd.network.enable
|
||||
then
|
||||
[ "In order to use a Tang pinned secret you must configure networking in initrd" ]
|
||||
else
|
||||
[ ];
|
||||
|
||||
boot.initrd.systemd = {
|
||||
# Install upstream clevis-luks-askpass.path and clevis-luks-askpass.service into the initrd
|
||||
packages = [ cfg.package ];
|
||||
|
||||
storePaths = [
|
||||
cfg.package
|
||||
"${pkgs.systemd}/lib/systemd/systemd-reply-password"
|
||||
"${pkgs.jose}/bin/jose"
|
||||
"${pkgs.curl}/bin/curl"
|
||||
"${pkgs.cryptsetup}/bin/cryptsetup"
|
||||
"${pkgs.gnused}/bin/sed"
|
||||
"${pkgs.gnugrep}/bin/grep"
|
||||
"${pkgs.gawk}/bin/gawk"
|
||||
"${pkgs.coreutils}/bin/cat"
|
||||
"${pkgs.luksmeta}/bin/luksmeta"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_createprimary"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_flushcontext"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_load"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_unseal"
|
||||
];
|
||||
|
||||
# This is in the [Install] section of clevis-luks-askpass.path but that's not processed in nixos so we add it here
|
||||
paths.clevis-luks-askpass = {
|
||||
wantedBy = [ "cryptsetup.target" ];
|
||||
};
|
||||
|
||||
services.clevis-luks-askpass = {
|
||||
wants = lib.optional cfg.useTang "network-online.target";
|
||||
after = lib.optional cfg.useTang "network-online.target";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -21,6 +21,7 @@ let
|
||||
forceGrubReinstallCount ? 0,
|
||||
withTestInstrumentation ? true,
|
||||
clevisTest,
|
||||
clevisAskpassTest ? false,
|
||||
}:
|
||||
pkgs.writeText "configuration.nix" ''
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
@@ -73,7 +74,12 @@ let
|
||||
boot.kernelParams = [ "console=tty0" "ip=192.168.1.1:::255.255.255.0::eth1:none" ];
|
||||
boot.initrd = {
|
||||
availableKernelModules = [ "tpm_tis" ];
|
||||
clevis = { enable = true; useTang = true; };
|
||||
${
|
||||
if clevisAskpassTest then
|
||||
"clevisLuksAskpass = { enable = true; useTang = true; };"
|
||||
else
|
||||
"clevis = { enable = true; useTang = true; };"
|
||||
}
|
||||
network.enable = true;
|
||||
};
|
||||
''}
|
||||
@@ -108,6 +114,7 @@ let
|
||||
testFlakeSwitch,
|
||||
testByAttrSwitch,
|
||||
clevisTest,
|
||||
clevisAskpassTest ? false,
|
||||
clevisFallbackTest,
|
||||
disableFileSystems,
|
||||
}:
|
||||
@@ -190,6 +197,7 @@ let
|
||||
grubUseEfi
|
||||
extraConfig
|
||||
clevisTest
|
||||
clevisAskpassTest
|
||||
;
|
||||
}
|
||||
}",
|
||||
@@ -197,11 +205,20 @@ let
|
||||
)
|
||||
installer.copy_from_host("${pkgs.writeText "secret" "secret"}", "/mnt/etc/nixos/secret")
|
||||
|
||||
${optionalString clevisTest ''
|
||||
with subtest("Create the Clevis secret with Tang"):
|
||||
${optionalString (clevisTest && !clevisAskpassTest)
|
||||
''
|
||||
with subtest("Create the Clevis secret with Tang"):
|
||||
installer.systemctl("start network-online.target")
|
||||
installer.wait_for_unit("network-online.target")
|
||||
installer.succeed('echo -n password | clevis encrypt sss \'{"t": 2, "pins": {"tpm2": {}, "tang": {"url": "http://192.168.1.2"}}}\' -y > /mnt/etc/nixos/clevis-secret.jwe')''
|
||||
}
|
||||
|
||||
${optionalString clevisAskpassTest ''
|
||||
with subtest("Bind Clevis to LUKS header"):
|
||||
installer.systemctl("start network-online.target")
|
||||
installer.wait_for_unit("network-online.target")
|
||||
installer.succeed('echo -n password | clevis encrypt sss \'{"t": 2, "pins": {"tpm2": {}, "tang": {"url": "http://192.168.1.2"}}}\' -y > /mnt/etc/nixos/clevis-secret.jwe')''}
|
||||
installer.succeed("echo -n password | clevis luks bind -y -k - -d /dev/vda3 sss '{\"t\": 2, \"pins\": {\"tpm2\": {}, \"tang\": {\"url\": \"http://192.168.1.2\"}}}'")
|
||||
''}
|
||||
|
||||
${optionalString clevisFallbackTest ''
|
||||
with subtest("Shutdown Tang to check fallback to interactive prompt"):
|
||||
@@ -283,6 +300,7 @@ let
|
||||
grubUseEfi
|
||||
extraConfig
|
||||
clevisTest
|
||||
clevisAskpassTest
|
||||
;
|
||||
forceGrubReinstallCount = 1;
|
||||
}
|
||||
@@ -316,6 +334,7 @@ let
|
||||
grubUseEfi
|
||||
extraConfig
|
||||
clevisTest
|
||||
clevisAskpassTest
|
||||
;
|
||||
forceGrubReinstallCount = 2;
|
||||
}
|
||||
@@ -384,6 +403,7 @@ let
|
||||
grubUseEfi
|
||||
extraConfig
|
||||
clevisTest
|
||||
clevisAskpassTest
|
||||
;
|
||||
forceGrubReinstallCount = 1;
|
||||
withTestInstrumentation = false;
|
||||
@@ -482,6 +502,7 @@ let
|
||||
grubUseEfi
|
||||
extraConfig
|
||||
clevisTest
|
||||
clevisAskpassTest
|
||||
;
|
||||
forceGrubReinstallCount = 1;
|
||||
}
|
||||
@@ -518,6 +539,7 @@ let
|
||||
grubUseEfi
|
||||
extraConfig
|
||||
clevisTest
|
||||
clevisAskpassTest
|
||||
;
|
||||
forceGrubReinstallCount = 1;
|
||||
withTestInstrumentation = false;
|
||||
@@ -637,6 +659,7 @@ let
|
||||
testFlakeSwitch ? false,
|
||||
testByAttrSwitch ? false,
|
||||
clevisTest ? false,
|
||||
clevisAskpassTest ? false,
|
||||
clevisFallbackTest ? false,
|
||||
disableFileSystems ? false,
|
||||
selectNixPackage ? pkgs: pkgs.nixVersions.stable,
|
||||
@@ -820,6 +843,7 @@ let
|
||||
testFlakeSwitch
|
||||
testByAttrSwitch
|
||||
clevisTest
|
||||
clevisAskpassTest
|
||||
clevisFallbackTest
|
||||
disableFileSystems
|
||||
;
|
||||
@@ -1044,6 +1068,43 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
mkClevisLuksAskpassTest =
|
||||
{
|
||||
fallback ? false,
|
||||
}:
|
||||
makeInstallerTest "clevis-luks-askpass${optionalString fallback "-fallback"}" {
|
||||
clevisTest = true;
|
||||
clevisAskpassTest = true;
|
||||
clevisFallbackTest = fallback;
|
||||
enableOCR = fallback;
|
||||
extraInstallerConfig = {
|
||||
environment.systemPackages = with pkgs; [ clevis ];
|
||||
};
|
||||
createPartitions = ''
|
||||
installer.succeed(
|
||||
"flock /dev/vda parted --script /dev/vda -- mklabel msdos"
|
||||
+ " mkpart primary ext2 1M 100MB"
|
||||
+ " mkpart primary linux-swap 100M 1024M"
|
||||
+ " mkpart primary 1024M -1s",
|
||||
"udevadm settle",
|
||||
"mkswap /dev/vda2 -L swap",
|
||||
"swapon -L swap",
|
||||
"modprobe dm_mod dm_crypt",
|
||||
"echo -n password | cryptsetup luksFormat -q /dev/vda3 -",
|
||||
"echo -n password | cryptsetup luksOpen --key-file - /dev/vda3 crypt-root",
|
||||
"mkfs.ext3 -L nixos /dev/mapper/crypt-root",
|
||||
"mount LABEL=nixos /mnt",
|
||||
"mkfs.ext3 -L boot /dev/vda1",
|
||||
"mkdir -p /mnt/boot",
|
||||
"mount LABEL=boot /mnt/boot",
|
||||
"udevadm settle")
|
||||
'';
|
||||
postBootCommands = optionalString fallback ''
|
||||
target.wait_for_text("Please enter")
|
||||
target.send_chars("password\n")
|
||||
'';
|
||||
};
|
||||
|
||||
mkClevisZfsTest =
|
||||
{
|
||||
fallback ? false,
|
||||
@@ -1741,6 +1802,9 @@ in
|
||||
};
|
||||
}
|
||||
// optionalAttrs systemdStage1 {
|
||||
clevisLuksAskpass = mkClevisLuksAskpassTest { };
|
||||
clevisLuksAskpassFallback = mkClevisLuksAskpassTest { fallback = true; };
|
||||
|
||||
stratisRoot = makeInstallerTest "stratisRoot" {
|
||||
createPartitions = ''
|
||||
installer.succeed(
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
cryptsetup,
|
||||
curl,
|
||||
fetchFromGitHub,
|
||||
gawk,
|
||||
gnugrep,
|
||||
gnused,
|
||||
jansson,
|
||||
@@ -17,6 +18,7 @@
|
||||
nixosTests,
|
||||
pkg-config,
|
||||
stdenv,
|
||||
systemd,
|
||||
tpm2-tools,
|
||||
}:
|
||||
|
||||
@@ -52,6 +54,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
jose
|
||||
libpwquality
|
||||
luksmeta
|
||||
systemd
|
||||
tpm2-tools
|
||||
];
|
||||
|
||||
@@ -74,11 +77,26 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
# The xargs command is a little bit convoluted because a simpler version would
|
||||
# be vulnerable to code injection. This hint is a courtesy of Stack Exchange:
|
||||
# https://unix.stackexchange.com/a/267438
|
||||
#
|
||||
# Meson's find_program only searches paths relative to the build prefix
|
||||
# and /usr/lib, neither of which contain systemd-reply-password in the
|
||||
# nix store. Prepend the actual nix store path so meson can find it.
|
||||
#
|
||||
# The systemd pkgconfig variable 'systemdsystemunitdir' points into the
|
||||
# read-only systemd store path. Redirect unit installation to $out.
|
||||
postPatch = ''
|
||||
for f in $(find src/ -type f -print0 |\
|
||||
xargs -0 -I@ sh -c 'grep -q "/bin/cat" "$1" && echo "$1"' sh @); do
|
||||
substituteInPlace "$f" --replace-fail '/bin/cat' '${lib.getExe' coreutils "cat"}'
|
||||
done
|
||||
|
||||
substituteInPlace src/luks/systemd/meson.build \
|
||||
--replace-fail "sd_reply_pass = find_program(" \
|
||||
"sd_reply_pass = find_program('${systemd}/lib/systemd/systemd-reply-password',"
|
||||
|
||||
substituteInPlace src/luks/systemd/meson.build \
|
||||
--replace-fail "unitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir')" \
|
||||
"unitdir = join_paths(get_option('prefix'), 'lib', 'systemd', 'system')"
|
||||
'';
|
||||
|
||||
# We wrap the main clevis binary entrypoint but not the sub-binaries.
|
||||
@@ -94,10 +112,23 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
luksmeta
|
||||
tpm2-tools
|
||||
];
|
||||
askpassPath = [
|
||||
coreutils
|
||||
cryptsetup
|
||||
curl
|
||||
gawk
|
||||
gnugrep
|
||||
gnused
|
||||
jose
|
||||
luksmeta
|
||||
];
|
||||
in
|
||||
''
|
||||
wrapProgram $out/bin/clevis \
|
||||
--prefix PATH ':' "${lib.makeBinPath includeIntoPath}:${placeholder "out"}/bin"
|
||||
|
||||
wrapProgram $out/libexec/clevis-luks-askpass \
|
||||
--prefix PATH ':' "${lib.makeBinPath askpassPath}:${placeholder "out"}/bin"
|
||||
'';
|
||||
|
||||
passthru.tests = {
|
||||
@@ -111,6 +142,9 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
;
|
||||
clevisLuksSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisLuks;
|
||||
clevisLuksFallbackSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisLuksFallback;
|
||||
clevisLuksAskpassSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisLuksAskpass;
|
||||
clevisLuksAskpassFallbackSystemdStage1 =
|
||||
nixosTests.installer-systemd-stage-1.clevisLuksAskpassFallback;
|
||||
clevisZfsSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisZfs;
|
||||
clevisZfsFallbackSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisZfsFallback;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user