flake: { pkgs , name ? "sandbox" , user ? "dummy" , config ? { } , tools ? [] , envVars ? { } , restrictNetwork ? true # to be replaced with virtualisation.restrictNetwork , patchQemu9p ? false # until qemu 7.2.0 becomes available in nixpkgs }: let shellLib = flake.lib.shell { inherit pkgs; }; in rec { nixosConfigurations.${name} = pkgs.nixos ({ modulesPath, lib, pkgs, ... }: { imports = [ (modulesPath + "/profiles/minimal.nix") { environment.noXlibs = false; } # avoid mass rebuild (modulesPath + "/profiles/qemu-guest.nix") (modulesPath + "/virtualisation/qemu-vm.nix") config ]; system.stateVersion = lib.mkDefault lib.trivial.release; networking = { hostName = name; firewall.enable = lib.mkDefault false; }; users.users.${user} = { isNormalUser = lib.mkDefault true; password = lib.mkDefault ""; extraGroups = lib.mkDefault [ "wheel" ]; }; security.sudo.wheelNeedsPassword = lib.mkDefault false; services.getty = { autologinUser = lib.mkDefault user; helpLine = lib.mkDefault '' Press to terminate the virtual machine. The working directory on the host is mounted to /mnt. ''; }; environment = { variables = envVars; systemPackages = tools; # TODO: also print a summary of the host mount points # TODO: also print a summary of the forwarded ports interactiveShellInit = lib.mkBefore '' ${shellLib.ifSomeAttrs envVars shellLib.printEnvVars} ${shellLib.ifSomeList tools shellLib.printBins} echo ''; }; virtualisation = { graphics = lib.mkDefault false; diskImage = lib.mkDefault "$TMP_DISK"; sharedDirectories.host = { source = "$SHARED_CWD"; target = "/mnt"; }; # Uncomment when this is merged: # https://github.com/NixOS/nixpkgs/pull/200225 #restrictNetwork = lib.mkDefault true; # Patched QEMU to fix slow 9p file share. # https://linus.schreibt.jetzt/posts/qemu-9p-performance.html qemu.package = lib.mkDefault ( if patchQemu9p then assert !(pkgs.lib.versionAtLeast pkgs.qemu_kvm.version "7.2.0"); pkgs.qemu_kvm.overrideAttrs (o: { patches = o.patches ++ [ (pkgs.fetchpatch { name = "qemu-9p-performance-fix.patch"; url = "https://github.com/qemu/qemu/commit/f5265c8.patch"; sha256 = "sha256-PSOv0dhiEq9g6B1uIbs6vbhGr7BQWCtAoLHnk4vnvVg="; }) ]; }) else pkgs.qemu_kvm ); }; }); packages.${name} = nixosConfigurations.${name}.config.system.build.vm; apps.${name} = { type = "app"; program = toString (pkgs.writeShellScript "sandbox-vm" ( (pkgs.lib.optionalString restrictNetwork '' # Isolate from network # Stopgap solution until this is merged: # https://github.com/NixOS/nixpkgs/pull/200225 QEMU_NET_OPTS="restrict=yes,''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}" export QEMU_NET_OPTS '') + '' # Save current directory for mounting in VM SHARED_CWD=$PWD export SHARED_CWD TMP_DISK="$(mktemp).qcow2" export TMP_DISK trap "rm -f \"$TMP_DISK\"" EXIT ${packages.${name}}/bin/run-${name}-vm reset echo "Exited virtual machine." '')); }; }