diff --git a/checks/default.nix b/checks/default.nix index 7cb48c88..c437b4fc 100644 --- a/checks/default.nix +++ b/checks/default.nix @@ -115,6 +115,37 @@ let imports = [ "${modulesPath}/profiles/hardened.nix" ]; }) ]; } ] + + [ { + # no + id = null; + } { + id = "credentials"; + modules = [ ({ config, pkgs, ... }: { + # This is the guest vm config + microvm.credentialFiles.SECRET_BOOTSRAP_KEY = "/etc/microvm-bootstrap.secret"; + microvm.testing.enableTest = builtins.elem config.microvm.hypervisor [ + # Hypervisors that support systemd credentials + "qemu" + ]; + # TODO: need to somehow have the test harness check for the success or failure of this service. + systemd.services.test-secret-availability = { + serviceConfig = { + ImportCredential = "SECRET_BOOTSRAP_KEY"; + Restart = "no"; + }; + path = [ pkgs.gnugrep pkgs.coreutils ]; + script = '' + cat $CREDENTIALS_DIRECTORY/SECRET_BOOTSRAP_KEY | grep -q "i am super secret" + if [ $? -ne 0 ]; then + echo "Secret not found at $CREDENTIALS_DIRECTORY/SECRET_BOOTSRAP_KEY" + exit 1 + fi + ''; + }; + }) ]; + } ] + ]; allVariants = diff --git a/checks/vm.nix b/checks/vm.nix index 3e4b30e9..0e918360 100644 --- a/checks/vm.nix +++ b/checks/vm.nix @@ -17,6 +17,8 @@ # Must be big enough for the store overlay volume virtualisation.diskSize = 4096; + environment.etc."microvm-bootstrap.secret".text = "i am super secret"; + microvm.vms."${system}-${hypervisor}-example".flake = self; }; testScript = '' diff --git a/lib/runners/qemu.nix b/lib/runners/qemu.nix index 18ad35fc..bab23023 100644 --- a/lib/runners/qemu.nix +++ b/lib/runners/qemu.nix @@ -38,7 +38,7 @@ let qemu = overrideQemu pkgs.qemu_kvm; - inherit (microvmConfig) hostName vcpu mem balloon initialBalloonMem deflateOnOOM hotplugMem hotpluggedMem user interfaces shares socket forwardPorts devices vsock graphics storeOnDisk kernel initrdPath storeDisk; + inherit (microvmConfig) hostName vcpu mem balloon initialBalloonMem deflateOnOOM hotplugMem hotpluggedMem user interfaces shares socket forwardPorts devices vsock graphics storeOnDisk kernel initrdPath storeDisk credentialFiles; inherit (microvmConfig.qemu) machine extraArgs serialConsole; inherit (import ../. { inherit (pkgs) lib; }) withDriveLetters; @@ -145,6 +145,8 @@ let then "console=ttyAMA0" else ""; + systemdCredentialStrings = lib.mapAttrsToList (name: path: "name=opt/io.systemd.credentials/${name},file=${path}" ) credentialFiles; + fwCfgOptions = systemdCredentialStrings; in lib.warnIf (mem == 2048) '' @@ -178,6 +180,9 @@ lib.warnIf (mem == 2048) '' "-chardev" "stdio,id=stdio,signal=off" "-device" "virtio-rng-${devType}" ] ++ + lib.optionals (fwCfgOptions != []) [ + "-fw_cfg" (lib.concatStringsSep "," fwCfgOptions) + ] ++ lib.optionals serialConsole [ "-serial" "chardev:stdio" ] ++ diff --git a/nixos-modules/microvm/options.nix b/nixos-modules/microvm/options.nix index 05dba15c..53940dd6 100644 --- a/nixos-modules/microvm/options.nix +++ b/nixos-modules/microvm/options.nix @@ -643,6 +643,19 @@ in This is required for commands like `microvm -l` to function but removes reference to the uncompressed store content when using a disk image for the nix store. ''; }; + + credentialFiles = mkOption { + type = with types; attrsOf path; + default = {}; + description = '' + Key-value pairs of credential files that will be loaded into the vm using systemd's io.systemd.credential feature. + ''; + example = literalExpression /* nix */ '' + { + SOPS_AGE_KEY = "/run/secrets/guest_microvm_age_key"; + } + ''; + }; }; imports = [