diff options
Diffstat (limited to 'nixos/modules/virtualisation')
-rw-r--r-- | nixos/modules/virtualisation/azure-agent.nix | 18 | ||||
-rw-r--r-- | nixos/modules/virtualisation/azure-image.nix | 46 | ||||
-rw-r--r-- | nixos/modules/virtualisation/google-compute-config.nix | 4 | ||||
-rw-r--r-- | nixos/modules/virtualisation/google-compute-image.nix | 13 | ||||
-rw-r--r-- | nixos/modules/virtualisation/incus.nix | 236 | ||||
-rw-r--r-- | nixos/modules/virtualisation/lxc-container.nix | 41 | ||||
-rw-r--r-- | nixos/modules/virtualisation/lxd.nix | 4 | ||||
-rw-r--r-- | nixos/modules/virtualisation/nixos-containers.nix | 15 | ||||
-rw-r--r-- | nixos/modules/virtualisation/oci-common.nix | 2 | ||||
-rw-r--r-- | nixos/modules/virtualisation/oci-containers.nix | 59 | ||||
-rw-r--r-- | nixos/modules/virtualisation/qemu-vm.nix | 85 | ||||
-rw-r--r-- | nixos/modules/virtualisation/vagrant-guest.nix | 1 | ||||
-rw-r--r-- | nixos/modules/virtualisation/virtualbox-host.nix | 2 |
13 files changed, 441 insertions, 85 deletions
diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix index 6e6021cf80f..e712fac17a4 100644 --- a/nixos/modules/virtualisation/azure-agent.nix +++ b/nixos/modules/virtualisation/azure-agent.nix @@ -61,7 +61,7 @@ in # Which provisioning agent to use. Supported values are "auto" (default), "waagent", # "cloud-init", or "disabled". - Provisioning.Agent=disabled + Provisioning.Agent=auto # Password authentication for root account will be unavailable. Provisioning.DeleteRootPassword=n @@ -241,7 +241,16 @@ in after = [ "network-online.target" "sshd.service" ]; wants = [ "network-online.target" ]; - path = [ pkgs.e2fsprogs pkgs.bash ]; + path = [ + pkgs.e2fsprogs + pkgs.bash + + # waagent's Microsoft.OSTCExtensions.VMAccessForLinux needs Python 3 + pkgs.python39 + + # waagent's Microsoft.CPlat.Core.RunCommandLinux needs lsof + pkgs.lsof + ]; description = "Windows Azure Agent Service"; unitConfig.ConditionPathExists = "/etc/waagent.conf"; serviceConfig = { @@ -250,5 +259,10 @@ in }; }; + # waagent will generate files under /etc/sudoers.d during provisioning + security.sudo.extraConfig = '' + #includedir /etc/sudoers.d + ''; + }; } diff --git a/nixos/modules/virtualisation/azure-image.nix b/nixos/modules/virtualisation/azure-image.nix index 17cfd393830..d909680cca1 100644 --- a/nixos/modules/virtualisation/azure-image.nix +++ b/nixos/modules/virtualisation/azure-image.nix @@ -16,6 +16,13 @@ in Size of disk image. Unit is MB. ''; }; + virtualisation.azureImage.contents = mkOption { + type = with types; listOf attrs; + default = [ ]; + description = lib.mdDoc '' + Extra contents to add to the image. + ''; + }; }; config = { system.build.azureImage = import ../../lib/make-disk-image.nix { @@ -26,46 +33,9 @@ in ''; configFile = ./azure-config-user.nix; format = "raw"; - inherit (cfg) diskSize; + inherit (cfg) diskSize contents; inherit config lib pkgs; }; - # Azure metadata is available as a CD-ROM drive. - fileSystems."/metadata".device = "/dev/sr0"; - - systemd.services.fetch-ssh-keys = { - description = "Fetch host keys and authorized_keys for root user"; - - wantedBy = [ "sshd.service" "waagent.service" ]; - before = [ "sshd.service" "waagent.service" ]; - - path = [ pkgs.coreutils ]; - script = - '' - eval "$(cat /metadata/CustomData.bin)" - if ! [ -z "$ssh_host_ecdsa_key" ]; then - echo "downloaded ssh_host_ecdsa_key" - echo "$ssh_host_ecdsa_key" > /etc/ssh/ssh_host_ed25519_key - chmod 600 /etc/ssh/ssh_host_ed25519_key - fi - - if ! [ -z "$ssh_host_ecdsa_key_pub" ]; then - echo "downloaded ssh_host_ecdsa_key_pub" - echo "$ssh_host_ecdsa_key_pub" > /etc/ssh/ssh_host_ed25519_key.pub - chmod 644 /etc/ssh/ssh_host_ed25519_key.pub - fi - - if ! [ -z "$ssh_root_auth_key" ]; then - echo "downloaded ssh_root_auth_key" - mkdir -m 0700 -p /root/.ssh - echo "$ssh_root_auth_key" > /root/.ssh/authorized_keys - chmod 600 /root/.ssh/authorized_keys - fi - ''; - serviceConfig.Type = "oneshot"; - serviceConfig.RemainAfterExit = true; - serviceConfig.StandardError = "journal+console"; - serviceConfig.StandardOutput = "journal+console"; - }; }; } diff --git a/nixos/modules/virtualisation/google-compute-config.nix b/nixos/modules/virtualisation/google-compute-config.nix index 3c503f027d7..887af26949f 100644 --- a/nixos/modules/virtualisation/google-compute-config.nix +++ b/nixos/modules/virtualisation/google-compute-config.nix @@ -84,6 +84,10 @@ in { groups = [ "google-sudoers" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; } ]; + security.sudo-rs.extraRules = mkIf config.users.mutableUsers [ + { groups = [ "google-sudoers" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; } + ]; + users.groups.google-sudoers = mkIf config.users.mutableUsers { }; boot.extraModprobeConfig = readFile "${pkgs.google-guest-configs}/etc/modprobe.d/gce-blacklist.conf"; diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix index 197ebb18b9a..e4a18fd81d7 100644 --- a/nixos/modules/virtualisation/google-compute-image.nix +++ b/nixos/modules/virtualisation/google-compute-image.nix @@ -44,10 +44,22 @@ in GZIP compression level of the resulting disk image (1-9). ''; }; + virtualisation.googleComputeImage.efi = mkEnableOption "EFI booting"; }; #### implementation config = { + boot.initrd.availableKernelModules = [ "nvme" ]; + boot.loader.grub = mkIf cfg.efi { + device = mkForce "nodev"; + efiSupport = true; + efiInstallAsRemovable = true; + }; + + fileSystems."/boot" = mkIf cfg.efi { + device = "/dev/disk/by-label/ESP"; + fsType = "vfat"; + }; system.build.googleComputeImage = import ../../lib/make-disk-image.nix { name = "google-compute-image"; @@ -62,6 +74,7 @@ in ''; format = "raw"; configFile = if cfg.configFile == null then defaultConfigFile else cfg.configFile; + partitionTableType = if cfg.efi then "efi" else "legacy"; inherit (cfg) diskSize; inherit config lib pkgs; }; diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix new file mode 100644 index 00000000000..3a4f0d7157a --- /dev/null +++ b/nixos/modules/virtualisation/incus.nix @@ -0,0 +1,236 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.virtualisation.incus; + preseedFormat = pkgs.formats.yaml { }; +in +{ + meta.maintainers = [ lib.maintainers.adamcstephens ]; + + options = { + virtualisation.incus = { + enable = lib.mkEnableOption (lib.mdDoc '' + incusd, a daemon that manages containers and virtual machines. + + Users in the "incus-admin" group can interact with + the daemon (e.g. to start or stop containers) using the + {command}`incus` command line tool, among others. + ''); + + package = lib.mkPackageOptionMD pkgs "incus" { }; + + lxcPackage = lib.mkPackageOptionMD pkgs "lxc" { }; + + preseed = lib.mkOption { + type = lib.types.nullOr ( + lib.types.submodule { freeformType = preseedFormat.type; } + ); + + default = null; + + description = lib.mdDoc '' + Configuration for Incus preseed, see + <https://linuxcontainers.org/incus/docs/main/howto/initialize/#non-interactive-configuration> + for supported values. + + Changes to this will be re-applied to Incus which will overwrite existing entities or create missing ones, + but entities will *not* be removed by preseed. + ''; + + example = { + networks = [ + { + name = "incusbr0"; + type = "bridge"; + config = { + "ipv4.address" = "10.0.100.1/24"; + "ipv4.nat" = "true"; + }; + } + ]; + profiles = [ + { + name = "default"; + devices = { + eth0 = { + name = "eth0"; + network = "incusbr0"; + type = "nic"; + }; + root = { + path = "/"; + pool = "default"; + size = "35GiB"; + type = "disk"; + }; + }; + } + ]; + storage_pools = [ + { + name = "default"; + driver = "dir"; + config = { + source = "/var/lib/incus/storage-pools/default"; + }; + } + ]; + }; + }; + + socketActivation = lib.mkEnableOption ( + lib.mdDoc '' + socket-activation for starting incus.service. Enabling this option + will stop incus.service from starting automatically on boot. + '' + ); + + startTimeout = lib.mkOption { + type = lib.types.ints.unsigned; + default = 600; + apply = toString; + description = lib.mdDoc '' + Time to wait (in seconds) for incusd to become ready to process requests. + If incusd does not reply within the configured time, `incus.service` will be + considered failed and systemd will attempt to restart it. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + # https://github.com/lxc/incus/blob/f145309929f849b9951658ad2ba3b8f10cbe69d1/doc/reference/server_settings.md + boot.kernel.sysctl = { + "fs.aio-max-nr" = lib.mkDefault 524288; + "fs.inotify.max_queued_events" = lib.mkDefault 1048576; + "fs.inotify.max_user_instances" = lib.mkOverride 1050 1048576; # override in case conflict nixos/modules/services/x11/xserver.nix + "fs.inotify.max_user_watches" = lib.mkOverride 1050 1048576; # override in case conflict nixos/modules/services/x11/xserver.nix + "kernel.dmesg_restrict" = lib.mkDefault 1; + "kernel.keys.maxbytes" = lib.mkDefault 2000000; + "kernel.keys.maxkeys" = lib.mkDefault 2000; + "net.core.bpf_jit_limit" = lib.mkDefault 1000000000; + "net.ipv4.neigh.default.gc_thresh3" = lib.mkDefault 8192; + "net.ipv6.neigh.default.gc_thresh3" = lib.mkDefault 8192; + # vm.max_map_count is set higher in nixos/modules/config/sysctl.nix + }; + + boot.kernelModules = [ + "veth" + "xt_comment" + "xt_CHECKSUM" + "xt_MASQUERADE" + "vhost_vsock" + ] ++ lib.optionals (!config.networking.nftables.enable) [ "iptable_mangle" ]; + + environment.systemPackages = [ cfg.package ]; + + # Note: the following options are also declared in virtualisation.lxc, but + # the latter can't be simply enabled to reuse the formers, because it + # does a bunch of unrelated things. + systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ]; + + security.apparmor = { + packages = [ cfg.lxcPackage ]; + policies = { + "bin.lxc-start".profile = '' + include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start + ''; + "lxc-containers".profile = '' + include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers + ''; + }; + }; + + systemd.services.incus = { + description = "Incus Container and Virtual Machine Management Daemon"; + + wantedBy = lib.mkIf (!cfg.socketActivation) [ "multi-user.target" ]; + after = [ + "network-online.target" + "lxcfs.service" + ] ++ (lib.optional cfg.socketActivation "incus.socket"); + requires = [ + "lxcfs.service" + ] ++ (lib.optional cfg.socketActivation "incus.socket"); + wants = [ + "network-online.target" + ]; + + path = lib.mkIf config.boot.zfs.enabled [ config.boot.zfs.package ]; + + environment = { + # Override Path to the LXC template configuration directory + INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config"; + }; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/incusd --group incus-admin"; + ExecStartPost = "${cfg.package}/bin/incusd waitready --timeout=${cfg.startTimeout}"; + ExecStop = "${cfg.package}/bin/incus admin shutdown"; + + KillMode = "process"; # when stopping, leave the containers alone + Delegate = "yes"; + LimitMEMLOCK = "infinity"; + LimitNOFILE = "1048576"; + LimitNPROC = "infinity"; + TasksMax = "infinity"; + + Restart = "on-failure"; + TimeoutStartSec = "${cfg.startTimeout}s"; + TimeoutStopSec = "30s"; + }; + }; + + systemd.sockets.incus = lib.mkIf cfg.socketActivation { + description = "Incus UNIX socket"; + wantedBy = [ "sockets.target" ]; + + socketConfig = { + ListenStream = "/var/lib/incus/unix.socket"; + SocketMode = "0660"; + SocketGroup = "incus-admin"; + Service = "incus.service"; + }; + }; + + systemd.services.incus-preseed = lib.mkIf (cfg.preseed != null) { + description = "Incus initialization with preseed file"; + + wantedBy = ["incus.service"]; + after = ["incus.service"]; + bindsTo = ["incus.service"]; + partOf = ["incus.service"]; + + script = '' + ${cfg.package}/bin/incus admin init --preseed <${ + preseedFormat.generate "incus-preseed.yaml" cfg.preseed + } + ''; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + users.groups.incus-admin = { }; + + users.users.root = { + # match documented default ranges https://linuxcontainers.org/incus/docs/main/userns-idmap/#allowed-ranges + subUidRanges = [ + { + startUid = 1000000; + count = 1000000000; + } + ]; + subGidRanges = [ + { + startGid = 1000000; + count = 1000000000; + } + ]; + }; + + virtualisation.lxc.lxcfs.enable = true; + }; +} diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix index 9402d3bf37d..61d7c4cb73f 100644 --- a/nixos/modules/virtualisation/lxc-container.nix +++ b/nixos/modules/virtualisation/lxc-container.nix @@ -9,15 +9,16 @@ in { options = { virtualisation.lxc = { - privilegedContainer = lib.mkOption { - type = lib.types.bool; - default = false; - description = lib.mdDoc '' - Whether this LXC container will be running as a privileged container or not. If set to `true` then - additional configuration will be applied to the `systemd` instance running within the container as - recommended by [distrobuilder](https://linuxcontainers.org/distrobuilder/introduction/). - ''; - }; + nestedContainer = lib.mkEnableOption (lib.mdDoc '' + Whether this container is configured as a nested container. On LXD containers this is recommended + for all containers and is enabled with `security.nesting = true`. + ''); + + privilegedContainer = lib.mkEnableOption (lib.mdDoc '' + Whether this LXC container will be running as a privileged container or not. If set to `true` then + additional configuration will be applied to the `systemd` instance running within the container as + recommended by [distrobuilder](https://linuxcontainers.org/distrobuilder/introduction/). + ''); }; }; @@ -36,7 +37,6 @@ in { ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system ''; - # TODO: build rootfs as squashfs for faster unpack system.build.tarball = pkgs.callPackage ../../lib/make-system-tarball.nix { extraArgs = "--owner=0"; @@ -63,11 +63,30 @@ in { extraCommands = "mkdir -p proc sys dev"; }; + system.build.squashfs = pkgs.callPackage ../../lib/make-squashfs.nix { + fileName = "nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}"; + + noStrip = true; # keep directory structure + comp = "zstd -Xcompression-level 6"; + + storeContents = [config.system.build.toplevel]; + + pseudoFiles = [ + "/sbin d 0755 0 0" + "/sbin/init s 0555 0 0 ${config.system.build.toplevel}/init" + "/dev d 0755 0 0" + "/proc d 0555 0 0" + "/sys d 0555 0 0" + ]; + }; + system.build.installBootLoader = pkgs.writeScript "install-lxd-sbin-init.sh" '' #!${pkgs.runtimeShell} - ln -fs "$1/init" /sbin/init + ${pkgs.coreutils}/bin/ln -fs "$1/init" /sbin/init ''; + systemd.additionalUpstreamSystemUnits = lib.mkIf cfg.nestedContainer ["systemd-udev-trigger.service"]; + # Add the overrides from lxd distrobuilder # https://github.com/lxc/distrobuilder/blob/05978d0d5a72718154f1525c7d043e090ba7c3e0/distrobuilder/main.go#L630 systemd.packages = [ diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index e30fbebb662..6f628c4a6e3 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -145,9 +145,7 @@ in { }; ui = { - enable = lib.mkEnableOption (lib.mdDoc '' - Enables the (experimental) LXD UI. - ''); + enable = lib.mkEnableOption (lib.mdDoc "(experimental) LXD UI"); package = lib.mkPackageOption pkgs.lxd-unwrapped "ui" { }; }; diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix index 5df9942dbc0..6fdb177b968 100644 --- a/nixos/modules/virtualisation/nixos-containers.nix +++ b/nixos/modules/virtualisation/nixos-containers.nix @@ -649,6 +649,15 @@ in ''; }; + restartIfChanged = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether the container should be restarted during a NixOS + configuration switch if its definition has changed. + ''; + }; + timeoutStartSec = mkOption { type = types.str; default = "1min"; @@ -745,7 +754,7 @@ in { services.postgresql.enable = true; services.postgresql.package = pkgs.postgresql_14; - system.stateVersion = "21.05"; + system.stateVersion = "${lib.trivial.release}"; }; }; } @@ -826,7 +835,7 @@ in containerConfig.path config.environment.etc."${configurationDirectoryName}/${name}.conf".source ]; - restartIfChanged = true; + restartIfChanged = containerConfig.restartIfChanged; } ) )) config.containers) @@ -897,4 +906,6 @@ in "tun" ]; }); + + meta.buildDocsInSandbox = false; } diff --git a/nixos/modules/virtualisation/oci-common.nix b/nixos/modules/virtualisation/oci-common.nix index ac9405e3ecf..a620df06315 100644 --- a/nixos/modules/virtualisation/oci-common.nix +++ b/nixos/modules/virtualisation/oci-common.nix @@ -56,5 +56,5 @@ in # Otherwise the instance may not have a working network-online.target, # making the fetch-ssh-keys.service fail - networking.useNetworkd = true; + networking.useNetworkd = lib.mkDefault true; } diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix index a9f4ab77f86..a4a40346f09 100644 --- a/nixos/modules/virtualisation/oci-containers.nix +++ b/nixos/modules/virtualisation/oci-containers.nix @@ -66,6 +66,17 @@ let ''; }; + labels = mkOption { + type = with types; attrsOf str; + default = {}; + description = lib.mdDoc "Labels to attach to the container at runtime."; + example = literalExpression '' + { + "traefik.https.routers.example.rule" = "Host(`example.container`)"; + } + ''; + }; + entrypoint = mkOption { type = with types; nullOr str; description = lib.mdDoc "Override the default entrypoint of the image."; @@ -203,6 +214,13 @@ let ''; }; + hostname = mkOption { + type = with types; nullOr str; + default = null; + description = lib.mdDoc "The hostname of the container."; + example = "hello-world"; + }; + extraOptions = mkOption { type = with types; listOf str; default = []; @@ -228,6 +246,25 @@ let mkService = name: container: let dependsOn = map (x: "${cfg.backend}-${x}.service") container.dependsOn; escapedName = escapeShellArg name; + preStartScript = pkgs.writeShellApplication { + name = "pre-start"; + runtimeInputs = [ ]; + text = '' + ${cfg.backend} rm -f ${name} || true + ${optionalString (isValidLogin container.login) '' + ${cfg.backend} login \ + ${container.login.registry} \ + --username ${container.login.username} \ + --password-stdin < ${container.login.passwordFile} + ''} + ${optionalString (container.imageFile != null) '' + ${cfg.backend} load -i ${container.imageFile} + ''} + ${optionalString (cfg.backend == "podman") '' + rm -f /run/podman-${escapedName}.ctr-id + ''} + ''; + }; in { wantedBy = [] ++ optional (container.autoStart) "multi-user.target"; after = lib.optionals (cfg.backend == "docker") [ "docker.service" "docker.socket" ] @@ -242,23 +279,6 @@ let else if cfg.backend == "podman" then [ config.virtualisation.podman.package ] else throw "Unhandled backend: ${cfg.backend}"; - preStart = '' - ${cfg.backend} rm -f ${name} || true - ${optionalString (isValidLogin container.login) '' - cat ${container.login.passwordFile} | \ - ${cfg.backend} login \ - ${container.login.registry} \ - --username ${container.login.username} \ - --password-stdin - ''} - ${optionalString (container.imageFile != null) '' - ${cfg.backend} load -i ${container.imageFile} - ''} - ${optionalString (cfg.backend == "podman") '' - rm -f /run/podman-${escapedName}.ctr-id - ''} - ''; - script = concatStringsSep " \\\n " ([ "exec ${cfg.backend} run" "--rm" @@ -266,6 +286,8 @@ let "--log-driver=${container.log-driver}" ] ++ optional (container.entrypoint != null) "--entrypoint=${escapeShellArg container.entrypoint}" + ++ optional (container.hostname != null) + "--hostname=${escapeShellArg container.hostname}" ++ lib.optionals (cfg.backend == "podman") [ "--cidfile=/run/podman-${escapedName}.ctr-id" "--cgroups=no-conmon" @@ -277,6 +299,7 @@ let ++ map (p: "-p ${escapeShellArg p}") container.ports ++ optional (container.user != null) "-u ${escapeShellArg container.user}" ++ map (v: "-v ${escapeShellArg v}") container.volumes + ++ (mapAttrsToList (k: v: "-l ${escapeShellArg k}=${escapeShellArg v}") container.labels) ++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}" ++ map escapeShellArg container.extraOptions ++ [container.image] @@ -306,7 +329,7 @@ let ### # ExecReload = ...; ### - + ExecStartPre = [ "${preStartScript}/bin/pre-start" ]; TimeoutStartSec = 0; TimeoutStopSec = 120; Restart = "always"; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 74c3e1ecd03..6f275baf60d 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -198,6 +198,39 @@ let fi ''} + ${lib.optionalString cfg.tpm.enable '' + NIX_SWTPM_DIR=$(readlink -f "''${NIX_SWTPM_DIR:-${config.system.name}-swtpm}") + mkdir -p "$NIX_SWTPM_DIR" + ${lib.getExe cfg.tpm.package} \ + socket \ + --tpmstate dir="$NIX_SWTPM_DIR" \ + --ctrl type=unixio,path="$NIX_SWTPM_DIR"/socket,terminate \ + --pid file="$NIX_SWTPM_DIR"/pid --daemon \ + --tpm2 \ + --log file="$NIX_SWTPM_DIR"/stdout,level=6 + + # Enable `fdflags` builtin in Bash + # We will need it to perform surgical modification of the file descriptor + # passed in the coprocess to remove `FD_CLOEXEC`, i.e. close the file descriptor + # on exec. + # If let alone, it will trigger the coprocess to read EOF when QEMU is `exec` + # at the end of this script. To work around that, we will just clear + # the `FD_CLOEXEC` bits as a first step. + enable -f ${hostPkgs.bash}/lib/bash/fdflags fdflags + # leave a dangling subprocess because the swtpm ctrl socket has + # "terminate" when the last connection disconnects, it stops swtpm. + # When qemu stops, or if the main shell process ends, the coproc will + # get signaled by virtue of the pipe between main and coproc ending. + # Which in turns triggers a socat connect-disconnect to swtpm which + # will stop it. + coproc waitingswtpm { + read || : + echo "" | ${lib.getExe hostPkgs.socat} STDIO UNIX-CONNECT:"$NIX_SWTPM_DIR"/socket + } + # Clear `FD_CLOEXEC` on the coprocess' file descriptor stdin. + fdflags -s-cloexec ''${waitingswtpm[1]} + ''} + cd "$TMPDIR" ${lib.optionalString (cfg.emptyDiskImages != []) "idx=0"} @@ -267,6 +300,7 @@ let }; storeImage = import ../../lib/make-disk-image.nix { + name = "nix-store-image"; inherit pkgs config lib; additionalPaths = [ regInfo ]; format = "qcow2"; @@ -656,8 +690,8 @@ in package = mkOption { type = types.package; - default = hostPkgs.qemu_kvm; - defaultText = literalExpression "config.virtualisation.host.pkgs.qemu_kvm"; + default = if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then hostPkgs.qemu_kvm else hostPkgs.qemu; + defaultText = literalExpression "if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then config.virtualisation.host.pkgs.qemu_kvm else config.virtualisation.host.pkgs.qemu"; example = literalExpression "pkgs.qemu_test"; description = lib.mdDoc "QEMU package to use."; }; @@ -862,6 +896,32 @@ in }; }; + virtualisation.tpm = { + enable = mkEnableOption "a TPM device in the virtual machine with a driver, using swtpm."; + + package = mkPackageOptionMD cfg.host.pkgs "swtpm" { }; + + deviceModel = mkOption { + type = types.str; + default = ({ + "i686-linux" = "tpm-tis"; + "x86_64-linux" = "tpm-tis"; + "ppc64-linux" = "tpm-spapr"; + "armv7-linux" = "tpm-tis-device"; + "aarch64-linux" = "tpm-tis-device"; + }.${pkgs.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU")); + defaultText = '' + Based on the guest platform Linux system: + + - `tpm-tis` for (i686, x86_64) + - `tpm-spapr` for ppc64 + - `tpm-tis-device` for (armv7, aarch64) + ''; + example = "tpm-tis-device"; + description = lib.mdDoc "QEMU device model for the TPM, uses the appropriate default based on th guest platform system and the package passed."; + }; + }; + virtualisation.useDefaultFilesystems = mkOption { type = types.bool; @@ -937,7 +997,7 @@ in virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max. ''; } - { assertion = cfg.directBoot.initrd != options.virtualisation.directBoot.initrd.default -> cfg.directBoot.enable; + { assertion = cfg.directBoot.enable || cfg.directBoot.initrd == options.virtualisation.directBoot.initrd.default; message = '' You changed the default of `virtualisation.directBoot.initrd` but you are not @@ -1027,7 +1087,8 @@ in boot.initrd.availableKernelModules = optional cfg.writableStore "overlay" - ++ optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx"; + ++ optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx" + ++ optional (cfg.tpm.enable) "tpm_tis"; virtualisation.additionalPaths = [ config.system.build.toplevel ]; @@ -1098,6 +1159,11 @@ in (mkIf (!cfg.graphics) [ "-nographic" ]) + (mkIf (cfg.tpm.enable) [ + "-chardev socket,id=chrtpm,path=\"$NIX_SWTPM_DIR\"/socket" + "-tpmdev emulator,id=tpm_dev_0,chardev=chrtpm" + "-device ${cfg.tpm.deviceModel},tpmdev=tpm_dev_0" + ]) ]; virtualisation.qemu.drives = mkMerge [ @@ -1121,11 +1187,12 @@ in }) cfg.emptyDiskImages) ]; - # Use mkVMOverride to enable building test VMs (e.g. via `nixos-rebuild - # build-vm`) of a system configuration, where the regular value for the - # `fileSystems' attribute should be disregarded (since those filesystems - # don't necessarily exist in the VM). - fileSystems = mkVMOverride cfg.fileSystems; + # By default, use mkVMOverride to enable building test VMs (e.g. via + # `nixos-rebuild build-vm`) of a system configuration, where the regular + # value for the `fileSystems' attribute should be disregarded (since those + # filesystems don't necessarily exist in the VM). You can disable this + # override by setting `virtualisation.fileSystems = lib.mkForce { };`. + fileSystems = lib.mkIf (cfg.fileSystems != { }) (mkVMOverride cfg.fileSystems); virtualisation.fileSystems = let mkSharedDir = tag: share: diff --git a/nixos/modules/virtualisation/vagrant-guest.nix b/nixos/modules/virtualisation/vagrant-guest.nix index 263b1ebca08..2fad376086e 100644 --- a/nixos/modules/virtualisation/vagrant-guest.nix +++ b/nixos/modules/virtualisation/vagrant-guest.nix @@ -55,4 +55,5 @@ in }; security.sudo.wheelNeedsPassword = false; + security.sudo-rs.wheelNeedsPassword = false; } diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix index b1565a09682..9741ea090f7 100644 --- a/nixos/modules/virtualisation/virtualbox-host.nix +++ b/nixos/modules/virtualisation/virtualbox-host.nix @@ -91,7 +91,7 @@ in }; config = mkIf cfg.enable (mkMerge [{ - warnings = mkIf (config.nixpkgs.config.virtualbox.enableExtensionPack or false) + warnings = mkIf (pkgs.config.virtualbox.enableExtensionPack or false) ["'nixpkgs.virtualbox.enableExtensionPack' has no effect, please use 'virtualisation.virtualbox.host.enableExtensionPack'"]; boot.kernelModules = [ "vboxdrv" "vboxnetadp" "vboxnetflt" ]; boot.extraModulePackages = [ kernelModules ]; |