diff --git a/checks/default.nix b/checks/default.nix index 7cb48c88..62a69024 100644 --- a/checks/default.nix +++ b/checks/default.nix @@ -115,6 +115,39 @@ let imports = [ "${modulesPath}/profiles/hardened.nix" ]; }) ]; } ] + + [ { + # no + id = null; + } { + id = "credentials"; + modules = [ ({ config, pkgs, ... }: { + # This is the guest vm config + microvm = { + credentialFiles.SECRET_BOOTSTRAP_KEY = "/etc/microvm-bootstrap.secret"; + 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_BOOTSTRAP_KEY"; + Restart = "no"; + }; + path = [ pkgs.gnugrep pkgs.coreutils ]; + script = '' + cat $CREDENTIALS_DIRECTORY/SECRET_BOOTSTRAP_KEY | grep -q "i am super secret" + if [ $? -ne 0 ]; then + echo "Secret not found at $CREDENTIALS_DIRECTORY/SECRET_BOOTSTRAP_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/alioth.nix b/lib/runners/alioth.nix index 612066ae..78f2bea5 100644 --- a/lib/runners/alioth.nix +++ b/lib/runners/alioth.nix @@ -9,7 +9,7 @@ let user vcpu mem balloon initialBalloonMem hotplugMem hotpluggedMem interfaces volumes shares devices vsock kernel initrdPath - storeDisk storeOnDisk; + storeDisk storeOnDisk credentialFiles; in { command = if user != null @@ -22,6 +22,8 @@ in { then throw "alioth does not support hotplugMem" else if hotpluggedMem != 0 then throw "alioth does not support hotpluggedMem" + else if credentialFiles != {} + then throw "alioth does not support credentialFiles" else builtins.concatStringsSep " " ( [ "${pkgs.alioth}/bin/alioth" "run" diff --git a/lib/runners/cloud-hypervisor.nix b/lib/runners/cloud-hypervisor.nix index ef868dfc..cfd2bd8b 100644 --- a/lib/runners/cloud-hypervisor.nix +++ b/lib/runners/cloud-hypervisor.nix @@ -7,7 +7,7 @@ let inherit (pkgs) lib; - inherit (microvmConfig) vcpu mem balloon initialBalloonMem deflateOnOOM hotplugMem hotpluggedMem user interfaces volumes shares socket devices hugepageMem graphics storeDisk storeOnDisk kernel initrdPath; + inherit (microvmConfig) vcpu mem balloon initialBalloonMem deflateOnOOM hotplugMem hotpluggedMem user interfaces volumes shares socket devices hugepageMem graphics storeDisk storeOnDisk kernel initrdPath credentialFiles; inherit (microvmConfig.cloud-hypervisor) platformOEMStrings extraArgs; hasUserConsole = (extractOptValues "--console" extraArgs).values != []; @@ -147,6 +147,8 @@ in { command = if user != null then throw "cloud-hypervisor will not change user" + else if credentialFiles != {} + then throw "cloud-hypervisor does not support credentialFiles" else lib.escapeShellArgs ( [ (if graphics.enable diff --git a/lib/runners/crosvm.nix b/lib/runners/crosvm.nix index 2e6edd13..1df40b33 100644 --- a/lib/runners/crosvm.nix +++ b/lib/runners/crosvm.nix @@ -9,7 +9,7 @@ let inherit (pkgs.stdenv) system; inherit (microvmConfig) vcpu mem balloon initialBalloonMem hotplugMem hotpluggedMem user volumes shares - socket devices vsock graphics + socket devices vsock graphics credentialFiles kernel initrdPath storeDisk storeOnDisk; inherit (microvmConfig.crosvm) pivotRoot extraArgs; @@ -53,6 +53,8 @@ in { then throw "crosvm does not support hotplugMem" else if hotpluggedMem != 0 then throw "crosvm does not support hotpluggedMem" + else if credentialFiles != {} + then throw "crosvm does not support credentialFiles" else lib.escapeShellArgs ( [ "${pkgs.crosvm}/bin/crosvm" "run" diff --git a/lib/runners/firecracker.nix b/lib/runners/firecracker.nix index d7955d45..e1f2b9bf 100644 --- a/lib/runners/firecracker.nix +++ b/lib/runners/firecracker.nix @@ -10,7 +10,7 @@ let vcpu mem balloon initialBalloonMem hotplugMem hotpluggedMem interfaces volumes shares devices kernel initrdPath - storeDisk; + storeDisk credentialFiles; inherit (microvmConfig.firecracker) cpu; kernelPath = { @@ -83,6 +83,8 @@ in { then throw "hotplugMem not implemented for Firecracker" else if hotpluggedMem != 0 then throw "hotpluggedMem not implemented for Firecracker" + else if credentialFiles != {} + then throw "credentialFiles are not implemented for Firecracker" else lib.escapeShellArgs [ "${pkgs.firecracker}/bin/firecracker" "--config-file" configFile diff --git a/lib/runners/kvmtool.nix b/lib/runners/kvmtool.nix index e0cc8ff5..8c3e104f 100644 --- a/lib/runners/kvmtool.nix +++ b/lib/runners/kvmtool.nix @@ -8,7 +8,7 @@ let inherit (microvmConfig) hostName preStart user vcpu mem balloon initialBalloonMem hotplugMem hotpluggedMem interfaces volumes shares devices vsock - kernel initrdPath + kernel initrdPath credentialFiles storeDisk storeOnDisk; in { preStart = '' @@ -25,6 +25,8 @@ in { then throw "kvmtool does not support hotplugMem" else if hotpluggedMem != 0 then throw "kvmtool does not support hotpluggedMem" + else if credentialFiles != {} + then throw "kvmtool does not support credentialFiles" else builtins.concatStringsSep " " ( [ "${pkgs.kvmtool}/bin/lkvm" "run" diff --git a/lib/runners/qemu.nix b/lib/runners/qemu.nix index 3a4215ec..cf246f2b 100644 --- a/lib/runners/qemu.nix +++ b/lib/runners/qemu.nix @@ -49,7 +49,7 @@ let qemu = overrideQemu qemuPkg; - 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; @@ -155,6 +155,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) '' @@ -188,6 +190,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/lib/runners/stratovirt.nix b/lib/runners/stratovirt.nix index eeeb68f2..e945f76c 100644 --- a/lib/runners/stratovirt.nix +++ b/lib/runners/stratovirt.nix @@ -11,7 +11,7 @@ let inherit (microvmConfig) hostName vcpu mem balloon initialBalloonMem hotplugMem hotpluggedMem interfaces shares socket forwardPorts devices - kernel initrdPath + kernel initrdPath credentialFiles storeOnDisk storeDisk; tapMultiQueue = vcpu > 1; @@ -79,6 +79,8 @@ in { then throw "stratovirt does not support hotplugMem" else if hotpluggedMem != 0 then throw "stratovirt does not support hotpluggedMem" + else if credentialFiles != {} + then throw "stratovirt does not support credentialFiles" else lib.escapeShellArgs ( [ "${pkgs.expect}/bin/unbuffer" diff --git a/nixos-modules/microvm/options.nix b/nixos-modules/microvm/options.nix index 31ae71ba..5f208914 100644 --- a/nixos-modules/microvm/options.nix +++ b/nixos-modules/microvm/options.nix @@ -675,6 +675,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 = [