modules/programs/sandbox: add an "embedProfile" option to source sandbox settings from the package instead of the system

This commit is contained in:
2024-01-27 12:23:25 +00:00
parent 79ee47bada
commit a6b824d3c4
3 changed files with 50 additions and 19 deletions

View File

@@ -43,7 +43,7 @@ let
in in
makeSandboxed { makeSandboxed {
inherit pkgName package; inherit pkgName package;
inherit (sandbox) binMap method extraConfig; inherit (sandbox) binMap embedProfile extraConfig method;
vpn = if net == "vpn" then vpn else null; vpn = if net == "vpn" then vpn else null;
allowedHomePaths = builtins.attrNames fs ++ builtins.attrNames persist.byPath ++ sandbox.extraHomePaths; allowedHomePaths = builtins.attrNames fs ++ builtins.attrNames persist.byPath ++ sandbox.extraHomePaths;
allowedRootPaths = [ allowedRootPaths = [
@@ -222,6 +222,17 @@ let
how/whether to sandbox all binaries in the package. how/whether to sandbox all binaries in the package.
''; '';
}; };
sandbox.embedProfile = mkOption {
type = types.bool;
default = false;
description = ''
whether to embed the sandbox settings (path access, etc) into the wrapped binary that lives in /nix/store (true),
or to encode only a profile name in the wrapper, and use it to query the settings at runtime (false).
embedded profile means you have to rebuild the wrapper any time you adjust the sandboxing flags,
but it also means you can run the program without installing it: helpful for iteration.
'';
};
sandbox.binMap = mkOption { sandbox.binMap = mkOption {
type = types.attrsOf types.str; type = types.attrsOf types.str;
default = {}; default = {};

View File

@@ -4,7 +4,7 @@
, sane-sandboxed , sane-sandboxed
, writeTextFile , writeTextFile
}: }:
{ pkgName, package, method, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], binMap ? {}, extraConfig ? [] }: { pkgName, package, method, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], binMap ? {}, extraConfig ? [], embedProfile ? false }:
let let
sane-sandboxed' = sane-sandboxed.meta.mainProgram; #< load by bin name to reduce rebuilds sane-sandboxed' = sane-sandboxed.meta.mainProgram; #< load by bin name to reduce rebuilds
@@ -34,6 +34,15 @@ let
++ lib.optionals (vpn != null) vpnItems ++ lib.optionals (vpn != null) vpnItems
++ extraConfig; ++ extraConfig;
sandboxProfilesPkg = writeTextFile {
name = "${pkgName}-sandbox-profiles";
destination = "/share/sane-sandboxed/profiles/${pkgName}.profile";
text = builtins.concatStringsSep "\n" sandboxFlags;
};
sandboxProfileDir = "${sandboxProfilesPkg}/share/sane-sandboxed/profiles";
maybeEmbedProfilesDir = lib.optionalString embedProfile ''"--sane-sandbox-profile-dir" "${sandboxProfileDir}"'';
# two ways i could wrap a package in a sandbox: # two ways i could wrap a package in a sandbox:
# 1. package.overrideAttrs, with `postFixup`. # 1. package.overrideAttrs, with `postFixup`.
# 2. pkgs.symlinkJoin, or pkgs.runCommand, creating an entirely new package which calls into the inner binaries. # 2. pkgs.symlinkJoin, or pkgs.runCommand, creating an entirely new package which calls into the inner binaries.
@@ -43,7 +52,7 @@ let
# but even no.2 has to consider such edge-cases, just less frequently. # but even no.2 has to consider such edge-cases, just less frequently.
# no.1 may bloat rebuild times. # no.1 may bloat rebuild times.
# #
# ultimately, no.1 is probably more reliable, but i expect i'll factor out a switch to allow either approach -- particularly when debugging package buld failures. # ultimately, no.1 is probably more reliable, but i expect i'll factor out a switch to allow either approach -- particularly when debugging package build failures.
package' = if package.override.__functionArgs ? runCommand then package' = if package.override.__functionArgs ? runCommand then
package.override { package.override {
runCommand = name: env: cmd: runCommand name env (cmd + lib.optionalString (name == package.name) '' runCommand = name: env: cmd: runCommand name env (cmd + lib.optionalString (name == package.name) ''
@@ -77,7 +86,7 @@ let
_profiles=("$_profileFromBinMap" "$_name" "${pkgName}" "${unwrapped.pname or ""}" "${unwrapped.name or ""}") _profiles=("$_profileFromBinMap" "$_name" "${pkgName}" "${unwrapped.pname or ""}" "${unwrapped.name or ""}")
# filter to just the unique profiles # filter to just the unique profiles
_profileArgs=() _profileArgs=(${maybeEmbedProfilesDir})
for _profile in "''${_profiles[@]}"; do for _profile in "''${_profiles[@]}"; do
if [ -n "$_profile" ] && ! [[ " ''${_profileArgs[@]} " =~ " $_profile " ]]; then if [ -n "$_profile" ] && ! [[ " ''${_profileArgs[@]} " =~ " $_profile " ]]; then
_profileArgs+=("--sane-sandbox-profile" "$_profile") _profileArgs+=("--sane-sandbox-profile" "$_profile")
@@ -121,11 +130,7 @@ let
test "$_numExec" -ne 0 && touch "$out" test "$_numExec" -ne 0 && touch "$out"
''; '';
sandboxProfiles = writeTextFile { sandboxProfiles = sandboxProfilesPkg;
name = "${pkgName}-sandbox-profiles";
destination = "/share/sane-sandboxed/profiles/${pkgName}.profile";
text = builtins.concatStringsSep "\n" sandboxFlags;
};
}; };
}); });
in in

