From a90b5b53dbacbd42ebdd17446257af32f278c940 Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 12 Feb 2024 12:48:02 +0000 Subject: [PATCH] modules/programs: sandboxing: dereference symlinks and also include those in the sandbox --- modules/programs/default.nix | 51 ++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/modules/programs/default.nix b/modules/programs/default.nix index 65e616be..8ed3ca6e 100644 --- a/modules/programs/default.nix +++ b/modules/programs/default.nix @@ -2,6 +2,7 @@ let saneCfg = config.sane; cfg = config.sane.programs; + path-lib = sane-lib.path; # create a map: # { @@ -40,21 +41,51 @@ let let makeProfile = pkgs.callPackage ./make-sandbox-profile.nix { }; makeSandboxed = pkgs.callPackage ./make-sandboxed.nix { sane-sandboxed = config.sane.sandboxHelper; }; + + # removeStorePaths: [ str ] -> [ str ], but remove store paths, because nix evals aren't allowed to contain any (for purity reasons?) + removeStorePaths = lib.filter (p: !(lib.hasPrefix "/nix/store" p)); + # onlySymlinks: [ str ] -> [ str ], keeping only those strings which represent paths that are symlinks + onlySymlinks = lib.filter + (p: (config.sane.fs."${p}" or { symlink = null; }).symlink != null); + # expandSymlinksOnce: [ str ] -> [ str ], returning all the original paths plus dereferencing any symlinks and adding their targets to this list. + derefSymlinks = paths: builtins.map + (p: config.sane.fs."${p}".symlink.target) + (onlySymlinks paths) + ; + expandSymlinksOnce = paths: lib.unique (paths ++ removeStorePaths (derefSymlinks paths)); + expandSymlinks = lib.converge expandSymlinksOnce; + vpn = lib.findSingle (v: v.default) null null (builtins.attrValues config.sane.vpn); + sandboxProfilesFor = userName: let homeDir = config.sane.users."${userName}".home; uid = config.users.users."${userName}".uid; xdgRuntimeDir = "/run/user/${builtins.toString uid}"; fullHomePaths = lib.optionals (userName != null) ( builtins.map - (p: "${homeDir}/${p}") + (p: path-lib.concat [ homeDir p ]) (builtins.attrNames fs ++ builtins.attrNames persist.byPath ++ sandbox.extraHomePaths) ); fullRuntimePaths = lib.optionals (userName != null) ( builtins.map - (p: "${xdgRuntimeDir}/${p}") + (p: path-lib.concat [ xdgRuntimeDir p ]) sandbox.extraRuntimePaths ); + allowedRootPaths = [ + "/nix/store" + "/bin/sh" + + "/etc" #< especially for /etc/profiles/per-user/$USER/bin + "/run/current-system" #< for basics like `ls`, and all this program's `suggestedPrograms` (/run/current-system/sw/bin) + "/run/wrappers" #< SUID wrappers, in this case so that firejail can be re-entrant + # "/bin/sh" #< to allow `firejail --join=...` (doesn't work) + "/run/systemd/resolve" #< to allow reading /etc/resolv.conf, which ultimately symlinks here + # /run/opengl-driver is a symlink into /nix/store; needed by e.g. mpv + "/run/opengl-driver" + "/run/opengl-driver-32" #< XXX: doesn't exist on aarch64? + "/run/secrets/home" #< TODO: this could be restricted per-app based on the HOME paths they need + "/usr/bin/env" + ] ++ sandbox.extraPaths ++ fullHomePaths ++ fullRuntimePaths; in makeProfile { inherit pkgName; inherit (sandbox) @@ -72,21 +103,7 @@ let vpn.dns else null; - allowedRootPaths = [ - "/nix/store" - "/bin/sh" - - "/etc" #< especially for /etc/profiles/per-user/$USER/bin - "/run/current-system" #< for basics like `ls`, and all this program's `suggestedPrograms` (/run/current-system/sw/bin) - "/run/wrappers" #< SUID wrappers, in this case so that firejail can be re-entrant - # "/bin/sh" #< to allow `firejail --join=...` (doesn't work) - "/run/systemd/resolve" #< to allow reading /etc/resolv.conf, which ultimately symlinks here - # /run/opengl-driver is a symlink into /nix/store; needed by e.g. mpv - "/run/opengl-driver" - "/run/opengl-driver-32" #< XXX: doesn't exist on aarch64? - "/run/secrets/home" #< TODO: this could be restricted per-app based on the HOME paths they need - "/usr/bin/env" - ] ++ sandbox.extraPaths ++ fullHomePaths ++ fullRuntimePaths; + allowedRootPaths = expandSymlinks allowedRootPaths; }; in makeSandboxed {