modules/programs: make-sandboxed: fix that /share/* was being linked into top-level /; better way to enforce sandboxing of /share entries
This commit is contained in:
parent
8d705af7a0
commit
2fc1fe7510
|
@ -236,7 +236,7 @@ in
|
||||||
|
|
||||||
# creds, but also 200 MB of node modules, etc
|
# creds, but also 200 MB of node modules, etc
|
||||||
discord.sandbox.method = "bwrap";
|
discord.sandbox.method = "bwrap";
|
||||||
discord.sandbox.wrapperType = "wrappedDerivation";
|
discord.sandbox.wrapperType = "inplace"; #< /opt-style packaging
|
||||||
discord.persist.byStore.private = [ ".config/discord" ];
|
discord.persist.byStore.private = [ ".config/discord" ];
|
||||||
|
|
||||||
dtc.sandbox.method = "bwrap";
|
dtc.sandbox.method = "bwrap";
|
||||||
|
|
|
@ -137,19 +137,21 @@ let
|
||||||
# - share/icons
|
# - share/icons
|
||||||
# - share/man
|
# - share/man
|
||||||
# - share/mime
|
# - share/mime
|
||||||
fixHardcodedRefs = unsandboxed: sandboxedNonBin: sandboxedBin: sandboxedNonBin.overrideAttrs (prevAttrs: {
|
fixHardcodedRefs = unsandboxed: sandboxedBin: unsandboxedNonBin: unsandboxedNonBin.overrideAttrs (prevAttrs: {
|
||||||
postInstall = (prevAttrs.postInstall or "") + ''
|
postInstall = (prevAttrs.postInstall or "") + ''
|
||||||
trySubstitute() {
|
trySubstitute() {
|
||||||
_outPath="$1"
|
_outPath="$1"
|
||||||
_pattern="$2"
|
_pattern="$2"
|
||||||
_from=$(printf "$_pattern" "${unsandboxed}")
|
_from=$(printf "$_pattern" "${unsandboxed}")
|
||||||
_to=$(printf "$_pattern" "${sandboxedBin}")
|
_to=$(printf "$_pattern" "${sandboxedBin}")
|
||||||
printf "applying known substitutions to %s" "$_outPath"
|
printf "applying known substitutions to %s\n" "$_outPath"
|
||||||
# substituteInPlace can fail on symlinks, but frequently that's fine because
|
# for closure efficiency, we only want to rewrite stuff which actually needs changing,
|
||||||
# the referenced file is already safe, so don't error on failure here.
|
# and allow unchanged stuff to remain as symlinks.
|
||||||
substituteInPlace "$_outPath" \
|
# `substitute` can't rewrite symlinks, so instead do the substitution to a temp output
|
||||||
--replace "$_from" "$_to" \
|
# with `--replace-fail` and only recreate the output symlink as a file if the substitution succeeds (i.e. if it matched).
|
||||||
|| true
|
if substitute "$_outPath" ./substituteResult --replace-fail "$_from" "$_to"; then
|
||||||
|
mv ./substituteResult "$_outPath"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
# fixup a few files i understand well enough
|
# fixup a few files i understand well enough
|
||||||
for d in $out/share/applications/*.desktop; do
|
for d in $out/share/applications/*.desktop; do
|
||||||
|
@ -159,36 +161,44 @@ let
|
||||||
trySubstitute "$d" "Exec=%s/bin/"
|
trySubstitute "$d" "Exec=%s/bin/"
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
|
passthru = (prevAttrs.passthru or {}) // {
|
||||||
|
# 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.
|
||||||
|
# further, since the sandboxed binaries intentionally reference the unsandboxed binaries,
|
||||||
|
# we have to patch those out as a way to whitelist them.
|
||||||
|
checkSandboxed = let
|
||||||
|
sandboxedNonBin = fixHardcodedRefs unsandboxed "/dev/null" unsandboxedNonBin;
|
||||||
|
in runCommand "${sandboxedNonBin.name}-check-sandboxed"
|
||||||
|
{ disallowedReferences = [ unsandboxed ]; }
|
||||||
|
''
|
||||||
|
# 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.
|
||||||
|
# in such case, this could lead to weird brokenness (e.g. no icons/images), so failing is reasonable.
|
||||||
|
cp -R --dereference "${sandboxedNonBin}" "$out" # IF YOUR BUILD FAILS HERE, TRY SANDBOXING WITH "inplace"
|
||||||
|
''
|
||||||
|
;
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
# copy or symlink the non-binary files from the unsandboxed package,
|
# symlink the non-binary files from the unsandboxed package,
|
||||||
# patch them to use the sandboxed binaries,
|
# patch them to use the sandboxed binaries,
|
||||||
# and add some passthru metadata to enforce no lingering references to the unsandboxed binaries.
|
# and add some passthru metadata to enforce no lingering references to the unsandboxed binaries.
|
||||||
sandboxNonBinaries = method: pkgName: unsandboxed: sandboxedBin: let
|
sandboxNonBinaries = pkgName: unsandboxed: sandboxedBin:
|
||||||
unsandboxedNonBinaries = runCommand "${pkgName}-sandboxed-non-binary" {
|
fixHardcodedRefs unsandboxed sandboxedBin (runCommand "${pkgName}-sandboxed-non-binary" {} ''
|
||||||
passthru.checkSandboxed = (copyNonBinaries pkgName unsandboxed sandboxedBin).overrideAttrs (_: {
|
set -e
|
||||||
# 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"
|
mkdir "$out"
|
||||||
if [ -e "${unsandboxed}/share" ]; then
|
if [ -e "${unsandboxed}/share" ]; then
|
||||||
${method} "${unsandboxed}/share" "$out/"
|
mkdir "$out/share"
|
||||||
|
${buildPackages.xorg.lndir}/bin/lndir "${unsandboxed}/share" "$out/share"
|
||||||
fi
|
fi
|
||||||
runHook postInstall
|
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 (finalAttrs: prevAttrs: let
|
fixupMetaAndPassthru = pkgName: pkg: sandboxProfiles: extraPassthru: pkg.overrideAttrs (finalAttrs: prevAttrs: let
|
||||||
final = fixupMetaAndPassthru pkgName pkg sandboxProfiles extraPassthru;
|
final = fixupMetaAndPassthru pkgName pkg sandboxProfiles extraPassthru;
|
||||||
|
nonBin = (prevAttrs.passthru or {}).sandboxedNonBin or {};
|
||||||
in {
|
in {
|
||||||
meta = (prevAttrs.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.
|
||||||
|
@ -197,6 +207,7 @@ let
|
||||||
passthru = (prevAttrs.passthru or {}) // extraPassthru // {
|
passthru = (prevAttrs.passthru or {}) // extraPassthru // {
|
||||||
inherit sandboxProfiles;
|
inherit sandboxProfiles;
|
||||||
checkSandboxed = runCommand "${pkgName}-check-sandboxed" {} ''
|
checkSandboxed = runCommand "${pkgName}-check-sandboxed" {} ''
|
||||||
|
set -e
|
||||||
# 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
|
||||||
|
@ -210,17 +221,17 @@ let
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "successfully tested $_numExec binaries"
|
echo "successfully tested $_numExec binaries"
|
||||||
|
test "$_numExec" -ne 0
|
||||||
|
mkdir "$out"
|
||||||
# forward prevAttrs checkSandboxed, to guarantee correctness for the /share directory (`sandboxNonBinaries`).
|
# forward prevAttrs checkSandboxed, to guarantee correctness for the /share directory (`sandboxNonBinaries`).
|
||||||
${lib.optionalString (prevAttrs ? passthru && prevAttrs.passthru ? checkSandboxed)
|
ln -s ${nonBin.checkSandboxed or "/dev/null"} "$out/sandboxed-non-binaries"
|
||||||
''echo "also checked: ${prevAttrs.passthru.checkSandboxed}"''
|
|
||||||
}
|
|
||||||
test "$_numExec" -ne 0 && touch "$out"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
make-sandboxed = { pkgName, package, method, wrapperType, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], autodetectCliPaths ? null, binMap ? {}, capabilities ? [], embedProfile ? false, embedSandboxer ? false, extraConfig ? [], whitelistPwd ? false }@args:
|
make-sandboxed = { pkgName, package, method, wrapperType, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], autodetectCliPaths ? null, binMap ? {}, capabilities ? [], embedProfile ? false, embedSandboxer ? false, extraConfig ? [], whitelistPwd ? false }@args:
|
||||||
let
|
let
|
||||||
|
unsandboxed = package;
|
||||||
sane-sandboxed' = if embedSandboxer then
|
sane-sandboxed' = if embedSandboxer then
|
||||||
# optionally hard-code the sandboxer. this forces rebuilds, but allows deep iteration w/o deploys.
|
# optionally hard-code the sandboxer. this forces rebuilds, but allows deep iteration w/o deploys.
|
||||||
lib.getExe sane-sandboxed
|
lib.getExe sane-sandboxed
|
||||||
|
@ -281,20 +292,20 @@ let
|
||||||
sane-sandboxed'
|
sane-sandboxed'
|
||||||
maybeEmbedProfilesDir
|
maybeEmbedProfilesDir
|
||||||
pkgName
|
pkgName
|
||||||
(makeHookable package);
|
(makeHookable unsandboxed);
|
||||||
|
|
||||||
wrappedDerivation = let
|
wrappedDerivation = let
|
||||||
binaries = sandboxBinariesInPlace
|
sandboxedBin = sandboxBinariesInPlace
|
||||||
binMap
|
binMap
|
||||||
sane-sandboxed'
|
sane-sandboxed'
|
||||||
maybeEmbedProfilesDir
|
maybeEmbedProfilesDir
|
||||||
pkgName
|
pkgName
|
||||||
(symlinkBinaries pkgName package);
|
(symlinkBinaries pkgName unsandboxed);
|
||||||
nonBinaries = symlinkNonBinaries pkgName package binaries;
|
sandboxedNonBin = sandboxNonBinaries pkgName unsandboxed sandboxedBin;
|
||||||
in symlinkJoin {
|
in symlinkJoin {
|
||||||
name = "${pkgName}-sandboxed-all";
|
name = "${pkgName}-sandboxed-all";
|
||||||
paths = [ binaries nonBinaries ];
|
paths = [ sandboxedBin sandboxedNonBin ];
|
||||||
passthru = { inherit binaries nonBinaries; };
|
passthru = { inherit sandboxedBin sandboxedNonBin unsandboxed; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
packageWrapped = sandboxedBy."${wrapperType}";
|
packageWrapped = sandboxedBy."${wrapperType}";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user