mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-14 06:00:33 +03:00
Merge pull request #257525 from SaumonNet/clevis
This commit is contained in:
commit
8626b5c06d
11 changed files with 492 additions and 20 deletions
|
@ -18,6 +18,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
|||
|
||||
- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
|
||||
|
||||
- [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).
|
||||
|
||||
## Backward Incompatibilities {#sec-release-24.05-incompatibilities}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
|
|
@ -1424,6 +1424,7 @@
|
|||
./system/activation/bootspec.nix
|
||||
./system/activation/top-level.nix
|
||||
./system/boot/binfmt.nix
|
||||
./system/boot/clevis.nix
|
||||
./system/boot/emergency-mode.nix
|
||||
./system/boot/grow-partition.nix
|
||||
./system/boot/initrd-network.nix
|
||||
|
|
51
nixos/modules/system/boot/clevis.md
Normal file
51
nixos/modules/system/boot/clevis.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Clevis {#module-boot-clevis}
|
||||
|
||||
[Clevis](https://github.com/latchset/clevis)
|
||||
is a framework for automated decryption of resources.
|
||||
Clevis allows for secure unattended disk decryption during boot, using decryption policies that must be satisfied for the data to decrypt.
|
||||
|
||||
|
||||
## Create a JWE file containing your secret {#module-boot-clevis-create-secret}
|
||||
|
||||
The first step is to embed your secret in a [JWE](https://en.wikipedia.org/wiki/JSON_Web_Encryption) file.
|
||||
JWE files have to be created through the clevis command line. 3 types of policies are supported:
|
||||
|
||||
1) TPM policies
|
||||
|
||||
Secrets are pinned against the presence of a TPM2 device, for example:
|
||||
```
|
||||
echo hi | clevis encrypt tpm2 '{}' > hi.jwe
|
||||
```
|
||||
2) Tang policies
|
||||
|
||||
Secrets are pinned against the presence of a Tang server, for example:
|
||||
```
|
||||
echo hi | clevis encrypt tang '{"url": "http://tang.local"}' > hi.jwe
|
||||
```
|
||||
|
||||
3) Shamir Secret Sharing
|
||||
|
||||
Using Shamir's Secret Sharing ([sss](https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing)), secrets are pinned using a combination of the two preceding policies. For example:
|
||||
```
|
||||
echo hi | clevis encrypt sss \
|
||||
'{"t": 2, "pins": {"tpm2": {"pcr_ids": "0"}, "tang": {"url": "http://tang.local"}}}' \
|
||||
> hi.jwe
|
||||
```
|
||||
|
||||
For more complete documentation on how to generate a secret with clevis, see the [clevis documentation](https://github.com/latchset/clevis).
|
||||
|
||||
|
||||
## Activate unattended decryption of a resource at boot {#module-boot-clevis-activate}
|
||||
|
||||
In order to activate unattended decryption of a resource at boot, enable the `clevis` module:
|
||||
|
||||
```
|
||||
boot.initrd.clevis.enable = true;
|
||||
```
|
||||
|
||||
Then, specify the device you want to decrypt using a given clevis secret. Clevis will automatically try to decrypt the device at boot and will fallback to interactive unlocking if the decryption policy is not fulfilled.
|
||||
```
|
||||
boot.initrd.clevis.devices."/dev/nvme0n1p1".secretFile = ./nvme0n1p1.jwe;
|
||||
```
|
||||
|
||||
Only `bcachefs`, `zfs` and `luks` encrypted devices are supported at this time.
|
107
nixos/modules/system/boot/clevis.nix
Normal file
107
nixos/modules/system/boot/clevis.nix
Normal file
|
@ -0,0 +1,107 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.boot.initrd.clevis;
|
||||
systemd = config.boot.initrd.systemd;
|
||||
supportedFs = [ "zfs" "bcachefs" ];
|
||||
in
|
||||
{
|
||||
meta.maintainers = with maintainers; [ julienmalka camillemndn ];
|
||||
meta.doc = ./clevis.md;
|
||||
|
||||
options = {
|
||||
boot.initrd.clevis.enable = mkEnableOption (lib.mdDoc "Clevis in initrd");
|
||||
|
||||
|
||||
boot.initrd.clevis.package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.clevis;
|
||||
defaultText = "pkgs.clevis";
|
||||
description = lib.mdDoc "Clevis package";
|
||||
};
|
||||
|
||||
boot.initrd.clevis.devices = mkOption {
|
||||
description = "Encrypted devices that need to be unlocked at boot using Clevis";
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule ({
|
||||
options.secretFile = mkOption {
|
||||
description = lib.mdDoc "Clevis JWE file used to decrypt the device at boot, in concert with the chosen pin (one of TPM2, Tang server, or SSS).";
|
||||
type = types.path;
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
boot.initrd.clevis.useTang = mkOption {
|
||||
description = "Whether the Clevis JWE file used to decrypt the devices uses a Tang server as a pin.";
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
# Implementation of clevis unlocking for the supported filesystems are located directly in the respective modules.
|
||||
|
||||
|
||||
assertions = (attrValues (mapAttrs
|
||||
(device: _: {
|
||||
assertion = (any (fs: fs.device == device && (elem fs.fsType supportedFs)) config.system.build.fileSystems) || (hasAttr device config.boot.initrd.luks.devices);
|
||||
message = ''
|
||||
No filesystem or LUKS device with the name ${device} is declared in your configuration.'';
|
||||
})
|
||||
cfg.devices));
|
||||
|
||||
|
||||
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 = {
|
||||
extraUtilsCommands = mkIf (!systemd.enable) ''
|
||||
copy_bin_and_libs ${pkgs.jose}/bin/jose
|
||||
copy_bin_and_libs ${pkgs.curl}/bin/curl
|
||||
copy_bin_and_libs ${pkgs.bash}/bin/bash
|
||||
|
||||
copy_bin_and_libs ${pkgs.tpm2-tools}/bin/.tpm2-wrapped
|
||||
mv $out/bin/{.tpm2-wrapped,tpm2}
|
||||
cp {${pkgs.tpm2-tss},$out}/lib/libtss2-tcti-device.so.0
|
||||
|
||||
copy_bin_and_libs ${cfg.package}/bin/.clevis-wrapped
|
||||
mv $out/bin/{.clevis-wrapped,clevis}
|
||||
|
||||
for BIN in ${cfg.package}/bin/clevis-decrypt*; do
|
||||
copy_bin_and_libs $BIN
|
||||
done
|
||||
|
||||
for BIN in $out/bin/clevis{,-decrypt{,-null,-tang,-tpm2}}; do
|
||||
sed -i $BIN -e 's,${pkgs.bash},,' -e 's,${pkgs.coreutils},,'
|
||||
done
|
||||
|
||||
sed -i $out/bin/clevis-decrypt-tpm2 -e 's,tpm2_,tpm2 ,'
|
||||
'';
|
||||
|
||||
secrets = lib.mapAttrs' (name: value: nameValuePair "/etc/clevis/${name}.jwe" value.secretFile) cfg.devices;
|
||||
|
||||
systemd = {
|
||||
extraBin = mkIf systemd.enable {
|
||||
clevis = "${cfg.package}/bin/clevis";
|
||||
curl = "${pkgs.curl}/bin/curl";
|
||||
};
|
||||
|
||||
storePaths = mkIf systemd.enable [
|
||||
cfg.package
|
||||
"${pkgs.jose}/bin/jose"
|
||||
"${pkgs.curl}/bin/curl"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_createprimary"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_flushcontext"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_load"
|
||||
"${pkgs.tpm2-tools}/bin/tpm2_unseal"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
{ config, options, lib, pkgs, ... }:
|
||||
{ config, options, lib, utils, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
luks = config.boot.initrd.luks;
|
||||
clevis = config.boot.initrd.clevis;
|
||||
systemd = config.boot.initrd.systemd;
|
||||
kernelPackages = config.boot.kernelPackages;
|
||||
defaultPrio = (mkOptionDefault {}).priority;
|
||||
|
||||
|
@ -594,7 +596,7 @@ in
|
|||
'';
|
||||
|
||||
type = with types; attrsOf (submodule (
|
||||
{ name, ... }: { options = {
|
||||
{ config, name, ... }: { options = {
|
||||
|
||||
name = mkOption {
|
||||
visible = false;
|
||||
|
@ -894,6 +896,19 @@ in
|
|||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (clevis.enable && (hasAttr name clevis.devices)) {
|
||||
preOpenCommands = mkIf (!systemd.enable) ''
|
||||
mkdir -p /clevis-${name}
|
||||
mount -t ramfs none /clevis-${name}
|
||||
clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted
|
||||
'';
|
||||
keyFile = "/clevis-${name}/decrypted";
|
||||
fallbackToPassword = !systemd.enable;
|
||||
postOpenCommands = mkIf (!systemd.enable) ''
|
||||
umount /clevis-${name}
|
||||
'';
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -1081,6 +1096,35 @@ in
|
|||
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);
|
||||
|
||||
boot.initrd.systemd.services = let devicesWithClevis = filterAttrs (device: _: (hasAttr device clevis.devices)) luks.devices; in
|
||||
mkIf (clevis.enable && systemd.enable) (
|
||||
(mapAttrs'
|
||||
(name: _: nameValuePair "cryptsetup-clevis-${name}" {
|
||||
wantedBy = [ "systemd-cryptsetup@${utils.escapeSystemdPath name}.service" ];
|
||||
before = [
|
||||
"systemd-cryptsetup@${utils.escapeSystemdPath name}.service"
|
||||
"initrd-switch-root.target"
|
||||
"shutdown.target"
|
||||
];
|
||||
wants = [ "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target";
|
||||
after = [ "systemd-modules-load.service" "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target";
|
||||
script = ''
|
||||
mkdir -p /clevis-${name}
|
||||
mount -t ramfs none /clevis-${name}
|
||||
umask 277
|
||||
clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted
|
||||
'';
|
||||
conflicts = [ "initrd-switch-root.target" "shutdown.target" ];
|
||||
unitConfig.DefaultDependencies = "no";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStop = "${config.boot.initrd.systemd.package.util-linux}/bin/umount /clevis-${name}";
|
||||
};
|
||||
})
|
||||
devicesWithClevis)
|
||||
);
|
||||
|
||||
environment.systemPackages = [ pkgs.cryptsetup ];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -57,7 +57,15 @@ let
|
|||
# bcachefs does not support mounting devices with colons in the path, ergo we don't (see #49671)
|
||||
firstDevice = fs: lib.head (lib.splitString ":" fs.device);
|
||||
|
||||
openCommand = name: fs: ''
|
||||
openCommand = name: fs: if config.boot.initrd.clevis.enable && (lib.hasAttr (firstDevice fs) config.boot.initrd.clevis.devices) then ''
|
||||
if clevis decrypt < /etc/clevis/${firstDevice fs}.jwe | bcachefs unlock ${firstDevice fs}
|
||||
then
|
||||
printf "unlocked ${name} using clevis\n"
|
||||
else
|
||||
printf "falling back to interactive unlocking...\n"
|
||||
tryUnlock ${name} ${firstDevice fs}
|
||||
fi
|
||||
'' else ''
|
||||
tryUnlock ${name} ${firstDevice fs}
|
||||
'';
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ let
|
|||
cfgZED = config.services.zfs.zed;
|
||||
|
||||
selectModulePackage = package: config.boot.kernelPackages.${package.kernelModuleAttribute};
|
||||
clevisDatasets = map (e: e.device) (filter (e: (hasAttr e.device config.boot.initrd.clevis.devices) && e.fsType == "zfs" && (fsNeededForBoot e)) config.system.build.fileSystems);
|
||||
|
||||
|
||||
inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
|
||||
inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
|
||||
|
||||
|
@ -120,12 +123,12 @@ let
|
|||
# but don't *require* it, because mounts shouldn't be killed if it's stopped.
|
||||
# In the future, hopefully someone will complete this:
|
||||
# https://github.com/zfsonlinux/zfs/pull/4943
|
||||
wants = [ "systemd-udev-settle.service" ];
|
||||
wants = [ "systemd-udev-settle.service" ] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target";
|
||||
after = [
|
||||
"systemd-udev-settle.service"
|
||||
"systemd-modules-load.service"
|
||||
"systemd-ask-password-console.service"
|
||||
];
|
||||
] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target";
|
||||
requiredBy = getPoolMounts prefix pool ++ [ "zfs-import.target" ];
|
||||
before = getPoolMounts prefix pool ++ [ "zfs-import.target" ];
|
||||
unitConfig = {
|
||||
|
@ -154,6 +157,9 @@ let
|
|||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
fi
|
||||
if poolImported "${pool}"; then
|
||||
${concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem} || true ") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets)}
|
||||
|
||||
|
||||
${optionalString keyLocations.hasKeys ''
|
||||
${keyLocations.command} | while IFS=$'\t' read ds kl ks; do
|
||||
{
|
||||
|
@ -623,6 +629,9 @@ in
|
|||
fi
|
||||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
fi
|
||||
|
||||
${concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem}") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets)}
|
||||
|
||||
${if isBool cfgZfs.requestEncryptionCredentials
|
||||
then optionalString cfgZfs.requestEncryptionCredentials ''
|
||||
zfs load-key -a
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
stratisRoot
|
||||
swraid
|
||||
zfsroot
|
||||
clevisLuks
|
||||
clevisLuksFallback
|
||||
clevisZfs
|
||||
clevisZfsFallback
|
||||
;
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ let
|
|||
# The configuration to install.
|
||||
makeConfig = { bootLoader, grubDevice, grubIdentifier, grubUseEfi
|
||||
, extraConfig, forceGrubReinstallCount ? 0, flake ? false
|
||||
, clevisTest
|
||||
}:
|
||||
pkgs.writeText "configuration.nix" ''
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
@ -52,6 +53,15 @@ let
|
|||
|
||||
boot.initrd.secrets."/etc/secret" = ./secret;
|
||||
|
||||
${optionalString clevisTest ''
|
||||
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; };
|
||||
network.enable = true;
|
||||
};
|
||||
''}
|
||||
|
||||
users.users.alice = {
|
||||
isNormalUser = true;
|
||||
home = "/home/alice";
|
||||
|
@ -71,7 +81,7 @@ let
|
|||
# partitions and filesystems.
|
||||
testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi, grubIdentifier
|
||||
, postInstallCommands, preBootCommands, postBootCommands, extraConfig
|
||||
, testSpecialisationConfig, testFlakeSwitch
|
||||
, testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest
|
||||
}:
|
||||
let iface = "virtio";
|
||||
isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
|
||||
|
@ -79,12 +89,16 @@ let
|
|||
in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then ''
|
||||
machine.succeed("true")
|
||||
'' else ''
|
||||
import subprocess
|
||||
tpm_folder = os.environ['NIX_BUILD_TOP']
|
||||
def assemble_qemu_flags():
|
||||
flags = "-cpu max"
|
||||
${if (system == "x86_64-linux" || system == "i686-linux")
|
||||
then ''flags += " -m 1024"''
|
||||
else ''flags += " -m 768 -enable-kvm -machine virt,gic-version=host"''
|
||||
}
|
||||
${optionalString clevisTest ''flags += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"''}
|
||||
${optionalString clevisTest ''flags += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""''}
|
||||
return flags
|
||||
|
||||
|
||||
|
@ -110,8 +124,45 @@ let
|
|||
def create_machine_named(name):
|
||||
return create_machine({**default_flags, "name": name})
|
||||
|
||||
class Tpm:
|
||||
def __init__(self):
|
||||
self.start()
|
||||
|
||||
def start(self):
|
||||
self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm",
|
||||
"socket",
|
||||
"--tpmstate", f"dir={tpm_folder}/swtpm",
|
||||
"--ctrl", f"type=unixio,path={tpm_folder}/swtpm-sock",
|
||||
"--tpm2"
|
||||
])
|
||||
|
||||
# Check whether starting swtpm failed
|
||||
try:
|
||||
exit_code = self.proc.wait(timeout=0.2)
|
||||
if exit_code is not None and exit_code != 0:
|
||||
raise Exception("failed to start swtpm")
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
|
||||
"""Check whether the swtpm process exited due to an error"""
|
||||
def check(self):
|
||||
exit_code = self.proc.poll()
|
||||
if exit_code is not None and exit_code != 0:
|
||||
raise Exception("swtpm process died")
|
||||
|
||||
|
||||
os.mkdir(f"{tpm_folder}/swtpm")
|
||||
tpm = Tpm()
|
||||
tpm.check()
|
||||
|
||||
start_all()
|
||||
${optionalString clevisTest ''
|
||||
tang.wait_for_unit("sockets.target")
|
||||
tang.wait_for_unit("network-online.target")
|
||||
machine.wait_for_unit("network-online.target")
|
||||
''}
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
machine.start()
|
||||
|
||||
with subtest("Assert readiness of login prompt"):
|
||||
machine.succeed("echo hello")
|
||||
|
@ -127,13 +178,23 @@ let
|
|||
machine.copy_from_host(
|
||||
"${ makeConfig {
|
||||
inherit bootLoader grubDevice grubIdentifier
|
||||
grubUseEfi extraConfig;
|
||||
grubUseEfi extraConfig clevisTest;
|
||||
}
|
||||
}",
|
||||
"/mnt/etc/nixos/configuration.nix",
|
||||
)
|
||||
machine.copy_from_host("${pkgs.writeText "secret" "secret"}", "/mnt/etc/nixos/secret")
|
||||
|
||||
${optionalString clevisTest ''
|
||||
with subtest("Create the Clevis secret with Tang"):
|
||||
machine.wait_for_unit("network-online.target")
|
||||
machine.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 clevisFallbackTest ''
|
||||
with subtest("Shutdown Tang to check fallback to interactive prompt"):
|
||||
tang.shutdown()
|
||||
''}
|
||||
|
||||
with subtest("Perform the installation"):
|
||||
machine.succeed("nixos-install < /dev/null >&2")
|
||||
|
||||
|
@ -200,7 +261,7 @@ let
|
|||
machine.copy_from_host_via_shell(
|
||||
"${ makeConfig {
|
||||
inherit bootLoader grubDevice grubIdentifier
|
||||
grubUseEfi extraConfig;
|
||||
grubUseEfi extraConfig clevisTest;
|
||||
forceGrubReinstallCount = 1;
|
||||
}
|
||||
}",
|
||||
|
@ -229,7 +290,7 @@ let
|
|||
machine.copy_from_host_via_shell(
|
||||
"${ makeConfig {
|
||||
inherit bootLoader grubDevice grubIdentifier
|
||||
grubUseEfi extraConfig;
|
||||
grubUseEfi extraConfig clevisTest;
|
||||
forceGrubReinstallCount = 2;
|
||||
}
|
||||
}",
|
||||
|
@ -303,7 +364,7 @@ let
|
|||
""")
|
||||
machine.copy_from_host_via_shell(
|
||||
"${makeConfig {
|
||||
inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig;
|
||||
inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig clevisTest;
|
||||
forceGrubReinstallCount = 1;
|
||||
flake = true;
|
||||
}}",
|
||||
|
@ -379,6 +440,8 @@ let
|
|||
, enableOCR ? false, meta ? {}
|
||||
, testSpecialisationConfig ? false
|
||||
, testFlakeSwitch ? false
|
||||
, clevisTest ? false
|
||||
, clevisFallbackTest ? false
|
||||
}:
|
||||
makeTest {
|
||||
inherit enableOCR;
|
||||
|
@ -416,13 +479,13 @@ let
|
|||
virtualisation.rootDevice = "/dev/vdb";
|
||||
virtualisation.bootLoaderDevice = "/dev/vda";
|
||||
virtualisation.qemu.diskInterface = "virtio";
|
||||
|
||||
# We don't want to have any networking in the guest whatsoever.
|
||||
# Also, if any vlans are enabled, the guest will reboot
|
||||
# (with a different configuration for legacy reasons),
|
||||
# and spend 5 minutes waiting for the vlan interface to show up
|
||||
# (which will never happen).
|
||||
virtualisation.vlans = [];
|
||||
virtualisation.qemu.options = mkIf (clevisTest) [
|
||||
"-chardev socket,id=chrtpm,path=$NIX_BUILD_TOP/swtpm-sock"
|
||||
"-tpmdev emulator,id=tpm0,chardev=chrtpm"
|
||||
"-device tpm-tis,tpmdev=tpm0"
|
||||
];
|
||||
# We don't want to have any networking in the guest apart from the clevis tests.
|
||||
virtualisation.vlans = mkIf (!clevisTest) [];
|
||||
|
||||
boot.loader.systemd-boot.enable = mkIf (bootLoader == "systemd-boot") true;
|
||||
|
||||
|
@ -471,7 +534,7 @@ let
|
|||
in [
|
||||
(pkgs.grub2.override { inherit zfsSupport; })
|
||||
(pkgs.grub2_efi.override { inherit zfsSupport; })
|
||||
]);
|
||||
]) ++ optionals clevisTest [ pkgs.klibc ];
|
||||
|
||||
nix.settings = {
|
||||
substituters = mkForce [];
|
||||
|
@ -480,12 +543,21 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
} // optionalAttrs clevisTest {
|
||||
tang = {
|
||||
services.tang = {
|
||||
enable = true;
|
||||
listenStream = [ "80" ];
|
||||
ipAddressAllow = [ "192.168.1.0/24" ];
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = testScriptFun {
|
||||
inherit bootLoader createPartitions postInstallCommands preBootCommands postBootCommands
|
||||
grubDevice grubIdentifier grubUseEfi extraConfig
|
||||
testSpecialisationConfig testFlakeSwitch;
|
||||
testSpecialisationConfig testFlakeSwitch clevisTest clevisFallbackTest;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -586,6 +658,145 @@ let
|
|||
zfs = super.zfs.overrideAttrs(_: {meta.platforms = [];});}
|
||||
)];
|
||||
};
|
||||
|
||||
mkClevisBcachefsTest = { fallback ? false }: makeInstallerTest "clevis-bcachefs${optionalString fallback "-fallback"}" {
|
||||
clevisTest = true;
|
||||
clevisFallbackTest = fallback;
|
||||
enableOCR = fallback;
|
||||
extraInstallerConfig = {
|
||||
imports = [ no-zfs-module ];
|
||||
boot.supportedFilesystems = [ "bcachefs" ];
|
||||
environment.systemPackages = with pkgs; [ keyutils clevis ];
|
||||
};
|
||||
createPartitions = ''
|
||||
machine.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",
|
||||
"keyctl link @u @s",
|
||||
"echo -n password | mkfs.bcachefs -L root --encrypted /dev/vda3",
|
||||
"echo -n password | bcachefs unlock /dev/vda3",
|
||||
"echo -n password | mount -t bcachefs /dev/vda3 /mnt",
|
||||
"mkfs.ext3 -L boot /dev/vda1",
|
||||
"mkdir -p /mnt/boot",
|
||||
"mount LABEL=boot /mnt/boot",
|
||||
"udevadm settle")
|
||||
'';
|
||||
extraConfig = ''
|
||||
boot.initrd.clevis.devices."/dev/vda3".secretFile = "/etc/nixos/clevis-secret.jwe";
|
||||
|
||||
# We override what nixos-generate-config has generated because we do
|
||||
# not know the UUID in advance.
|
||||
fileSystems."/" = lib.mkForce { device = "/dev/vda3"; fsType = "bcachefs"; };
|
||||
'';
|
||||
preBootCommands = ''
|
||||
tpm = Tpm()
|
||||
tpm.check()
|
||||
'' + optionalString fallback ''
|
||||
machine.start()
|
||||
machine.wait_for_text("enter passphrase for")
|
||||
machine.send_chars("password\n")
|
||||
'';
|
||||
};
|
||||
|
||||
mkClevisLuksTest = { fallback ? false }: makeInstallerTest "clevis-luks${optionalString fallback "-fallback"}" {
|
||||
clevisTest = true;
|
||||
clevisFallbackTest = fallback;
|
||||
enableOCR = fallback;
|
||||
extraInstallerConfig = {
|
||||
environment.systemPackages = with pkgs; [ clevis ];
|
||||
};
|
||||
createPartitions = ''
|
||||
machine.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")
|
||||
'';
|
||||
extraConfig = ''
|
||||
boot.initrd.clevis.devices."crypt-root".secretFile = "/etc/nixos/clevis-secret.jwe";
|
||||
'';
|
||||
preBootCommands = ''
|
||||
tpm = Tpm()
|
||||
tpm.check()
|
||||
'' + optionalString fallback ''
|
||||
machine.start()
|
||||
${if systemdStage1 then ''
|
||||
machine.wait_for_text("Please enter")
|
||||
'' else ''
|
||||
machine.wait_for_text("Passphrase for")
|
||||
''}
|
||||
machine.send_chars("password\n")
|
||||
'';
|
||||
};
|
||||
|
||||
mkClevisZfsTest = { fallback ? false }: makeInstallerTest "clevis-zfs${optionalString fallback "-fallback"}" {
|
||||
clevisTest = true;
|
||||
clevisFallbackTest = fallback;
|
||||
enableOCR = fallback;
|
||||
extraInstallerConfig = {
|
||||
boot.supportedFilesystems = [ "zfs" ];
|
||||
environment.systemPackages = with pkgs; [ clevis ];
|
||||
};
|
||||
createPartitions = ''
|
||||
machine.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",
|
||||
"zpool create -O mountpoint=legacy rpool /dev/vda3",
|
||||
"echo -n password | zfs create"
|
||||
+ " -o encryption=aes-256-gcm -o keyformat=passphrase rpool/root",
|
||||
"mount -t zfs rpool/root /mnt",
|
||||
"mkfs.ext3 -L boot /dev/vda1",
|
||||
"mkdir -p /mnt/boot",
|
||||
"mount LABEL=boot /mnt/boot",
|
||||
"udevadm settle")
|
||||
'';
|
||||
extraConfig = ''
|
||||
boot.initrd.clevis.devices."rpool/root".secretFile = "/etc/nixos/clevis-secret.jwe";
|
||||
boot.zfs.requestEncryptionCredentials = true;
|
||||
|
||||
|
||||
# Using by-uuid overrides the default of by-id, and is unique
|
||||
# to the qemu disks, as they don't produce by-id paths for
|
||||
# some reason.
|
||||
boot.zfs.devNodes = "/dev/disk/by-uuid/";
|
||||
networking.hostId = "00000000";
|
||||
'';
|
||||
preBootCommands = ''
|
||||
tpm = Tpm()
|
||||
tpm.check()
|
||||
'' + optionalString fallback ''
|
||||
machine.start()
|
||||
${if systemdStage1 then ''
|
||||
machine.wait_for_text("Enter key for rpool/root")
|
||||
'' else ''
|
||||
machine.wait_for_text("Key load error")
|
||||
''}
|
||||
machine.send_chars("password\n")
|
||||
'';
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
# !!! `parted mkpart' seems to silently create overlapping partitions.
|
||||
|
@ -1175,6 +1386,13 @@ in {
|
|||
)
|
||||
'';
|
||||
};
|
||||
} // {
|
||||
clevisBcachefs = mkClevisBcachefsTest { };
|
||||
clevisBcachefsFallback = mkClevisBcachefsTest { fallback = true; };
|
||||
clevisLuks = mkClevisLuksTest { };
|
||||
clevisLuksFallback = mkClevisLuksTest { fallback = true; };
|
||||
clevisZfs = mkClevisZfsTest { };
|
||||
clevisZfsFallback = mkClevisZfsTest { fallback = true; };
|
||||
} // optionalAttrs systemdStage1 {
|
||||
stratisRoot = makeInstallerTest "stratisRoot" {
|
||||
createPartitions = ''
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
, ninja
|
||||
, pkg-config
|
||||
, tpm2-tools
|
||||
, nixosTests
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
|
@ -29,6 +30,12 @@ stdenv.mkDerivation rec {
|
|||
hash = "sha256-3J3ti/jRiv+p3eVvJD7u0ko28rPd8Gte0mCJaVaqyOs=";
|
||||
};
|
||||
|
||||
patches = [
|
||||
# Replaces the clevis-decrypt 300s timeout to a 10s timeout
|
||||
# https://github.com/latchset/clevis/issues/289
|
||||
./tang-timeout.patch
|
||||
];
|
||||
|
||||
postPatch = ''
|
||||
for f in $(find src/ -type f); do
|
||||
grep -q "/bin/cat" "$f" && substituteInPlace "$f" \
|
||||
|
@ -65,6 +72,14 @@ stdenv.mkDerivation rec {
|
|||
"man"
|
||||
];
|
||||
|
||||
passthru.tests = {
|
||||
inherit (nixosTests.installer) clevisBcachefs clevisBcachefsFallback clevisLuks clevisLuksFallback clevisZfs clevisZfsFallback;
|
||||
clevisLuksSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisLuks;
|
||||
clevisLuksFallbackSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisLuksFallback;
|
||||
clevisZfsSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisZfs;
|
||||
clevisZfsFallbackSystemdStage1 = nixosTests.installer-systemd-stage-1.clevisZfsFallback;
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
description = "Automated Encryption Framework";
|
||||
homepage = "https://github.com/latchset/clevis";
|
||||
|
|
13
pkgs/tools/security/clevis/tang-timeout.patch
Normal file
13
pkgs/tools/security/clevis/tang-timeout.patch
Normal file
|
@ -0,0 +1,13 @@
|
|||
diff --git a/src/pins/tang/clevis-decrypt-tang b/src/pins/tang/clevis-decrypt-tang
|
||||
index 72393b4..40b660f 100755
|
||||
--- a/src/pins/tang/clevis-decrypt-tang
|
||||
+++ b/src/pins/tang/clevis-decrypt-tang
|
||||
@@ -101,7 +101,7 @@ xfr="$(jose jwk exc -i '{"alg":"ECMR"}' -l- -r- <<< "$clt$eph")"
|
||||
|
||||
rec_url="$url/rec/$kid"
|
||||
ct="Content-Type: application/jwk+json"
|
||||
-if ! rep="$(curl -sfg -X POST -H "$ct" --data-binary @- "$rec_url" <<< "$xfr")"; then
|
||||
+if ! rep="$(curl --connect-timeout 10 -sfg -X POST -H "$ct" --data-binary @- "$rec_url" <<< "$xfr")"; then
|
||||
echo "Error communicating with server $url" >&2
|
||||
exit 1
|
||||
fi
|
Loading…
Add table
Add a link
Reference in a new issue