diff --git a/nixosModules/default.nix b/nixosModules/default.nix index d6cf925..e81cde8 100644 --- a/nixosModules/default.nix +++ b/nixosModules/default.nix @@ -1,5 +1,16 @@ -{ self, virtwl, sidebus }: -{ pkgs, lib, utils, config, ... }: let +{ + self, + virtwl, + sidebus, +}: +{ + pkgs, + lib, + utils, + config, + ... +}: +let useTTY = { TTYPath = "/dev/hvc0"; StandardOutput = "tty"; @@ -7,265 +18,315 @@ StandardError = "tty"; }; runtimeDir = "/run/vm-user"; - userbornConfig = { - groups = lib.mapAttrsToList (username: opts: { - inherit (opts) name gid members; - }) config.users.groups; - users = lib.mapAttrsToList (username: opts: { - inherit (opts) - name - uid - group - description - home - password - hashedPassword - hashedPasswordFile - initialPassword - initialHashedPassword - ; - isNormal = opts.isNormalUser; - shell = utils.toShellPath opts.shell; - }) (lib.filterAttrs (_: u: u.enable) config.users.users); - }; - userbornConfigJson = pkgs.writeText "userborn.json" (builtins.toJSON userbornConfig); - userbornResults = pkgs.runCommand "baked userborn" {} "mkdir -p $out; ${lib.getExe pkgs.userborn} ${userbornConfigJson} $out"; + userbornConfig = { + groups = lib.mapAttrsToList (username: opts: { + inherit (opts) name gid members; + }) config.users.groups; + users = lib.mapAttrsToList (username: opts: { + inherit (opts) + name + uid + group + description + home + password + hashedPassword + hashedPasswordFile + initialPassword + initialHashedPassword + ; + isNormal = opts.isNormalUser; + shell = utils.toShellPath opts.shell; + }) (lib.filterAttrs (_: u: u.enable) config.users.users); + }; + userbornConfigJson = pkgs.writeText "userborn.json" (builtins.toJSON userbornConfig); + userbornResults = + pkgs.runCommand "baked userborn" { } + "mkdir -p $out; ${lib.getExe pkgs.userborn} ${userbornConfigJson} $out"; system = pkgs.stdenv.hostPlatform.system; -in { - boot.isContainer = true; - fileSystems."/".device = lib.mkDefault "/dev/sda"; # dummy - - # Disable unused things - environment.defaultPackages = lib.mkDefault [ ]; - documentation = { - enable = lib.mkDefault false; - doc.enable = lib.mkDefault false; - info.enable = lib.mkDefault false; - man.enable = lib.mkDefault false; - nixos.enable = lib.mkDefault false; +in +{ + options.virtualisation.munix.defaultCommand = lib.mkOption { + type = lib.types.str; + default = "bash"; + description = "Default command to run when starting the VM without arguments."; }; - services.logrotate.enable = false; - services.udisks2.enable = false; - system.tools.nixos-generate-config.enable = false; - system.activationScripts.specialfs = lib.mkForce ""; - systemd.coredump.enable = false; - networking.firewall.enable = false; - powerManagement.enable = false; - boot.kexec.enable = false; - console.enable = false; - # Configure activation / systemd - boot.initrd.systemd.enable = true; # for etc.overlay, but we don't have initrd - system.etc.overlay.enable = true; # erofs - system.etc.overlay.mutable = false; - system.switch.enable = false; - services.udev.enable = lib.mkDefault true; - services.udev.packages = lib.mkDefault []; - services.resolved.enable = false; - environment.etc."resolv.conf".source = "/run/resolv.conf"; - environment.etc."machine-id".source = "/run/machine-id"; - environment.etc."systemd/system".source = lib.mkForce (utils.systemdUtils.lib.generateUnits { - type = "system"; - units = config.systemd.units; - upstreamUnits = [ - "sysinit.target" - "local-fs.target" - "nss-user-lookup.target" - "umount.target" - "sockets.target" - "shutdown.target" - "reboot.target" - "exit.target" - "final.target" - "systemd-exit.service" - "systemd-journald.socket" - "systemd-journald-audit.socket" - "systemd-journald-dev-log.socket" - "systemd-journald.service" - "systemd-udevd-kernel.socket" - "systemd-udevd-control.socket" - "user.slice" - ]; - upstreamWants = ["multi-user.target.wants"]; - }); + config = { + boot.isContainer = true; + fileSystems."/".device = lib.mkDefault "/dev/sda"; # dummy - # systemd.package = pkgs.systemdMinimal; # no analyze - systemd.defaultUnit = "microvm.target"; - systemd.targets.microvm = { - description = "Minimal microVM system"; - wants = ["systemd-journald.socket" "systemd-udevd.service" "dbus.socket"]; - unitConfig.AllowIsolate = "yes"; - }; - systemd.services.generate-shutdown-ramfs.enable = lib.mkForce false; - systemd.services.systemd-remount-fs.enable = lib.mkForce false; - systemd.services.systemd-pstore.enable = lib.mkForce false; - systemd.services.lastlog2-import.enable = lib.mkForce false; - systemd.services.suid-sgid-wrappers.enable = lib.mkForce false; - systemd.services.systemd-udevd = { - # Redefine to remove the Before deps and get out of the critical chain - enable = true; - description = "Rule-based Manager for Device Events and Files"; - unitConfig.DefaultDependencies = "no"; - serviceConfig = { - CapabilityBoundingSet = "~CAP_SYS_TIME CAP_WAKE_ALARM"; - Delegate = ""; - DelegateSubgroup = "udev"; - Type = "notify-reload"; - OOMScoreAdjust = "-1000"; - Sockets = "systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd-varlink.socket"; - Restart = "always"; - RestartSec = "0"; - ExecStart = "${pkgs.systemd}/lib/systemd/systemd-udevd"; - FileDescriptorStoreMax = "512"; - FileDescriptorStorePreserve = "yes"; - KillMode = "mixed"; - TasksMax = "infinity"; - PrivateMounts = "yes"; - ProtectHostname = "yes"; - MemoryDenyWriteExecute = "yes"; - RestrictAddressFamilies = "AF_UNIX AF_NETLINK AF_INET AF_INET6"; - RestrictRealtime = "yes"; - RestrictSUIDSGID = "yes"; - SystemCallFilter = ["@system-service @module @raw-io bpf" "~@clock"]; - SystemCallErrorNumber = "EPERM"; - SystemCallArchitectures = "native"; - LockPersonality = "yes"; - IPAddressDeny = "any"; - WatchdogSec = "3min"; + # Disable unused things + environment.defaultPackages = lib.mkDefault [ ]; + documentation = { + enable = lib.mkDefault false; + doc.enable = lib.mkDefault false; + info.enable = lib.mkDefault false; + man.enable = lib.mkDefault false; + nixos.enable = lib.mkDefault false; }; - }; + services.logrotate.enable = false; + services.udisks2.enable = false; + system.tools.nixos-generate-config.enable = false; + system.activationScripts.specialfs = lib.mkForce ""; + systemd.coredump.enable = false; + networking.firewall.enable = false; + powerManagement.enable = false; + boot.kexec.enable = false; + console.enable = false; - # Configure user accounts - # The immutable overlay wants userborn or sysusers.. we just want baked-in files w/o running a service. - # So we can just run userborn at system closure build time! - systemd.sysusers.enable = false; - services.userborn.enable = true; - systemd.services.userborn.enable = false; - environment.etc."passwd" = lib.mkForce { source = "${userbornResults}/passwd"; mode = "0444"; }; - environment.etc."group" = lib.mkForce { source = "${userbornResults}/group"; mode = "0444"; }; - environment.etc."shadow" = lib.mkForce { source = "${userbornResults}/shadow"; mode = "0440"; }; - users.mutableUsers = false; - users.users.appvm = { - uid = 1337; - group = "appvm"; - isNormalUser = false; - isSystemUser = true; - home = "/home/appvm"; - description = "microVM User"; - extraGroups = [ "wheel" "video" "input" "systemd-journal" ]; - }; - users.groups.appvm.gid = 1337; - users.allowNoPasswordLogin = true; + # Configure activation / systemd + boot.initrd.systemd.enable = true; # for etc.overlay, but we don't have initrd + system.etc.overlay.enable = true; # erofs + system.etc.overlay.mutable = false; + system.switch.enable = false; + services.udev.enable = lib.mkDefault true; + services.udev.packages = lib.mkDefault [ ]; + services.resolved.enable = false; + environment.etc."resolv.conf".source = "/run/resolv.conf"; + environment.etc."machine-id".source = "/run/machine-id"; + environment.etc."systemd/system".source = lib.mkForce ( + utils.systemdUtils.lib.generateUnits { + type = "system"; + units = config.systemd.units; + upstreamUnits = [ + "sysinit.target" + "local-fs.target" + "nss-user-lookup.target" + "umount.target" + "sockets.target" + "shutdown.target" + "reboot.target" + "exit.target" + "final.target" + "systemd-exit.service" + "systemd-journald.socket" + "systemd-journald-audit.socket" + "systemd-journald-dev-log.socket" + "systemd-journald.service" + "systemd-udevd-kernel.socket" + "systemd-udevd-control.socket" + "user.slice" + ]; + upstreamWants = [ "multi-user.target.wants" ]; + } + ); - # Configure services - - systemd.settings.Manager.DefaultEnvironment = "XDG_RUNTIME_DIR=${runtimeDir}"; - - systemd.services.muvm-remote = { - enable = true; - description = "microVM Application runner"; - onFailure = ["exit.target"]; - onSuccess = ["exit.target"]; - wants = ["sockets.target"]; - after = ["sockets.target"]; - wantedBy = ["microvm.target"]; - serviceConfig = { - Type = "exec"; - PassEnvironment = ["TERM" "MESA_LOADER_DRIVER_OVERRIDE" "MUVM_REMOTE_CONFIG"]; # "KRUN_CONFIG"]; - Environment = [ - "WAYLAND_DISPLAY=wayland-1" - "DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeDir}/dbus.sock" - "PATH=/run/current-system/sw/bin" + # systemd.package = pkgs.systemdMinimal; # no analyze + systemd.defaultUnit = "microvm.target"; + systemd.targets.microvm = { + description = "Minimal microVM system"; + wants = [ + "systemd-journald.socket" + "systemd-udevd.service" + "dbus.socket" ]; - User = "appvm"; - Group = "appvm"; - ExecStartPre = "+/run/current-system/sw/bin/chown appvm:appvm ${runtimeDir}"; - ExecStart = "/opt/bin/muvm-remote"; - ExecStopPost = ''+${pkgs.python3}/bin/python -c "import os,fcntl,struct;print(os.getenv('EXIT_STATUS', '1'));fcntl.ioctl(os.open('/', os.O_RDONLY), 0x7602, int(os.getenv('EXIT_STATUS', '1')))"''; - } // useTTY; - }; + unitConfig.AllowIsolate = "yes"; + }; + systemd.services.generate-shutdown-ramfs.enable = lib.mkForce false; + systemd.services.systemd-remount-fs.enable = lib.mkForce false; + systemd.services.systemd-pstore.enable = lib.mkForce false; + systemd.services.lastlog2-import.enable = lib.mkForce false; + systemd.services.suid-sgid-wrappers.enable = lib.mkForce false; + systemd.services.systemd-udevd = { + # Redefine to remove the Before deps and get out of the critical chain + enable = true; + description = "Rule-based Manager for Device Events and Files"; + unitConfig.DefaultDependencies = "no"; + serviceConfig = { + CapabilityBoundingSet = "~CAP_SYS_TIME CAP_WAKE_ALARM"; + Delegate = ""; + DelegateSubgroup = "udev"; + Type = "notify-reload"; + OOMScoreAdjust = "-1000"; + Sockets = "systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd-varlink.socket"; + Restart = "always"; + RestartSec = "0"; + ExecStart = "${pkgs.systemd}/lib/systemd/systemd-udevd"; + FileDescriptorStoreMax = "512"; + FileDescriptorStorePreserve = "yes"; + KillMode = "mixed"; + TasksMax = "infinity"; + PrivateMounts = "yes"; + ProtectHostname = "yes"; + MemoryDenyWriteExecute = "yes"; + RestrictAddressFamilies = "AF_UNIX AF_NETLINK AF_INET AF_INET6"; + RestrictRealtime = "yes"; + RestrictSUIDSGID = "yes"; + SystemCallFilter = [ + "@system-service @module @raw-io bpf" + "~@clock" + ]; + SystemCallErrorNumber = "EPERM"; + SystemCallArchitectures = "native"; + LockPersonality = "yes"; + IPAddressDeny = "any"; + WatchdogSec = "3min"; + }; + }; - systemd.services.muvm-configure-network = { - enable = true; - description = "microVM Network configuration"; - wantedBy = ["microvm.target"]; - serviceConfig.Type = "oneshot"; - serviceConfig.ExecStart = "/opt/bin/muvm-configure-network"; - }; + # Configure user accounts + # The immutable overlay wants userborn or sysusers.. we just want baked-in files w/o running a service. + # So we can just run userborn at system closure build time! + systemd.sysusers.enable = false; + services.userborn.enable = true; + systemd.services.userborn.enable = false; + environment.etc."passwd" = lib.mkForce { + source = "${userbornResults}/passwd"; + mode = "0444"; + }; + environment.etc."group" = lib.mkForce { + source = "${userbornResults}/group"; + mode = "0444"; + }; + environment.etc."shadow" = lib.mkForce { + source = "${userbornResults}/shadow"; + mode = "0440"; + }; + users.mutableUsers = false; + users.users.appvm = { + uid = 1337; + group = "appvm"; + isNormalUser = false; + isSystemUser = true; + home = "/home/appvm"; + description = "microVM User"; + extraGroups = [ + "wheel" + "video" + "input" + "systemd-journal" + ]; + }; + users.groups.appvm.gid = 1337; + users.allowNoPasswordLogin = true; - systemd.sockets.muvm-pwbridge = { - enable = true; - description = "PipeWire cross-domain proxy socket"; - wantedBy = ["microvm.target"]; - partOf = ["muvm-pwbridge.service"]; - listenStreams = [ "${runtimeDir}/pipewire-0" ]; - socketConfig = { - SocketUser = "appvm"; - SocketGroup = "appvm"; + # Configure services + + systemd.settings.Manager.DefaultEnvironment = "XDG_RUNTIME_DIR=${runtimeDir}"; + + systemd.services.muvm-remote = { + enable = true; + description = "microVM Application runner"; + onFailure = [ "exit.target" ]; + onSuccess = [ "exit.target" ]; + wants = [ "sockets.target" ]; + after = [ "sockets.target" ]; + wantedBy = [ "microvm.target" ]; + serviceConfig = { + Type = "exec"; + PassEnvironment = [ + "TERM" + "MESA_LOADER_DRIVER_OVERRIDE" + "MUVM_REMOTE_CONFIG" + ]; # "KRUN_CONFIG"]; + Environment = [ + "WAYLAND_DISPLAY=wayland-1" + "DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeDir}/dbus.sock" + "PATH=/run/current-system/sw/bin" + ]; + User = "appvm"; + Group = "appvm"; + ExecStartPre = "+/run/current-system/sw/bin/chown appvm:appvm ${runtimeDir}"; + ExecStart = "/opt/bin/muvm-remote"; + ExecStopPost = ''+${pkgs.python3}/bin/python -c "import os,fcntl,struct;print(os.getenv('EXIT_STATUS', '1'));fcntl.ioctl(os.open('/', os.O_RDONLY), 0x7602, int(os.getenv('EXIT_STATUS', '1')))"''; + } + // useTTY; + }; + + systemd.services.muvm-configure-network = { + enable = true; + description = "microVM Network configuration"; + wantedBy = [ "microvm.target" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.ExecStart = "/opt/bin/muvm-configure-network"; + }; + + systemd.sockets.muvm-pwbridge = { + enable = true; + description = "PipeWire cross-domain proxy socket"; + wantedBy = [ "microvm.target" ]; + partOf = [ "muvm-pwbridge.service" ]; + listenStreams = [ "${runtimeDir}/pipewire-0" ]; + socketConfig = { + SocketUser = "appvm"; + SocketGroup = "appvm"; + }; + }; + systemd.services.muvm-pwbridge = { + enable = true; + description = "PipeWire cross-domain proxy"; + requires = [ "muvm-pwbridge.socket" ]; + serviceConfig.Type = "exec"; + serviceConfig.ExecStart = "/opt/bin/muvm-pwbridge"; + }; + + systemd.sockets.wayland-proxy-virtwl = { + enable = true; + description = "Wayland cross-domain proxy socket"; + wantedBy = [ "microvm.target" ]; + partOf = [ "wayland-proxy-virtwl.service" ]; + listenStreams = [ "${runtimeDir}/wayland-1" ]; + socketConfig = { + SocketUser = "appvm"; + SocketGroup = "appvm"; + FileDescriptorName = "wayland"; + }; + }; + systemd.services.wayland-proxy-virtwl = { + enable = true; + description = "Wayland cross-domain proxy"; + requires = [ "wayland-proxy-virtwl.socket" ]; + serviceConfig = { + ExecStartPre = "+/run/current-system/sw/bin/chmod 0666 /dev/dri/card0 /dev/dri/renderD128"; + ExecStart = "${virtwl.packages.${system}.proxy}/bin/wayland-proxy-virtwl --virtio-gpu"; + User = "appvm"; + Group = "appvm"; + }; + }; + + systemd.sockets.session-bus = { + enable = true; + description = "D-Bus session bus socket"; + wantedBy = [ "microvm.target" ]; + partOf = [ "session-bus.service" ]; + listenStreams = [ "${runtimeDir}/dbus.sock" ]; + socketConfig = { + SocketUser = "appvm"; + SocketGroup = "appvm"; + }; + }; + systemd.services.session-bus = { + enable = true; + description = "D-Bus session bus"; + requires = [ "session-bus.socket" ]; + serviceConfig = { + ImportCredential = "sidebus.port"; # inherited by the activated agent.. + ExecStart = "${pkgs.dbus}/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --syslog-only"; # no systemd activation, we don't run a *session* systemd + User = "appvm"; + Group = "appvm"; + }; + }; + services.dbus.packages = [ + (pkgs.writeTextDir "/share/dbus-1/services/org.freedesktop.portal.Desktop.service" '' + [D-BUS Service] + Name=org.freedesktop.portal.Desktop + Exec=${sidebus.packages.${system}.sidebus-agent}/bin/sidebus-agent + '') + ]; + + hardware.graphics.enable = true; + hardware.graphics.package = self.packages.${system}.mesa; + + system.build.munix = pkgs.symlinkJoin { + name = "munix"; + paths = [ self.packages.${system}.munix ]; + buildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/munix \ + --add-flags ${config.system.build.toplevel} \ + --set MICROVM_DEFAULT_COMMAND ${lib.escapeShellArg config.virtualisation.munix.defaultCommand} + ''; }; }; - systemd.services.muvm-pwbridge = { - enable = true; - description = "PipeWire cross-domain proxy"; - requires = ["muvm-pwbridge.socket"]; - serviceConfig.Type = "exec"; - serviceConfig.ExecStart = "/opt/bin/muvm-pwbridge"; - }; - - systemd.sockets.wayland-proxy-virtwl = { - enable = true; - description = "Wayland cross-domain proxy socket"; - wantedBy = ["microvm.target"]; - partOf = ["wayland-proxy-virtwl.service"]; - listenStreams = [ "${runtimeDir}/wayland-1" ]; - socketConfig = { - SocketUser = "appvm"; - SocketGroup = "appvm"; - FileDescriptorName = "wayland"; - }; - }; - systemd.services.wayland-proxy-virtwl = { - enable = true; - description = "Wayland cross-domain proxy"; - requires = ["wayland-proxy-virtwl.socket"]; - serviceConfig = { - ExecStartPre = "+/run/current-system/sw/bin/chmod 0666 /dev/dri/card0 /dev/dri/renderD128"; - ExecStart = "${virtwl.packages.${system}.proxy}/bin/wayland-proxy-virtwl --virtio-gpu"; - User = "appvm"; - Group = "appvm"; - }; - }; - - systemd.sockets.session-bus = { - enable = true; - description = "D-Bus session bus socket"; - wantedBy = ["microvm.target"]; - partOf = ["session-bus.service"]; - listenStreams = [ "${runtimeDir}/dbus.sock" ]; - socketConfig = { - SocketUser = "appvm"; - SocketGroup = "appvm"; - }; - }; - systemd.services.session-bus = { - enable = true; - description = "D-Bus session bus"; - requires = ["session-bus.socket"]; - serviceConfig = { - ImportCredential = "sidebus.port"; # inherited by the activated agent.. - ExecStart = "${pkgs.dbus}/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --syslog-only"; # no systemd activation, we don't run a *session* systemd - User = "appvm"; - Group = "appvm"; - }; - }; - services.dbus.packages = [ - (pkgs.writeTextDir "/share/dbus-1/services/org.freedesktop.portal.Desktop.service" '' - [D-BUS Service] - Name=org.freedesktop.portal.Desktop - Exec=${sidebus.packages.${system}.sidebus-agent}/bin/sidebus-agent - '') - ]; - - hardware.graphics.enable = true; - hardware.graphics.package = self.packages.${system}.mesa; } diff --git a/templates/musictest/flake.nix b/templates/musictest/flake.nix index 6f0d3ae..4178615 100644 --- a/templates/musictest/flake.nix +++ b/templates/musictest/flake.nix @@ -19,14 +19,16 @@ ... }: { - # First, define system configuration in a module: nixosModules.musictest = { pkgs, ... }: { system.stateVersion = "26.05"; + virtualisation.munix.defaultCommand = "euphonica"; + programs.dconf.enable = true; fonts.packages = with pkgs; [ adwaita-fonts ]; environment.systemPackages = with pkgs; [ euphonica ]; + # Local background service as a demo that doesn't require network creds :) services.mpd = { enable = true; @@ -48,7 +50,6 @@ }; }; - # And then define system closures per arch using the module above: nixosConfigurations.musictest-aarch64 = nixpkgs.lib.nixosSystem { system = "aarch64-linux"; modules = [ @@ -66,31 +67,11 @@ apps.aarch64-linux.default = { type = "app"; - program = "${ - nixpkgs.legacyPackages.aarch64-linux.symlinkJoin { - name = "munix"; - paths = [ munix.packages.aarch64-linux.munix ]; - buildInputs = [ nixpkgs.legacyPackages.aarch64-linux.makeWrapper ]; - postBuild = '' - wrapProgram $out/bin/munix --add-flags ${self.nixosConfigurations.musictest-aarch64.config.system.build.toplevel} --set MICROVM_DEFAULT_COMMAND euphonica - ''; - } - }/bin/munix"; - meta.description = "Run Music Demo App"; + program = "${self.nixosConfigurations.musictest-aarch64.config.system.build.munix}/bin/munix"; }; apps.x86_64-linux.default = { type = "app"; - program = "${ - nixpkgs.legacyPackages.x86_64-linux.symlinkJoin { - name = "munix"; - paths = [ munix.packages.x86_64-linux.munix ]; - buildInputs = [ nixpkgs.legacyPackages.x86_64-linux.makeWrapper ]; - postBuild = '' - wrapProgram $out/bin/munix --add-flags ${self.nixosConfigurations.musictest-x86_64.config.system.build.toplevel} --set MICROVM_DEFAULT_COMMAND euphonica - ''; - } - }/bin/munix"; - meta.description = "Run Music Demo App"; + program = "${self.nixosConfigurations.musictest-x86_64.config.system.build.munix}/bin/munix"; }; }; }