modules/programs: make-sandboxed: avoid deep-copying all of /share when sandboxing
saves like 1 GiB of closure. but i haven't thoroughly tested this
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
{ lib
|
{ lib
|
||||||
|
, buildPackages
|
||||||
, runCommand
|
, runCommand
|
||||||
, runtimeShell
|
, runtimeShell
|
||||||
, sane-sandboxed
|
, sane-sandboxed
|
||||||
@@ -128,65 +129,80 @@ let
|
|||||||
runHook postFixup
|
runHook postFixup
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# helper used for `wrapperType == "wrappedDerivation"` which copies over the .desktop files
|
# helper used for `wrapperType == "wrappedDerivation"` which ensures that and copied/symlinked share/ files (like .desktop) files
|
||||||
# and ensures that they don't point to the unwrapped versions.
|
# don't point to the unwrapped binaries.
|
||||||
# other important files it preserves:
|
# other important files it preserves:
|
||||||
# - share/applications
|
# - share/applications
|
||||||
# - share/dbus-1 (frequently a source of leaked references!)
|
# - share/dbus-1 (frequently a source of leaked references!)
|
||||||
# - share/icons
|
# - share/icons
|
||||||
# - share/man
|
# - share/man
|
||||||
# - share/mime
|
# - share/mime
|
||||||
# TODO: it'd be nice to just symlink these instead, but then we couldn't leverage `disallowedReferences` like this.
|
fixHardcodedRefs = unsandboxed: sandboxedNonBin: sandboxedBin: sandboxedNonBin.overrideAttrs (prevAttrs: {
|
||||||
copyNonBinaries = pkgName: package: sandboxedBins: runCommand "${pkgName}-sandboxed-non-binary" {
|
postInstall = (prevAttrs.postInstall or "") + ''
|
||||||
disallowedReferences = [ package ];
|
trySubstitute() {
|
||||||
# users can build this one when they get a disallowed references failure
|
_outPath="$1"
|
||||||
passthru.unchecked = (copyNonBinaries pkgName package sandboxedBins).overrideAttrs (_: {
|
_pattern="$2"
|
||||||
disallowedReferences = [];
|
_from=$(printf "$_pattern" "${unsandboxed}")
|
||||||
});
|
_to=$(printf "$_pattern" "${sandboxedBin}")
|
||||||
} ''
|
printf "applying known substitutions to %s" "$_outPath"
|
||||||
trySubstitute() {
|
# substituteInPlace can fail on symlinks, but frequently that's fine because
|
||||||
_outPath="$1"
|
# the referenced file is already safe, so don't error on failure here.
|
||||||
_pattern="$2"
|
substituteInPlace "$_outPath" \
|
||||||
_from=$(printf "$_pattern" "${package}")
|
--replace "$_from" "$_to" \
|
||||||
_to=$(printf "$_pattern" "${sandboxedBins}")
|
|| true
|
||||||
printf "applying known substitutions to %s" "$_outPath"
|
}
|
||||||
# substituteInPlace can fail on symlinks, but frequently that's fine because
|
# fixup a few files i understand well enough
|
||||||
# the referenced file is already safe, so don't error on failure here.
|
for d in $out/share/applications/*.desktop; do
|
||||||
substituteInPlace "$_outPath" \
|
trySubstitute "$d" "Exec=%s/bin/"
|
||||||
--replace "$_from" "$_to" \
|
done
|
||||||
|| true
|
for d in $out/share/dbus-1/services/*.service; do
|
||||||
}
|
trySubstitute "$d" "Exec=%s/bin/"
|
||||||
mkdir "$out"
|
done
|
||||||
if [ -e "${package}/share" ]; then
|
'';
|
||||||
cp -R "${package}/share" "$out/"
|
});
|
||||||
fi
|
|
||||||
# fixup a few files i understand well enough
|
# copy or symlink the non-binary files from the unsandboxed package,
|
||||||
for d in $out/share/applications/*.desktop; do
|
# patch them to use the sandboxed binaries,
|
||||||
trySubstitute "$d" "Exec=%s/bin/"
|
# and add some passthru metadata to enforce no lingering references to the unsandboxed binaries.
|
||||||
done
|
sandboxNonBinaries = method: pkgName: unsandboxed: sandboxedBin: let
|
||||||
for d in $out/share/dbus-1/services/*.service; do
|
unsandboxedNonBinaries = runCommand "${pkgName}-sandboxed-non-binary" {
|
||||||
trySubstitute "$d" "Exec=%s/bin/"
|
passthru.checkSandboxed = (copyNonBinaries pkgName unsandboxed sandboxedBin).overrideAttrs (_: {
|
||||||
done
|
# copy everything, patch just as we would for any method, and then enforce no refs to the unsandboxed pkg.
|
||||||
'';
|
# we can do that only because we're copying instead of symlinking.
|
||||||
|
# TODO: instead of `passthru.checkSandboxed`, just make this a build prereq of the out derivation,
|
||||||
|
# such that it fails the main derivation if it would contain bad refs
|
||||||
|
disallowedReferences = [ unsandboxed ];
|
||||||
|
});
|
||||||
|
} ''
|
||||||
|
mkdir "$out"
|
||||||
|
if [ -e "${unsandboxed}/share" ]; then
|
||||||
|
${method} "${unsandboxed}/share" "$out/"
|
||||||
|
fi
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
in fixHardcodedRefs unsandboxed unsandboxedNonBinaries sandboxedBin;
|
||||||
|
|
||||||
|
copyNonBinaries = sandboxNonBinaries "cp -R";
|
||||||
|
symlinkNonBinaries = sandboxNonBinaries "${buildPackages.xorg.lndir}/bin/lndir";
|
||||||
|
|
||||||
# take the nearly-final sandboxed package, with binaries and and else, and
|
# take the nearly-final sandboxed package, with binaries and and else, and
|
||||||
# populate passthru attributes the caller expects, like `sandboxProfiles` and `checkSandboxed`.
|
# populate passthru attributes the caller expects, like `sandboxProfiles` and `checkSandboxed`.
|
||||||
fixupMetaAndPassthru = pkgName: pkg: sandboxProfiles: extraPassthru: pkg.overrideAttrs (orig: let
|
fixupMetaAndPassthru = pkgName: pkg: sandboxProfiles: extraPassthru: pkg.overrideAttrs (finalAttrs: prevAttrs: let
|
||||||
final = fixupMetaAndPassthru pkgName pkg sandboxProfiles extraPassthru;
|
final = fixupMetaAndPassthru pkgName pkg sandboxProfiles extraPassthru;
|
||||||
in {
|
in {
|
||||||
meta = (orig.meta or {}) // {
|
meta = (prevAttrs.meta or {}) // {
|
||||||
# take precedence over non-sandboxed versions of the same binary.
|
# take precedence over non-sandboxed versions of the same binary.
|
||||||
priority = ((orig.meta or {}).priority or 0) - 1;
|
priority = ((prevAttrs.meta or {}).priority or 0) - 1;
|
||||||
};
|
};
|
||||||
passthru = (pkg.passthru or {}) // extraPassthru // {
|
passthru = (prevAttrs.passthru or {}) // extraPassthru // {
|
||||||
inherit sandboxProfiles;
|
inherit sandboxProfiles;
|
||||||
checkSandboxed = runCommand "${pkgName}-check-sandboxed" {} ''
|
checkSandboxed = runCommand "${pkgName}-check-sandboxed" {} ''
|
||||||
# invoke each binary in a way only the sandbox wrapper will recognize,
|
# invoke each binary in a way only the sandbox wrapper will recognize,
|
||||||
# ensuring that every binary has in fact been wrapped.
|
# ensuring that every binary has in fact been wrapped.
|
||||||
_numExec=0
|
_numExec=0
|
||||||
for b in ${final}/bin/*; do
|
for b in ${finalAttrs.finalPackage}/bin/*; do
|
||||||
echo "checking if $b is sandboxed"
|
echo "checking if $b is sandboxed"
|
||||||
PATH="${final}/bin:${sane-sandboxed}/bin:$PATH" \
|
PATH="${finalAttrs.finalPackage}/bin:${sane-sandboxed}/bin:$PATH" \
|
||||||
SANE_SANDBOX_DISABLE=1 \
|
SANE_SANDBOX_DISABLE=1 \
|
||||||
"$b" --sane-sandbox-replace-cli echo "printing for test" \
|
"$b" --sane-sandbox-replace-cli echo "printing for test" \
|
||||||
| grep "printing for test"
|
| grep "printing for test"
|
||||||
@@ -194,6 +210,10 @@ let
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "successfully tested $_numExec binaries"
|
echo "successfully tested $_numExec binaries"
|
||||||
|
# forward prevAttrs checkSandboxed, to guarantee correctness for the /share directory (`sandboxNonBinaries`).
|
||||||
|
${lib.optionalString (prevAttrs ? passthru && prevAttrs.passthru ? checkSandboxed)
|
||||||
|
''echo "also checked: ${prevAttrs.passthru.checkSandboxed}"''
|
||||||
|
}
|
||||||
test "$_numExec" -ne 0 && touch "$out"
|
test "$_numExec" -ne 0 && touch "$out"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@@ -270,7 +290,7 @@ let
|
|||||||
maybeEmbedProfilesDir
|
maybeEmbedProfilesDir
|
||||||
pkgName
|
pkgName
|
||||||
(symlinkBinaries pkgName package);
|
(symlinkBinaries pkgName package);
|
||||||
nonBinaries = copyNonBinaries pkgName package binaries;
|
nonBinaries = symlinkNonBinaries pkgName package binaries;
|
||||||
in symlinkJoin {
|
in symlinkJoin {
|
||||||
name = "${pkgName}-sandboxed-all";
|
name = "${pkgName}-sandboxed-all";
|
||||||
paths = [ binaries nonBinaries ];
|
paths = [ binaries nonBinaries ];
|
||||||
|
Reference in New Issue
Block a user