From 1464ef774b40be0d60e44c8e9ec295d9305c600b Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 30 Dec 2024 02:02:20 +0900 Subject: [PATCH] cmd/fpkg: expose nixGL wrappers Signed-off-by: Ophestra --- bundle.nix | 21 +++++++- cmd/fpkg/bundle.go | 2 + cmd/fpkg/install.go | 97 ++++--------------------------------- cmd/fpkg/start.go | 113 +++++++++++++++++++++++++++++++++----------- cmd/fpkg/with.go | 98 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 115 deletions(-) create mode 100644 cmd/fpkg/with.go diff --git a/bundle.nix b/bundle.nix index ba2ffa7..aa79890 100644 --- a/bundle.nix +++ b/bundle.nix @@ -10,6 +10,7 @@ writeScript, runtimeShell, writeText, + symlinkJoin, vmTools, runCommand, fetchFromGitHub, @@ -117,6 +118,23 @@ let hash = "sha256-lnzZQYG0+EXl/6NkGpyIz+FEOc/DSEG57AP1VsdeNrM="; }; + mesaWrappers = + let + isIntelX86Platform = system == "x86_64-linux"; + nixGLPackages = import (nixGL + "/default.nix") { + pkgs = nixpkgs.legacyPackages.${system}; + enable32bits = isIntelX86Platform; + enableIntelX86Extensions = isIntelX86Platform; + }; + in + symlinkJoin { + name = "nixGL-mesa"; + paths = with nixGLPackages; [ + nixGLIntel + nixVulkanIntel + ]; + }; + info = builtins.toJSON { inherit name @@ -152,6 +170,7 @@ let + (if allow_dbus then 4 else 0) + (if allow_pulse then 8 else 0); + mesa = if gpu then mesaWrappers else null; nix_gl = if gpu then nixGL else null; current_system = nixos.config.system.build.toplevel; activation_package = homeManagerConfiguration.activationPackage; @@ -169,7 +188,7 @@ writeScript "fortify-${pname}-bundle-prelude" '' chmod -R +r "$OUT/nix/var" nix copy --no-check-sigs --to "file://$OUT/res?compression=zstd&compression-level=19¶llel-compression=true" \ "${homeManagerConfiguration.activationPackage}" \ - "${launcher}" ${if gpu then nixGL else ""} + "${launcher}" ${if gpu then "${mesaWrappers} ${nixGL}" else ""} mkdir -p "$OUT/etc" tar -C "$OUT/etc" -xf "${etc}/etc.tar" cp "${writeText "bundle.json" info}" "$OUT/bundle.json" diff --git a/cmd/fpkg/bundle.go b/cmd/fpkg/bundle.go index 572b295..22b9de2 100644 --- a/cmd/fpkg/bundle.go +++ b/cmd/fpkg/bundle.go @@ -40,6 +40,8 @@ type bundleInfo struct { // allow gpu access within sandbox GPU bool `json:"gpu"` + // store path to nixGL mesa wrappers + Mesa string `json:"mesa,omitempty"` // store path to nixGL source NixGL string `json:"nix_gl,omitempty"` // store path to activate-and-exec script diff --git a/cmd/fpkg/install.go b/cmd/fpkg/install.go index 5e4d67a..47d4ffc 100644 --- a/cmd/fpkg/install.go +++ b/cmd/fpkg/install.go @@ -5,7 +5,6 @@ import ( "flag" "os" "path" - "strings" "git.gensokyo.uk/security/fortify/fst" "git.gensokyo.uk/security/fortify/internal/fmsg" @@ -144,6 +143,15 @@ func actionInstall(args []string) { "chmod 0755 .", }, workDir, bundle, pathSet, dropShellInstall, cleanup) + if bundle.GPU { + withCacheDir("mesa-wrappers", []string{ + // link nixGL mesa wrappers + "mkdir -p nix/.nixGL", + "ln -s " + bundle.Mesa + "/bin/nixGLIntel nix/.nixGL/nixGL", + "ln -s " + bundle.Mesa + "/bin/nixVulkanIntel nix/.nixGL/nixVulkan", + }, workDir, bundle, pathSet, false, cleanup) + } + /* Activate home-manager generation. */ @@ -155,7 +163,7 @@ func actionInstall(args []string) { "rm -rf .local/state/{nix,home-manager}", // run activation script bundle.ActivationPackage + "/activate", - }, false, bundle, pathSet, dropShellActivate, cleanup) + }, false, func(config *fst.Config) *fst.Config { return config }, bundle, pathSet, dropShellActivate, cleanup) /* Installation complete. Write metadata to block re-installs or downgrades. @@ -183,88 +191,3 @@ func actionInstall(args []string) { cleanup() } - -func withNixDaemon(action string, command []string, net bool, bundle *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { - fortifyAppDropShell(&fst.Config{ - ID: bundle.ID, - Command: []string{shell, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " + - // start nix-daemon - "nix-daemon --store / & " + - // wait for socket to appear - "(while [ ! -S /nix/var/nix/daemon-socket/socket ]; do sleep 0.01; done) && " + - strings.Join(command, " && ") + - // terminate nix-daemon - " && pkill nix-daemon", - }, - Confinement: fst.ConfinementConfig{ - AppID: bundle.AppID, - Groups: bundle.Groups, - Username: "fortify", - Inner: path.Join("/data/data", bundle.ID), - Outer: pathSet.homeDir, - Sandbox: &fst.SandboxConfig{ - Hostname: formatHostname(bundle.Name) + "-" + action, - UserNS: true, // nix sandbox requires userns - Net: net, - NoNewSession: dropShell, - Filesystem: []*fst.FilesystemConfig{ - {Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true}, - }, - Link: [][2]string{ - {bundle.CurrentSystem, "/run/current-system"}, - {"/run/current-system/sw/bin", "/bin"}, - {"/run/current-system/sw/bin", "/usr/bin"}, - }, - Etc: path.Join(pathSet.cacheDir, "etc"), - AutoEtc: true, - }, - ExtraPerms: []*fst.ExtraPermConfig{ - {Path: dataHome, Execute: true}, - {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, - }, - }, - }, dropShell, beforeFail) -} - -func withCacheDir(action string, command []string, workDir string, bundle *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { - fortifyAppDropShell(&fst.Config{ - ID: bundle.ID, - Command: []string{shell, "-lc", strings.Join(command, " && ")}, - Confinement: fst.ConfinementConfig{ - AppID: bundle.AppID, - Username: "nixos", - Inner: path.Join("/data/data", bundle.ID, "cache"), - Outer: pathSet.cacheDir, // this also ensures cacheDir via fshim - Sandbox: &fst.SandboxConfig{ - Hostname: formatHostname(bundle.Name) + "-" + action, - NoNewSession: dropShell, - Filesystem: []*fst.FilesystemConfig{ - {Src: path.Join(workDir, "nix"), Dst: "/nix", Must: true}, - {Src: workDir, Dst: path.Join(fst.Tmp, "bundle"), Must: true}, - }, - Link: [][2]string{ - {bundle.CurrentSystem, "/run/current-system"}, - {"/run/current-system/sw/bin", "/bin"}, - {"/run/current-system/sw/bin", "/usr/bin"}, - }, - Etc: path.Join(workDir, "etc"), - AutoEtc: true, - }, - ExtraPerms: []*fst.ExtraPermConfig{ - {Path: dataHome, Execute: true}, - {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, - {Path: workDir, Execute: true}, - }, - }, - }, dropShell, beforeFail) -} - -func fortifyAppDropShell(config *fst.Config, dropShell bool, beforeFail func()) { - if dropShell { - config.Command = []string{shell, "-l"} - fortifyApp(config, beforeFail) - beforeFail() - fmsg.Exit(0) - } - fortifyApp(config, beforeFail) -} diff --git a/cmd/fpkg/start.go b/cmd/fpkg/start.go index 372ca70..1dc31e4 100644 --- a/cmd/fpkg/start.go +++ b/cmd/fpkg/start.go @@ -10,8 +10,14 @@ import ( func actionStart(args []string) { set := flag.NewFlagSet("start", flag.ExitOnError) - var dropShell bool + var ( + dropShell bool + dropShellNixGL bool + autoDrivers bool + ) set.BoolVar(&dropShell, "s", false, "Drop to a shell") + set.BoolVar(&dropShellNixGL, "sg", false, "Drop to a shell on nixGL build") + set.BoolVar(&autoDrivers, "autodrivers", false, "Attempt automatic opengl driver detection") // Ignore errors; set is set for ExitOnError. _ = set.Parse(args) @@ -21,14 +27,53 @@ func actionStart(args []string) { if len(args) < 1 { fmsg.Fatal("invalid argument") } + + /* + Parse app metadata. + */ + id := args[0] pathSet := pathSetByApp(id) app := loadBundleInfo(pathSet.metaPath, func() {}) - if app.ID != id { fmsg.Fatalf("app %q claims to have identifier %q", id, app.ID) } + /* + Prepare nixGL. + */ + + if app.GPU && autoDrivers { + withNixDaemon("nix-gl", []string{ + "mkdir -p /nix/.nixGL/auto", + "rm -rf /nix/.nixGL/auto", + "export NIXPKGS_ALLOW_UNFREE=1", + "nix build --impure " + + "--out-link /nix/.nixGL/auto/opengl " + + "--override-input nixpkgs path:/etc/nixpkgs " + + "path:" + app.NixGL, + "nix build --impure " + + "--out-link /nix/.nixGL/auto/vulkan " + + "--override-input nixpkgs path:/etc/nixpkgs " + + "path:" + app.NixGL + "#nixVulkanNvidia", + }, true, func(config *fst.Config) *fst.Config { + config.Confinement.Sandbox.Filesystem = append(config.Confinement.Sandbox.Filesystem, []*fst.FilesystemConfig{ + {Src: "/etc/resolv.conf"}, + {Src: "/sys/block"}, + {Src: "/sys/bus"}, + {Src: "/sys/class"}, + {Src: "/sys/dev"}, + {Src: "/sys/devices"}, + }...) + appendGPUFilesystem(config) + return config + }, app, pathSet, dropShellNixGL, func() {}) + } + + /* + Create app configuration. + */ + command := make([]string, 1, len(args)) if !dropShell { command[0] = app.Launcher @@ -82,35 +127,49 @@ func actionStart(args []string) { }, } + /* + Expose GPU devices. + */ + if app.GPU { config.Confinement.Sandbox.Filesystem = append(config.Confinement.Sandbox.Filesystem, - // flatpak commit 763a686d874dd668f0236f911de00b80766ffe79 - &fst.FilesystemConfig{Src: "/dev/dri", Device: true}, - // mali - &fst.FilesystemConfig{Src: "/dev/mali", Device: true}, - &fst.FilesystemConfig{Src: "/dev/mali0", Device: true}, - &fst.FilesystemConfig{Src: "/dev/umplock", Device: true}, - // nvidia - &fst.FilesystemConfig{Src: "/dev/nvidiactl", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia-modeset", Device: true}, - // nvidia OpenCL/CUDA - &fst.FilesystemConfig{Src: "/dev/nvidia-uvm", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia-uvm-tools", Device: true}, - - // flatpak commit d2dff2875bb3b7e2cd92d8204088d743fd07f3ff - &fst.FilesystemConfig{Src: "/dev/nvidia0", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia1", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia2", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia3", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia4", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia5", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia6", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia7", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia8", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia9", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia10", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia11", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia12", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia13", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia14", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia15", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia16", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia17", Device: true}, - &fst.FilesystemConfig{Src: "/dev/nvidia18", Device: true}, &fst.FilesystemConfig{Src: "/dev/nvidia19", Device: true}, - ) + &fst.FilesystemConfig{Src: path.Join(pathSet.nixPath, ".nixGL"), Dst: path.Join(fst.Tmp, "nixGL")}) + appendGPUFilesystem(config) } + /* + Spawn app. + */ + fortifyApp(config, func() {}) fmsg.Exit(0) } + +func appendGPUFilesystem(config *fst.Config) { + config.Confinement.Sandbox.Filesystem = append(config.Confinement.Sandbox.Filesystem, []*fst.FilesystemConfig{ + // flatpak commit 763a686d874dd668f0236f911de00b80766ffe79 + {Src: "/dev/dri", Device: true}, + // mali + {Src: "/dev/mali", Device: true}, + {Src: "/dev/mali0", Device: true}, + {Src: "/dev/umplock", Device: true}, + // nvidia + {Src: "/dev/nvidiactl", Device: true}, + {Src: "/dev/nvidia-modeset", Device: true}, + // nvidia OpenCL/CUDA + {Src: "/dev/nvidia-uvm", Device: true}, + {Src: "/dev/nvidia-uvm-tools", Device: true}, + + // flatpak commit d2dff2875bb3b7e2cd92d8204088d743fd07f3ff + {Src: "/dev/nvidia0", Device: true}, {Src: "/dev/nvidia1", Device: true}, + {Src: "/dev/nvidia2", Device: true}, {Src: "/dev/nvidia3", Device: true}, + {Src: "/dev/nvidia4", Device: true}, {Src: "/dev/nvidia5", Device: true}, + {Src: "/dev/nvidia6", Device: true}, {Src: "/dev/nvidia7", Device: true}, + {Src: "/dev/nvidia8", Device: true}, {Src: "/dev/nvidia9", Device: true}, + {Src: "/dev/nvidia10", Device: true}, {Src: "/dev/nvidia11", Device: true}, + {Src: "/dev/nvidia12", Device: true}, {Src: "/dev/nvidia13", Device: true}, + {Src: "/dev/nvidia14", Device: true}, {Src: "/dev/nvidia15", Device: true}, + {Src: "/dev/nvidia16", Device: true}, {Src: "/dev/nvidia17", Device: true}, + {Src: "/dev/nvidia18", Device: true}, {Src: "/dev/nvidia19", Device: true}, + }...) +} diff --git a/cmd/fpkg/with.go b/cmd/fpkg/with.go new file mode 100644 index 0000000..e4bb729 --- /dev/null +++ b/cmd/fpkg/with.go @@ -0,0 +1,98 @@ +package main + +import ( + "path" + "strings" + + "git.gensokyo.uk/security/fortify/fst" + "git.gensokyo.uk/security/fortify/internal/fmsg" +) + +func withNixDaemon( + action string, command []string, net bool, updateConfig func(config *fst.Config) *fst.Config, + app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func(), +) { + fortifyAppDropShell(updateConfig(&fst.Config{ + ID: app.ID, + Command: []string{shell, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " + + // start nix-daemon + "nix-daemon --store / & " + + // wait for socket to appear + "(while [ ! -S /nix/var/nix/daemon-socket/socket ]; do sleep 0.01; done) && " + + // create directory so nix stops complaining + "mkdir -p /nix/var/nix/profiles/per-user/root/channels && " + + strings.Join(command, " && ") + + // terminate nix-daemon + " && pkill nix-daemon", + }, + Confinement: fst.ConfinementConfig{ + AppID: app.AppID, + Username: "fortify", + Inner: path.Join("/data/data", app.ID), + Outer: pathSet.homeDir, + Sandbox: &fst.SandboxConfig{ + Hostname: formatHostname(app.Name) + "-" + action, + UserNS: true, // nix sandbox requires userns + Net: net, + NoNewSession: dropShell, + Filesystem: []*fst.FilesystemConfig{ + {Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true}, + }, + Link: [][2]string{ + {app.CurrentSystem, "/run/current-system"}, + {"/run/current-system/sw/bin", "/bin"}, + {"/run/current-system/sw/bin", "/usr/bin"}, + }, + Etc: path.Join(pathSet.cacheDir, "etc"), + AutoEtc: true, + }, + ExtraPerms: []*fst.ExtraPermConfig{ + {Path: dataHome, Execute: true}, + {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, + }, + }, + }), dropShell, beforeFail) +} + +func withCacheDir(action string, command []string, workDir string, app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { + fortifyAppDropShell(&fst.Config{ + ID: app.ID, + Command: []string{shell, "-lc", strings.Join(command, " && ")}, + Confinement: fst.ConfinementConfig{ + AppID: app.AppID, + Username: "nixos", + Inner: path.Join("/data/data", app.ID, "cache"), + Outer: pathSet.cacheDir, // this also ensures cacheDir via fshim + Sandbox: &fst.SandboxConfig{ + Hostname: formatHostname(app.Name) + "-" + action, + NoNewSession: dropShell, + Filesystem: []*fst.FilesystemConfig{ + {Src: path.Join(workDir, "nix"), Dst: "/nix", Must: true}, + {Src: workDir, Dst: path.Join(fst.Tmp, "bundle"), Must: true}, + }, + Link: [][2]string{ + {app.CurrentSystem, "/run/current-system"}, + {"/run/current-system/sw/bin", "/bin"}, + {"/run/current-system/sw/bin", "/usr/bin"}, + }, + Etc: path.Join(workDir, "etc"), + AutoEtc: true, + }, + ExtraPerms: []*fst.ExtraPermConfig{ + {Path: dataHome, Execute: true}, + {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, + {Path: workDir, Execute: true}, + }, + }, + }, dropShell, beforeFail) +} + +func fortifyAppDropShell(config *fst.Config, dropShell bool, beforeFail func()) { + if dropShell { + config.Command = []string{shell, "-l"} + fortifyApp(config, beforeFail) + beforeFail() + fmsg.Exit(0) + } + fortifyApp(config, beforeFail) +}