2024-01-23 01:23:14 +00:00
{ lib
, runCommand
, runtimeShell
2024-01-23 03:48:09 +00:00
, sane-sandboxed
2024-01-23 08:01:23 +00:00
, writeTextFile
2024-01-23 01:23:14 +00:00
} :
2024-01-27 12:23:25 +00:00
{ pkgName , package , method , vpn ? null , allowedHomePaths ? [ ] , allowedRootPaths ? [ ] , binMap ? { } , extraConfig ? [ ] , embedProfile ? false }:
2024-01-23 01:23:14 +00:00
let
2024-01-23 03:48:09 +00:00
sane-sandboxed' = sane-sandboxed . meta . mainProgram ; #< load by bin name to reduce rebuilds
2024-01-23 01:23:14 +00:00
allowPath = p : [
2024-01-23 08:01:23 +00:00
" - - s a n e - s a n d b o x - p a t h "
p
] ;
allowHomePath = p : [
" - - s a n e - s a n d b o x - h o m e - p a t h "
p
2024-01-23 01:23:14 +00:00
] ;
allowPaths = paths : lib . flatten ( builtins . map allowPath paths ) ;
allowHomePaths = paths : lib . flatten ( builtins . map allowHomePath paths ) ;
vpnItems = [
2024-01-23 08:01:23 +00:00
" - - s a n e - s a n d b o x - n e t "
vpn . bridgeDevice
] ++ lib . flatten ( builtins . map ( addr : [
" - - s a n e - s a n d b o x - d n s "
addr
] ) vpn . dns ) ;
2024-01-23 01:23:14 +00:00
2024-01-23 10:44:13 +00:00
sandboxFlags = [
" - - s a n e - s a n d b o x - m e t h o d " method
] ++ allowPaths allowedRootPaths
2024-01-23 01:23:14 +00:00
++ allowHomePaths allowedHomePaths
2024-01-23 14:30:42 +00:00
++ lib . optionals ( vpn != null ) vpnItems
++ extraConfig ;
2024-01-23 01:23:14 +00:00
2024-01-27 12:23:25 +00:00
sandboxProfilesPkg = writeTextFile {
name = " ${ pkgName } - s a n d b o x - p r o f i l e s " ;
destination = " / s h a r e / s a n e - s a n d b o x e d / p r o f i l e s / ${ pkgName } . p r o f i l e " ;
text = builtins . concatStringsSep " \n " sandboxFlags ;
} ;
sandboxProfileDir = " ${ sandboxProfilesPkg } / s h a r e / s a n e - s a n d b o x e d / p r o f i l e s " ;
maybeEmbedProfilesDir = lib . optionalString embedProfile '' " - - s a n e - s a n d b o x - p r o f i l e - d i r " " ${ sandboxProfileDir } " '' ;
2024-01-23 01:23:14 +00:00
# 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.
#
# no.2 would require special-casing for .desktop files, to ensure they refer to the jailed version.
# no.1 may require extra care for recursive binaries, or symlink-heavy binaries (like busybox)
# but even no.2 has to consider such edge-cases, just less frequently.
# no.1 may bloat rebuild times.
#
2024-01-27 12:23:25 +00:00
# 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.
2024-01-23 01:23:14 +00:00
package' = if package . override . __functionArgs ? runCommand then
package . override {
runCommand = name : env : cmd : runCommand name env ( cmd + lib . optionalString ( name == package . name ) ''
# if the package is a runCommand (common for wrappers), then patch it to call our `postFixup` hook, first
runHook postFixup
'' ) ;
}
else
package
;
packageWrapped = package' . overrideAttrs ( unwrapped : {
postFixup = ( unwrapped . postFixup or " " ) + ''
2024-01-23 08:01:23 +00:00
getProfileFromBinMap ( ) {
2024-01-23 01:23:14 +00:00
case " $ 1 " in
$ { builtins . concatStringsSep " \n " ( lib . mapAttrsToList
( bin : profile : ''
( $ { bin } )
2024-01-23 08:01:23 +00:00
echo " ${ profile } "
2024-01-23 01:23:14 +00:00
; ;
'' )
binMap
) }
( * )
; ;
esac
}
2024-01-23 08:01:23 +00:00
sandboxWrap ( ) {
_name = " $ 1 "
_profileFromBinMap = " $ ( g e t P r o f i l e F r o m B i n M a p $ _ n a m e ) "
_profiles = ( " $ _ p r o f i l e F r o m B i n M a p " " $ _ n a m e " " ${ pkgName } " " ${ unwrapped . pname or " " } " " ${ unwrapped . name or " " } " )
# filter to just the unique profiles
2024-01-27 12:23:25 +00:00
_profileArgs = ( $ { maybeEmbedProfilesDir } )
2024-01-23 08:01:23 +00:00
for _profile in " ' ' ${ _profiles [ @ ] } " ; do
if [ - n " $ _ p r o f i l e " ] && ! [ [ " ' ' ${ _profileArgs [ @ ] } " = ~ " $ _ p r o f i l e " ] ] ; then
_profileArgs + = ( " - - s a n e - s a n d b o x - p r o f i l e " " $ _ p r o f i l e " )
fi
done
mv " $ o u t / b i n / $ _ n a m e " " $ o u t / b i n / . $ _ n a m e - s a n d b o x e d "
cat < < EOF > > " $ o u t / b i n / $ _ n a m e "
2024-01-23 01:23:14 +00:00
#!${runtimeShell}
2024-01-23 03:48:09 +00:00
exec $ { sane-sandboxed' } \
2024-01-23 08:01:23 +00:00
'' ${ _profileArgs [ @ ] } \
" $ o u t / b i n / . $ _ n a m e - s a n d b o x e d " " \$ @ "
2024-01-23 01:23:14 +00:00
EOF
2024-01-23 08:01:23 +00:00
chmod + x " $ o u t / b i n / $ _ n a m e "
2024-01-23 01:23:14 +00:00
}
for _p in $ ( ls " $ o u t / b i n / " ) ; do
2024-01-23 08:01:23 +00:00
sandboxWrap " $ _ p "
2024-01-23 01:23:14 +00:00
done
'' ;
2024-01-23 10:44:13 +00:00
2024-01-23 01:23:14 +00:00
meta = ( unwrapped . meta or { } ) // {
# take precedence over non-sandboxed versions of the same binary.
priority = ( ( unwrapped . meta or { } ) . priority or 0 ) - 1 ;
} ;
2024-01-23 10:44:13 +00:00
2024-01-23 01:23:14 +00:00
passthru = ( unwrapped . passthru or { } ) // {
2024-01-23 08:01:23 +00:00
checkSandboxed = runCommand " ${ pkgName } - c h e c k - s a n d b o x e d " { } ''
2024-01-23 10:44:13 +00:00
# invoke each binary in a way only the sandbox wrapper will recognize,
# ensuring that every binary has in fact been wrapped.
_numExec = 0
for b in $ { packageWrapped } /bin /* ; d o
2024-01-23 11:19:52 +00:00
PATH = " $ P A T H : ${ packageWrapped } / b i n : ${ sane-sandboxed } / b i n " \
SANE_SANDBOX_DISABLE = 1 \
" $ b " - - sane-sandbox-replace-cli echo " p r i n t i n g f o r t e s t " \
| grep " p r i n t i n g f o r t e s t "
2024-01-23 10:44:13 +00:00
_numExec = $ ( ( $ _numExec + 1 ) )
done
echo " s u c c e s s f u l l y t e s t e d $ _ n u m E x e c b i n a r i e s "
test " $ _ n u m E x e c " - ne 0 && touch " $ o u t "
2024-01-23 01:23:14 +00:00
'' ;
2024-01-23 10:44:13 +00:00
2024-01-27 12:23:25 +00:00
sandboxProfiles = sandboxProfilesPkg ;
2024-01-23 01:23:14 +00:00
} ;
} ) ;
in
packageWrapped