summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Malka <julien@malka.sh>2023-11-18 19:38:08 +0000
committerRaito Bezarius <masterancpp@gmail.com>2023-11-28 19:13:46 +0100
commit5d2420e5e71601b3eb031a7ceaba57bf9a480b6f (patch)
tree159ed08efc106c8f9068ffdb852512797bfb1295
parentnixos/clevis: init (diff)
downloadnixpkgs-origin/clevis.tar.gz
nixos/tests/installer: init clevis testsorigin/clevis
Co-Authored-By: Camille Mondon <camillemondon@free.fr>
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix4
-rw-r--r--nixos/tests/installer.nix248
2 files changed, 237 insertions, 15 deletions
diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix
index 1b4c92b584b9..d0c01a779ef1 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -32,6 +32,10 @@
stratisRoot
swraid
zfsroot
+ clevisLuks
+ clevisLuksFallback
+ clevisZfs
+ clevisZfsFallback
;
}
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index e9ec28749850..f7fc168eba8c 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -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 = ''