{ options, config, lib, pkgs, modulesPath, ... }: let cfg = config.vmapps; desktopItems' = pkgs.callPackage ../pkgs/desktopAdapters.nix { }; desktopItems = desktopItems'.overrideScope ( _: pre: { vmUser = "root"; waylandProxyUnit = if config.microvm.graphics.enable or false then "wayland-proxy.service" else null; } ); nixosSystem = import (pkgs.path + "/nixos/lib/eval-config.nix"); mkStaticVM = name: extraModules: extraHostModules: let evaluated = nixosSystem { inherit (pkgs.stdenv) system; inherit pkgs; modules = extraModules ++ [ { microvm.hypervisor = "cloud-hypervisor"; microvm.hotpluggedMem = 128; microvm.hotplugMem = 512; microvm.shares = [ { source = "send"; mountPoint = "/home/user/uvms/${name}/send"; tag = "rw-send"; proto = "virtiofs"; } { source = "authorized_keys.d"; mountPoint = "/etc/ssh/authorized_keys.d"; tag = "ro-auth-keys"; proto = "virtiofs"; } ]; networking.hostName = name; services.openssh.enable = true; } ./uvms-guest.nix ./vsock-connect-guest.nix (modulesPath + "/profiles/minimal.nix") (config._module.args.inputs."microvm.nix" + "/nixos-modules/microvm") ]; specialArgs = { inherit (config._module.args) inputs; }; }; runner = evaluated.config.microvm.declaredRunner; in lib.mkMerge ( extraHostModules ++ [ (lib.mkIf (options ? "debug"."closure"."layers") { debug.closure.extraLayers = [ runner ]; }) { systemd.user.targets.default.wants = [ "uvm@${name}.service" ]; # TODO: Ugly dirty hack, we didn't have the time to arrange a more # sensible workaround for CH trying to create TAPs, or to figure out # which of the services actually need the extra permissions (probably # microvm-run and tap-up) security.wrappers = lib.listToAttrs ( map ( exe: lib.nameValuePair "${exe}-${name}" { owner = "root"; group = "kvm"; setuid = false; capabilities = "cap_net_admin+ep"; source = pkgs.writeShellScript "${exe}-${name}" '' PATH=${lib.getBin pkgs.util-linux}/bin''${PATH:+:}''${PATH} PATH=${lib.getBin runner}/bin''${PATH:+:}''${PATH} ${exe} $@ ''; } ) [ "microvm-run" "microvm-shutdown" "virtiofsd-run" "virtiofsd-shutdown" "tap-up" "tap-down" ] ); } ] ); # TODO: clean up; came out of copy-pasting stuff backnforth preamble = '' set -euo pipefail INSTANCE="$1" shift STATE_DIR="$HOME/uvms/$INSTANCE" mkdir -p "$STATE_DIR" QUICKSHARE_DIR="$HOME/uvms/$INSTANCE/send" mkdir -p "$QUICKSHARE_DIR" cd "$STATE_DIR" mkdir -p "$STATE_DIR"/authorized_keys.d ( umask 0077 \ && cp --no-preserve=all /etc/ssh/ssh_host_ed25519_key.pub "$STATE_DIR"/authorized_keys.d/root \ && cp --no-preserve=all /etc/ssh/ssh_host_ed25519_key.pub "$STATE_DIR"/authorized_keys.d/user \ ) PATH=/run/wrappers/bin''${PATH:+:}''${PATH} set -x ''; in { options = { vmapps.enable = lib.mkEnableOption "Enable \"VM Apps\" demo"; }; # demo is currently based on microvm.nix config = lib.mkMerge [ (lib.mkIf (!(config._module.args ? "inputs"."microvm.nix")) { vmapps.enable = false; }) (lib.mkIf cfg.enable { networking.useNetworkd = true; networking.nftables.enable = true; systemd.network.enable = true; networking.firewall.extraInputRules = '' iifname "vt-*" udp dport { 53, 67 } accept ''; networking.nat.internalInterfaces = [ "vt-*" ]; users.motd = '' vmapp-demo.nix ============== Check `systemctl status --user uvm@browser`. Run a command inside the VM: `run-in-browser echo foo`, and see the outputs in syslog: `journalctl --user -u uvm@browser -e` ''; systemd.user.services."root-ssh-keys" = { wantedBy = [ "default.target" ]; before = [ "default.target" ]; script = '' [[ "$USER" == "root" ]] || exit 0 [[ -f "$HOME/.ssh/config" ]] && echo ".ssh/config already exists, not writing" && exit 0 cat > "$HOME/.ssh/config" << \EOF Host * IdentityFile /etc/ssh/ssh_host_ed25519_key EOF ''; }; systemd.user.services."uvm@" = rec { requires = [ "virtiofsd@.service" "taps@.service" ]; after = requires; serviceConfig.Type = "notify"; serviceConfig.LimitMEMLOCK = "infinity"; serviceConfig.LimitNOFILE = 1048576; serviceConfig.NotifyAccess = "all"; serviceConfig.ExecStop = "${pkgs.writeShellScript "microvm-shutdown-instance" '' ${preamble} microvm-shutdown-"$INSTANCE" ''} %i"; serviceConfig.ExecStart = "${pkgs.writeShellScript "uuvm-start-instance" '' ${preamble} microvm-run-"$INSTANCE" ''} %i"; }; systemd.user.services."virtiofsd@" = { requires = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; serviceConfig.Type = "notify"; serviceConfig.LimitMEMLOCK = "infinity"; serviceConfig.LimitNOFILE = 1048576; serviceConfig.NotifyAccess = "all"; serviceConfig.ExecStop = "${pkgs.writeShellScript "virtiofsd-shutdown-instance" '' ${preamble} virtiofsd-shutdown-"$INSTANCE" ''} %i"; serviceConfig.ExecStart = "${pkgs.writeShellScript "virtiofsd-start-instance" '' ${preamble} virtiofsd-run-"$INSTANCE" ''} %i"; }; systemd.user.services."taps@" = { serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; serviceConfig.SyslogIdentifier = "taps@"; serviceConfig.LimitMEMLOCK = "infinity"; serviceConfig.NotifyAccess = "all"; serviceConfig.ExecStop = "${pkgs.writeShellScript "taps-down-instance" '' ${preamble} tap-down-"$INSTANCE" ''} %i"; serviceConfig.ExecStart = "${pkgs.writeShellScript "taps-start-instance" '' ${preamble} tap-up-"$INSTANCE" ''} %i"; }; boot.initrd.systemd.settings.Manager.DefaultTimeoutStartSec = 30; systemd.settings.Manager.DefaultTimeoutStopSec = 10; systemd.services."user@".serviceConfig.TimeoutStopSec = 10; services.openssh.enable = true; environment.systemPackages = [ pkgs.xdg-utils ]; users.users.microvm.isSystemUser = true; users.users.microvm.group = "kvm"; }) (lib.mkIf cfg.enable ( mkStaticVM "browser" [ { microvm.interfaces = [ { type = "tap"; id = "vt-browse"; mac = "00:00:00:00:00:02"; } ]; environment.systemPackages = [ pkgs.tor-browser ]; } ] [ { environment.systemPackages = [ (desktopItems.mkRunInVMPkg "browser") (pkgs.makeDesktopItem ( desktopItems.toBrowser ( (desktopItems.toVM "browser" { name = "tor-browser"; exec = "tor-browser %U"; icon = "${pkgs.tor-browser}/share/icons/hicolor/128x128/apps/tor-browser.png"; desktopName = "Tor (uuvm/browser)"; startupNotify = true; terminal = false; type = "Application"; }) ) )) ]; } ] )) ]; }