make-sandboxed: preserve outputs of multiple-output packages

especially, this fixes the dconf service, since we keep '/libexec'
This commit is contained in:
2024-08-21 03:28:02 +00:00
parent 9c9b237e69
commit ae0d6cb8e8

View File

@@ -5,6 +5,7 @@
file, file,
gnugrep, gnugrep,
gnused, gnused,
linkFarm,
makeWrapper, makeWrapper,
runCommandLocal, runCommandLocal,
runtimeShell, runtimeShell,
@@ -55,9 +56,7 @@ let
env = (unwrapped.env or {}) // { env = (unwrapped.env or {}) // {
SANEBOX_DISABLE = 1; SANEBOX_DISABLE = 1;
}; };
# TODO: handle multi-output packages; until then, squash lib into the main output, particularly for `libexec`. outputs = unwrapped.outputs or [ "out" ];
# (this line here only affects `inplace` style wrapping)
outputs = lib.remove "lib" (unwrapped.outputs or [ "out" ]);
nativeBuildInputs = [ nativeBuildInputs = [
# the ordering here is specific: inject our deps BEFORE the unwrapped program's # the ordering here is specific: inject our deps BEFORE the unwrapped program's
# so that the unwrapped's take precendence and we limit interference (e.g. makeWrapper impl) # so that the unwrapped's take precendence and we limit interference (e.g. makeWrapper impl)
@@ -197,59 +196,77 @@ let
; ;
# helper used for `wrapperType == "wrappedDerivation"` which simply symlinks all a package's binaries into a new derivation # helper used for `wrapperType == "wrappedDerivation"` which simply symlinks all a package's binaries into a new derivation
symlinkDirs = suffix: symlinkRoots: pkgName: package: (runCommandLocal "${pkgName}-${suffix}" { symlinkDirs = suffix: symlinkRoots: pkgName: package: let
# remove altogether some problem outputs which i don't have a use for in the deployed system
outputs = lib.remove "dev" (package.outputs or [ "out" ]);
in (runCommandLocal "${pkgName}-${suffix}" {
env.symlinkRoots = lib.concatStringsSep " " symlinkRoots; env.symlinkRoots = lib.concatStringsSep " " symlinkRoots;
nativeBuildInputs = [ gnused ]; nativeBuildInputs = [ gnused ];
inherit outputs;
propagatedBuildOutputs = [ ]; #< disable propagation, since we disabled the `dev` output via which things are ordinarily propagated
} '' } ''
set -e set -e
symlinkPath() { symlinkPath() {
if [ -e "$out/$1" ]; then local inbase="$1"
local outbase="$2"
local path="$3"
if [ -e "$outbase/$path" ]; then
: # already linked. may happen when e.g. the package has bin/foo, and sbin -> bin. : # already linked. may happen when e.g. the package has bin/foo, and sbin -> bin.
elif [ -L "${package}/$1" ]; then elif [ -L "$inbase/$path" ]; then
local target=$(readlink "${package}/$1") local target=$(readlink "$inbase/$path")
if [[ "$target" =~ ^${package}/ ]]; then if [[ "$target" =~ ^$inbase/ ]]; then
# absolute link back into the same package # absolute link back into the same package
echo "handling $1: descending into absolute symlink to same package: $target" echo "handling $path: descending into absolute symlink to same package: $target"
target=$(echo "$target" | sed 's:${package}/::') target=$(echo "$target" | sed 's:${package}/::')
ln -s "$out/$target" "$out/$1" ln -s "$outbase/$target" "$outbase/$path"
# create/link the backing path # create/link the backing path
# N.B.: if some leading component of the backing path is also a symlink... this might not work as expected. # N.B.: if some leading component of the backing path is also a symlink... this might not work as expected.
local parent=$(dirname "$out/$target") local parent=$(dirname "$out/$target")
mkdir -p "$parent" mkdir -p "$parent"
symlinkPath "$target" symlinkPath "$inbase" "$outbase" "$target"
elif [[ "$target" =~ ^/nix/store/ ]]; then elif [[ "$target" =~ ^/nix/store/ ]]; then
# absolute link to another package # absolute link to another package
echo "handling $1: symlinking absolute store path: $target" echo "handling $path: symlinking absolute store path: $target"
ln -s "$target" "$out/$1" ln -s "$target" "$outbase/$path"
else else
# relative link # relative link
echo "handling $1: descending into relative symlink: $target" echo "handling $path: descending into relative symlink: $target"
ln -s "$target" "$out/$1" ln -s "$target" "$outbase/$path"
local parent=$(dirname "$1") local parent=$(dirname "$path")
local derefParent=$(dirname "$out/$parent/$target") local derefParent=$(dirname "$outbase/$parent/$target")
$(set -x && mkdir -p "$derefParent") $(set -x && mkdir -p "$derefParent")
symlinkPath "$parent/$target" symlinkPath "$inbase" "$outbase" "$parent/$target"
fi fi
elif [ -d "${package}/$1" ]; then elif [ -d "$inbase/$path" ]; then
echo "handling $1: descending into directory" echo "handling $path: descending into directory"
mkdir -p "$out/$1" mkdir -p "$outbase/$path"
items=($(ls -a "${package}/$1")) items=($(ls -a "$inbase/$path"))
for item in "''${items[@]}"; do for item in "''${items[@]}"; do
if [ "$item" != . ] && [ "$item" != .. ] && [[ "$item" != .* ]]; then if [ "$item" != . ] && [ "$item" != .. ] && [[ "$item" != .* ]]; then
symlinkPath "$1/$item" symlinkPath "$inbase" "$outbase" "$path/$item"
fi fi
done done
elif [ -e "${package}/$1" ]; then elif [ -e "$inbase/$path" ]; then
echo "handling $1: symlinking ordinary file" echo "handling $path: symlinking ordinary file"
ln -s "${package}/$1" "$out/$1" ln -s "$inbase/$path" "$outbase/$path"
fi fi
} }
mkdir -p "$out"
_symlinkRoots=($symlinkRoots) crawlOutput() {
for root in "''${_symlinkRoots[@]}"; do local inbase="$1"
echo "crawling top-level directory for symlinking: $root" local outbase="$2"
symlinkPath "$root" mkdir -p "$outbase"
done _symlinkRoots=($symlinkRoots)
for root in "''${_symlinkRoots[@]}"; do
echo "crawling top-level directory for symlinking: $inbase/$root -> $outbase"
symlinkPath "$inbase" "$outbase" "$root"
done
}
${lib.concatMapStringsSep "\n" (o:
"crawlOutput ${package."${o}"} $" + o
) outputs}
# allow downstream wrapping to hook this (and thereby actually wrap binaries, etc) # allow downstream wrapping to hook this (and thereby actually wrap binaries, etc)
runHook postInstall runHook postInstall
runHook postFixup runHook postFixup
@@ -288,55 +305,67 @@ let
# remove any files which exist in sandoxedBin (makes it possible to sandbox /opt-style packages) # remove any files which exist in sandoxedBin (makes it possible to sandbox /opt-style packages)
removeUnwanted() { removeUnwanted() {
local file_=$(basename "$1") local outbase="$1"
if [ -f "$out/$1" ] || [ -L "$out/$1" ]; then local path="$2"
if [ -e "${sandboxedBin}/$1" ]; then local file_=$(basename "$path")
rm "$out/$1" if [ -f "$outbase/$path" ] || [ -L "$outbase/$path" ]; then
fi for sboxBase in ${lib.concatStringsSep " " sandboxedBin.all}; do
elif [ -d "$out/$1" ]; then if [ -e "$sboxBase/$path" ]; then
local files=($(ls -a "$out/$1")) rm "$outbase/$path"
fi
done
elif [ -d "$outbase/$path" ]; then
local files=($(ls -a "$outbase/$path"))
for item in "''${files[@]}"; do for item in "''${files[@]}"; do
if [ "$item" != . ] && [ "$item" != .. ]; then if [ "$item" != . ] && [ "$item" != .. ]; then
removeUnwanted "$1/$item" removeUnwanted "$outbase" "$path/$item"
fi fi
done done
fi fi
} }
removeUnwanted ""
# fixup a few files i understand well enough for output in $outputs; do
for d in \ local outdir=''${!output}
$out/etc/xdg/autostart/*.desktop \ removeUnwanted "$outdir" ""
$out/share/applications/*.desktop \
$out/share/dbus-1/{services,system-services}/*.service \ # fixup a few files i understand well enough
$out/{etc,lib,share}/systemd/{system,user}/*.service \ for d in \
; do $outdir/etc/xdg/autostart/*.desktop \
# Exec: dbus and desktop files $outdir/share/applications/*.desktop \
# ExecStart,ExecReload: systemd service files $outdir/share/dbus-1/{services,system-services}/*.service \
for key in Exec ExecStart ExecReload; do $outdir/{etc,lib,share}/systemd/{system,user}/*.service \
for binLoc in bin libexec sbin; do ; do
trySubstitute "$d" "$key=%s/$binLoc/" # Exec: dbus and desktop files
# ExecStart,ExecReload: systemd service files
for key in Exec ExecStart ExecReload; do
for binLoc in bin libexec sbin; do
trySubstitute "$d" "$key=%s/$binLoc/"
done
done done
done done
done done
''; '';
passthru = (prevAttrs.passthru or {}) // { passthru = let
# check that sandboxedNonBin references only sandboxed binaries, and never the original unsandboxed binaries. # check that sandboxedNonBin references only sandboxed binaries, and never the original unsandboxed binaries.
# do this by dereferencing all sandboxedNonBin symlinks, and making `unsandboxed` a disallowedReference. # do this by dereferencing all sandboxedNonBin symlinks, and making `unsandboxed` a disallowedReference.
checkSandboxed = let sandboxedNonBin = fixHardcodedRefs unsandboxed sandboxedBin unsandboxedNonBin;
sandboxedNonBin = fixHardcodedRefs unsandboxed sandboxedBin unsandboxedNonBin; outputs = sandboxedNonBin.outputs or [ "out" ];
in runCommandLocal "${sandboxedNonBin.name}-check-sandboxed" checkSandboxed = runCommandLocal "${sandboxedNonBin.name}-check-sandboxed"
{ disallowedReferences = [ unsandboxed ]; } {
inherit outputs;
disallowedReferences = [ unsandboxed ];
passthru.flat = linkFarm "${sandboxedNonBin.name}-check-sandboxed-flat" (builtins.map (o: { name = o; path = checkSandboxed."${o}"; }) outputs);
}
# dereference every symlink, ensuring that whatever data is behind it does not reference non-sandboxed binaries. # dereference every symlink, ensuring that whatever data is behind it does not reference non-sandboxed binaries.
# the dereference *can* fail, in case it's a relative symlink that refers to a part of the non-binaries we don't patch. # the dereference *can* fail, in case it's a relative symlink that refers to a part of the non-binaries we don't patch.
# in such case, this could lead to weird brokenness (e.g. no icons/images), so failing is reasonable. # in such case, this could lead to weird brokenness (e.g. no icons/images), so failing is reasonable.
# N.B.: this `checkSandboxed` protects against accidentally referencing unsandboxed binaries from data files (.deskop, .service, etc). # N.B.: this `checkSandboxed` protects against accidentally referencing unsandboxed binaries from data files (.deskop, .service, etc).
# there's an *additional* `checkSandboxed` further below which invokes every executable in the final package to make sure the binaries are truly sandboxed. # there's an *additional* `checkSandboxed` further below which invokes every executable in the final package to make sure the binaries are truly sandboxed.
'' (lib.concatMapStringsSep "\n" (o:
cp -R --dereference "${sandboxedNonBin}" "$out" # IF YOUR BUILD FAILS HERE, TRY SANDBOXING WITH "inplace" "cp -R --dereference ${sandboxedNonBin."${o}"} $" + o
'' ) sandboxedNonBin.outputs or [ "out" ])
; ;
}; in (prevAttrs.passthru or {}) // { inherit checkSandboxed; };
}); });
# symlink the non-binary files from the unsandboxed package, # symlink the non-binary files from the unsandboxed package,
@@ -414,7 +443,7 @@ let
test "$_numExec" -ne 0 test "$_numExec" -ne 0
mkdir "$out" mkdir "$out"
# forward prevAttrs checkSandboxed, to guarantee correctness for the /share directory (`sandboxNonBinaries`). # forward prevAttrs checkSandboxed, to guarantee correctness for the /share directory (`sandboxNonBinaries`).
ln -s ${nonBin.checkSandboxed or "/dev/null"} "$out/sandboxed-non-binaries" ln -s ${nonBin.checkSandboxed.flat or "/dev/null"} "$out/sandboxed-non-binaries"
''; '';
}; };
}); });
@@ -450,13 +479,25 @@ let
pkgName pkgName
(symlinkBinaries pkgName unsandboxed); (symlinkBinaries pkgName unsandboxed);
sandboxedNonBin = sandboxNonBinaries pkgName unsandboxed sandboxedBin; sandboxedNonBin = sandboxNonBinaries pkgName unsandboxed sandboxedBin;
in symlinkJoin { outputs = lib.unique (
name = "${pkgName}-sandboxed-all"; (sandboxedBin.outputs or [ "out" ])
paths = [ sandboxedBin sandboxedNonBin ]; ++ (sandboxedNonBin.outputs or [ "out" ])
);
in runCommandLocal "${pkgName}-sandboxed-all" {
inherit outputs;
nativeBuildInputs = [ xorg.lndir ];
passthru = { inherit sandboxedBin sandboxedNonBin unsandboxed; }; passthru = { inherit sandboxedBin sandboxedNonBin unsandboxed; };
# specifically, for priority # specifically, for priority
meta = extractMeta sandboxedBin; meta = extractMeta sandboxedBin;
}; } ''
${lib.concatMapStringsSep "\n" (o:
"mkdir -p $" + o + "\n"
+ (lib.optionalString ((sandboxedBin."${o}" or null) != null) ("lndir -silent ${sandboxedBin.${o}} $" + o + "\n"))
+ (lib.optionalString ((sandboxedNonBin."${o}" or null) != null) ("lndir -silent ${sandboxedNonBin.${o}} $" + o + "\n"))
) outputs}
runHook postInstall
runHook postFixup
'';
}; };
packageWrapped = sandboxedBy."${wrapperType}"; packageWrapped = sandboxedBy."${wrapperType}";
in in