at 23.11-pre 4.1 kB view raw
1import ./make-test-python.nix ({ lib, pkgs, system, ... }: 2 3let 4 tpmSocketPath = "/tmp/swtpm-sock"; 5 tpmDeviceModels = { 6 x86_64-linux = "tpm-tis"; 7 aarch64-linux = "tpm-tis-device"; 8 }; 9in 10 11{ 12 name = "systemd-credentials-tpm2"; 13 14 meta = { 15 maintainers = with pkgs.lib.maintainers; [ tmarkus ]; 16 }; 17 18 nodes.machine = { pkgs, ... }: { 19 virtualisation = { 20 qemu.options = [ 21 "-chardev socket,id=chrtpm,path=${tpmSocketPath}" 22 "-tpmdev emulator,id=tpm_dev_0,chardev=chrtpm" 23 "-device ${tpmDeviceModels.${system}},tpmdev=tpm_dev_0" 24 ]; 25 }; 26 27 boot.initrd.availableKernelModules = [ "tpm_tis" ]; 28 29 environment.systemPackages = with pkgs; [ diffutils ]; 30 }; 31 32 testScript = '' 33 import subprocess 34 from tempfile import TemporaryDirectory 35 36 # From systemd-initrd-luks-tpm2.nix 37 class Tpm: 38 def __init__(self): 39 self.state_dir = TemporaryDirectory() 40 self.start() 41 42 def start(self): 43 self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", 44 "socket", 45 "--tpmstate", f"dir={self.state_dir.name}", 46 "--ctrl", "type=unixio,path=${tpmSocketPath}", 47 "--tpm2", 48 ]) 49 50 # Check whether starting swtpm failed 51 try: 52 exit_code = self.proc.wait(timeout=0.2) 53 if exit_code is not None and exit_code != 0: 54 raise Exception("failed to start swtpm") 55 except subprocess.TimeoutExpired: 56 pass 57 58 """Check whether the swtpm process exited due to an error""" 59 def check(self): 60 exit_code = self.proc.poll() 61 if exit_code is not None and exit_code != 0: 62 raise Exception("swtpm process died") 63 64 CRED_NAME = "testkey" 65 CRED_RAW_FILE = f"/root/{CRED_NAME}" 66 CRED_FILE = f"/root/{CRED_NAME}.cred" 67 68 def systemd_run(machine, cmd): 69 machine.log(f"Executing command (via systemd-run): \"{cmd}\"") 70 71 (status, out) = machine.execute( " ".join([ 72 "systemd-run", 73 "--service-type=exec", 74 "--quiet", 75 "--wait", 76 "-E PATH=\"$PATH\"", 77 "-p StandardOutput=journal", 78 "-p StandardError=journal", 79 f"-p LoadCredentialEncrypted={CRED_NAME}:{CRED_FILE}", 80 f"$SHELL -c '{cmd}'" 81 ]) ) 82 83 if status != 0: 84 raise Exception(f"systemd_run failed (status {status})") 85 86 machine.log("systemd-run finished successfully") 87 88 tpm = Tpm() 89 90 @polling_condition 91 def swtpm_running(): 92 tpm.check() 93 94 machine.wait_for_unit("multi-user.target") 95 96 with subtest("Check whether TPM device exists"): 97 machine.succeed("test -e /dev/tpm0") 98 machine.succeed("test -e /dev/tpmrm0") 99 100 with subtest("Check whether systemd-creds detects TPM2 correctly"): 101 cmd = "systemd-creds has-tpm2" 102 machine.log(f"Running \"{cmd}\"") 103 (status, _) = machine.execute(cmd) 104 105 # Check exit code equals 0 or 1 (1 means firmware support is missing, which is OK here) 106 if status != 0 and status != 1: 107 raise Exception("systemd-creds failed to detect TPM2") 108 109 with subtest("Encrypt credential using systemd-creds"): 110 machine.succeed(f"dd if=/dev/urandom of={CRED_RAW_FILE} bs=1k count=16") 111 machine.succeed(f"systemd-creds --with-key=host+tpm2 encrypt --name=testkey {CRED_RAW_FILE} {CRED_FILE}") 112 113 with subtest("Write provided credential and check for equality"): 114 CRED_OUT_FILE = f"/root/{CRED_NAME}.out" 115 systemd_run(machine, f"systemd-creds cat testkey > {CRED_OUT_FILE}") 116 machine.succeed(f"cmp --silent -- {CRED_RAW_FILE} {CRED_OUT_FILE}") 117 118 with subtest("Check whether systemd service can see credential in systemd-creds list"): 119 systemd_run(machine, f"systemd-creds list | grep {CRED_NAME}") 120 121 with subtest("Check whether systemd service can access credential in $CREDENTIALS_DIRECTORY"): 122 systemd_run(machine, f"cmp --silent -- $CREDENTIALS_DIRECTORY/{CRED_NAME} {CRED_RAW_FILE}") 123 ''; 124})