{ stdenv, lib , runCommandLocal, writeShellScriptBin, makeDesktopItem , bubblewrap, coreutils, glibc, pkgsi686Linux }: runScript: { name , version ? "1.0.0" , desktopFileArgs ? {} , pkg ? null , copyIntoSandbox ? [] , unshareUser ? true , unshareIpc ? true , unsharePid ? true , unshareNet ? false , unshareUts ? true , unshareCgroup ? true , dieWithParent ? true , mountInHome ? [] , chdirTo ? "\"$(pwd)\"" , additionalBlacklist ? [] , additionalMounts ? [] , extraEnv ? {} }: let etcBindFlags = let files = [ # NixOS Compatibility "static" "nix" # mainly for nixUnstable users, but also for access to nix/netrc # Shells "bashrc" "zshenv" "zshrc" "zinputrc" "zprofile" # Users, Groups, NSS "passwd" "group" "shadow" "hosts" "resolv.conf" "nsswitch.conf" # User profiles "profiles" # Sudo & Su "login.defs" "sudoers" "sudoers.d" # Time "localtime" "zoneinfo" # Other Core Stuff "machine-id" "os-release" # PAM "pam.d" # Fonts "fonts" # ALSA "alsa" "asound.conf" # SSL "ssl/certs" "pki" ]; in builtins.concatStringsSep "\n " (map (file: "--ro-bind-try /etc/${file} /etc/${file}") files); # Create this on the fly instead of linking from /nix # The container might have to modify it and re-run ldconfig if there are # issues running some binary with LD_LIBRARY_PATH createLdConfCache = '' cat > /etc/ld.so.conf < /dev/null ''; init = run: writeShellScriptBin "${name}-init" '' source /etc/profile ${createLdConfCache} exec ${run} "$@" ''; extraEnvString = lib.foldl (acc: val: acc + val + "\n") "" (lib.mapAttrsToList (name: value: "--setenv ${name} \"${value}\"") extraEnv); mountHome = mountInHome == []; bwrapCmd = { initArgs ? "" }: '' blacklist=(/nix /dev /proc /etc ${lib.optionalString (!mountHome) "/home"} ${builtins.toString additionalBlacklist}) ro_mounts=() symlinks=() declare -a auto_mounts # loop through all directories in the root for dir in /*; do # if it is a directory and it is not in the blacklist if [[ -d "$dir" ]] && [[ ! "''${blacklist[@]}" =~ "$dir" ]]; then # add it to the mount list auto_mounts+=(--bind "$dir" "$dir") fi done if [[ "${lib.optionalString (!mountHome) "1"}" = "1" ]]; then for entry in ${builtins.toString mountInHome}; do auto_mounts+=(--bind "/home/$USER/$entry" "/home/$USER/$entry") done fi if [[ ! -z "${builtins.toString additionalMounts}" ]]; then for entry in ${builtins.toString additionalMounts}; do auto_mounts+=(--bind "$entry" "$entry") done fi cmd=( ${bubblewrap}/bin/bwrap --dev-bind /dev /dev --proc /proc --chdir ${chdirTo} ${lib.optionalString unshareUser "--unshare-user"} ${lib.optionalString unshareIpc "--unshare-ipc"} ${lib.optionalString unsharePid "--unshare-pid"} ${lib.optionalString unshareNet "--unshare-net"} ${lib.optionalString unshareUts "--unshare-uts"} ${lib.optionalString unshareCgroup "--unshare-cgroup"} ${lib.optionalString dieWithParent "--die-with-parent"} --ro-bind /nix /nix # Our glibc will look for the cache in its own path in `/nix/store`. # As such, we need a cache to exist there, because pressure-vessel # depends on the existence of an ld cache. However, adding one # globally proved to be a bad idea (see #100655), the solution we # settled on being mounting one via bwrap. # Also, the cache needs to go to both 32 and 64 bit glibcs, for games # of both architectures to work. --tmpfs ${glibc}/etc \ --symlink /etc/ld.so.conf ${glibc}/etc/ld.so.conf \ --symlink /etc/ld.so.cache ${glibc}/etc/ld.so.cache \ --ro-bind ${glibc}/etc/rpc ${glibc}/etc/rpc \ --remount-ro ${glibc}/etc \ --tmpfs ${pkgsi686Linux.glibc}/etc \ --symlink /etc/ld.so.conf ${pkgsi686Linux.glibc}/etc/ld.so.conf \ --symlink /etc/ld.so.cache ${pkgsi686Linux.glibc}/etc/ld.so.cache \ --ro-bind ${pkgsi686Linux.glibc}/etc/rpc ${pkgsi686Linux.glibc}/etc/rpc \ --remount-ro ${pkgsi686Linux.glibc}/etc \ ${etcBindFlags} "''${ro_mounts[@]}" "''${symlinks[@]}" "''${auto_mounts[@]}" ${extraEnvString} ${init runScript}/bin/${name}-init ${initArgs} ) exec "''${cmd[@]}" ''; bin = writeShellScriptBin name (bwrapCmd { initArgs = ''"$@"''; }); desktopItem = makeDesktopItem (desktopFileArgs // { exec = "${bin}/bin/${name}"; }); in stdenv.mkDerivation { pname = "${name}-sandboxed"; version = version; unpackPhase = ":"; dontBuild = true; installPhase = '' mkdir -p $out/bin ln -s ${bin}/bin/${name} $out/bin/${name} mkdir -p $out/share/ cp -r ${desktopItem}/share/applications $out/share '' + (lib.concatStrings (map (x: "cp -Lr ${pkg}/${x} $out/${x}\n") copyIntoSandbox)); }