View File

@@ -4,6 +4,8 @@ isDebug="$SANE_SANDBOX_DEBUG"
test -n "$isDebug" && set -x test -n "$isDebug" && set -x
isDisable="$SANE_SANDBOX_DISABLE" isDisable="$SANE_SANDBOX_DISABLE"
profileDirs=(@profileDirs@)
cliArgs=() cliArgs=()
cliPathArgs=() cliPathArgs=()
autodetect= autodetect=
@@ -31,17 +33,25 @@ loadProfileByPath() {
} }
tryLoadProfileByName() { tryLoadProfileByName() {
profilesNamed+=("$1") _profile="$1"
if [ "${_profile:0:1}" = "/" ]; then
# absolute path to profile.
# consider it an error if it doesn't exist.
# in general, prefer to use `--sane-sandbox-profile-dir` and specify the profile by name.
# doing so maximizes compatibility with anything else that uses the name, like firejail.
loadProfileByPath "$_profile"
else
profilesNamed+=("$_profile")
_profileDirs=(@profileDirs@) for _profileDir in "${profileDirs[@]}"; do
for _profileDir in "${_profileDirs[@]}"; do _profilePath="$_profileDir/$_profile.profile"
_profile="$_profileDir/$1.profile" debug "try profile at path: '$_profilePath'"
debug "try profile at path: '$_profile'" if [ -f "$_profilePath" ]; then
if [ -f "$_profile" ]; then loadProfileByPath "$_profilePath"
loadProfileByPath "$_profile" break
break fi
fi done
done fi
} }
# convert e.g. `file:///Local%20Users/foo.mp3` to `file:///Local Users/foo.mp3` # convert e.g. `file:///Local%20Users/foo.mp3` to `file:///Local Users/foo.mp3`
@@ -160,6 +170,11 @@ parseArgs() {
tryLoadProfileByName "$1" tryLoadProfileByName "$1"
shift shift
;; ;;
(--sane-sandbox-profile-dir)
_dir="$1"
shift
profileDirs+=("$_dir")
;;
(*) (*)
parseArgsExtra+=("$_arg") parseArgsExtra+=("$_arg")
;; ;;