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

This commit is contained in:
Colin 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
makeSandboxed {
inherit pkgName package;
inherit (sandbox) binMap method extraConfig;
inherit (sandbox) binMap embedProfile extraConfig method;
vpn = if net == "vpn" then vpn else null;
allowedHomePaths = builtins.attrNames fs ++ builtins.attrNames persist.byPath ++ sandbox.extraHomePaths;
allowedRootPaths = [
@ -222,6 +222,17 @@ let
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 {
type = types.attrsOf types.str;
default = {};

View File

@ -4,7 +4,7 @@
, sane-sandboxed
, writeTextFile
}:
{ pkgName, package, method, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], binMap ? {}, extraConfig ? [] }:
{ pkgName, package, method, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], binMap ? {}, extraConfig ? [], embedProfile ? false }:
let
sane-sandboxed' = sane-sandboxed.meta.mainProgram; #< load by bin name to reduce rebuilds
@ -34,6 +34,15 @@ let
++ lib.optionals (vpn != null) vpnItems
++ 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:
# 1. package.overrideAttrs, with `postFixup`.
# 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.
# 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.override {
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 ""}")
# filter to just the unique profiles
_profileArgs=()
_profileArgs=(${maybeEmbedProfilesDir})
for _profile in "''${_profiles[@]}"; do
if [ -n "$_profile" ] && ! [[ " ''${_profileArgs[@]} " =~ " $_profile " ]]; then
_profileArgs+=("--sane-sandbox-profile" "$_profile")
@ -121,11 +130,7 @@ let
test "$_numExec" -ne 0 && touch "$out"
'';
sandboxProfiles = writeTextFile {
name = "${pkgName}-sandbox-profiles";
destination = "/share/sane-sandboxed/profiles/${pkgName}.profile";
text = builtins.concatStringsSep "\n" sandboxFlags;
};
sandboxProfiles = sandboxProfilesPkg;
};
});
in

View File

@ -4,6 +4,8 @@ isDebug="$SANE_SANDBOX_DEBUG"
test -n "$isDebug" && set -x
isDisable="$SANE_SANDBOX_DISABLE"
profileDirs=(@profileDirs@)
cliArgs=()
cliPathArgs=()
autodetect=
@ -31,17 +33,25 @@ loadProfileByPath() {
}
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
_profile="$_profileDir/$1.profile"
debug "try profile at path: '$_profile'"
if [ -f "$_profile" ]; then
loadProfileByPath "$_profile"
break
fi
done
for _profileDir in "${profileDirs[@]}"; do
_profilePath="$_profileDir/$_profile.profile"
debug "try profile at path: '$_profilePath'"
if [ -f "$_profilePath" ]; then
loadProfileByPath "$_profilePath"
break
fi
done
fi
}
# convert e.g. `file:///Local%20Users/foo.mp3` to `file:///Local Users/foo.mp3`
@ -160,6 +170,11 @@ parseArgs() {
tryLoadProfileByName "$1"
shift
;;
(--sane-sandbox-profile-dir)
_dir="$1"
shift
profileDirs+=("$_dir")
;;
(*)
parseArgsExtra+=("$_arg")
;;