lib: Improve the sandbox derivation

This commit is contained in:
2022-04-26 17:14:08 +02:00
parent a34bf2d319
commit b63b2e0d9c
5 changed files with 240 additions and 109 deletions

View File

@@ -5,38 +5,38 @@
}:
{
name
, package
, binaryName
, version ? "1.0.0"
, desktopFileArgs ? null
, copyIntoSandbox ? null
name # Name of the sandboxed package, e.g. "package-wrapped"
, launchScriptName # Name of the script that will be placed in $out/bin
, binary # The binary to execute inside the sandbox
, extraArgs ? [] # Extra arguments to add to the argv
, desktopFileAttributes ? {} # Arguments to apply to the desktop file
, preDesktopFilePhase ? "" # Code to run during the install phase before building the desktop file
, enableDesktopFile ? false # Whether to build a desktop file
, copyIntoSandbox ? {} # Source -> destination relative to $out
, unshareUser ? true
, unshareIpc ? true
, unsharePid ? true
, unshareNet ? false
, unshareUts ? true
, unshareCgroup ? true
, dieWithParent ? true
, mountInHome ? []
, chdirTo ? "\"$(pwd)\""
, additionalBlacklist ? []
, additionalMounts ? []
, extraEnv ? {}
, dieWithParent ? true # Should bubblewrap exit when the parents exits
, mountInHome ? [] # Files, directories to mount inside the sandbox
, chdirTo ? "\"$(pwd)\"" # What should be the CWD when entering the sandbox
, additionalBlacklist ? [] # Root directories that should not be mounted
, additionalMounts ? [] # Files, directories outside the home directory to mount inside the sandbox
, extraEnv ? {} # Extra environment variables to set inside the sandbox
, enableAlsa ? true # Mount ALSA files from /etc/
, enableSudo ? true # Mount sudo files from /etc/
, enableShells ? true # Mount shell related files from /etc/
, enableNix ? true # Mount nix related files from /etc/
, enableOsInfo ? true # Mount /etc/{machine-id,os-release}
}:
let
mountHome = mountInHome == [];
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"
@@ -46,30 +46,23 @@ let
"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);
]
++ (lib.optionals enableNix [ "static" "nix" ])
++ (lib.optionals enableShells [ "bashrc" "zshenv" "zshrc" "zinputrc" "zprofile" ])
++ (lib.optionals enableSudo [ "login.defs" "sudoers" "sudoers.d" ])
++ (lib.optionals enableAlsa [ "alsa" "asound.conf" ])
++ (lib.optionals enableOsInfo [ "machine-id" "os-release" ])
# Only add SSL stuff when we have networking enabled
++ (lib.optionals (!unshareNet) [ "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
@@ -88,15 +81,21 @@ let
EOF
ldconfig &> /dev/null
'';
init = run: writeShellScriptBin "${binaryName}-init" ''
sandboxInitScriptName = "${name}-wrapped-init.sh";
sandboxInitScript = writeShellScriptBin sandboxInitScriptName ''
source /etc/profile
${createLdConfCache}
exec ${run} "$@"
exec ${binary} "$@"
'';
extraEnvString = lib.foldl (acc: val: acc + val + "\n") "" (lib.mapAttrsToList (name: value: "--setenv ${name} \"${value}\"") extraEnv);
mountHome = mountInHome == [];
initStr = init "${package}/bin/${binaryName}";
bwrapCmd = { initArgs ? "" }: ''
sandboxScriptName = "${name}-wrapped.sh";
sandboxScript = let
extraEnvString = lib.foldl (acc: val: acc + val + "\n") "" (lib.mapAttrsToList (name: value: "--setenv ${name} \"${value}\"") extraEnv);
stringify = x: "\"${x}\"";
safeAdditionalMounts = map stringify additionalMounts;
safeMountInHome = map stringify mountInHome;
in writeShellScriptBin sandboxScriptName ''
blacklist=(/nix /dev /proc /etc ${lib.optionalString (!mountHome) "/home"} ${builtins.toString additionalBlacklist})
ro_mounts=()
symlinks=()
@@ -112,13 +111,13 @@ let
done
if [[ "${lib.optionalString (!mountHome) "1"}" = "1" ]]; then
for entry in ${builtins.toString mountInHome}; do
for entry in ${builtins.toString safeMountInHome}; 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
for entry in ${builtins.toString safeAdditionalMounts}; do
auto_mounts+=(--bind "$entry" "$entry")
done
fi
@@ -158,27 +157,40 @@ let
"''${symlinks[@]}"
"''${auto_mounts[@]}"
${extraEnvString}
${initStr}/bin/${name}-init ${initArgs}
${sandboxInitScript}/bin/${sandboxInitScriptName}
)
exec "''${cmd[@]}"
exec "''${cmd[@]}" $@
'';
bin = writeShellScriptBin name (bwrapCmd { initArgs = ''"$@"''; });
desktopItem = makeDesktopItem (desktopFileArgs // {
exec = "${bin}/bin/${name}";
});
copyIntoSandboxString = lib.concatStrings (map (x: "cp -Lr ${package}/${x} $out/${x}\n") copyIntoSandbox);
in stdenv.mkDerivation {
pname = "${name}-sandboxed";
version = version;
pname = name;
version = "1.0.0";
unpackPhase = ":";
dontBuild = true;
installPhase = ''
installPhase = let
desktopFilePhase = let
desktopFile = makeDesktopItem (desktopFileAttributes // {
exec = "${sandboxScript}/bin/${sandboxScriptName}";
});
in ''
cp -r ${desktopFile}/share/applications $out/share
'';
copyIntoSandboxPhase = let
attrs = builtins.attrNames copyIntoSandbox;
cps = map (x: "cp -Lr ${x} $out/${copyIntoSandbox."${x}"}\n") attrs;
in lib.concatStrings cps;
in ''
mkdir -p $out/bin
ln -s ${bin}/bin/${name} $out/bin/${name}
'' + lib.strings.optionalString (!(builtins.isNull desktopFileArgs)) ''
mkdir -p $out/share/
cp -r ${desktopItem}/share/applications $out/share
'' + lib.strings.optionalString (!(builtins.isNull copyIntoSandbox)) copyIntoSandboxString;
ln -s ${sandboxScript}/bin/${sandboxScriptName} $out/bin/${launchScriptName}
# Generate the desktop file, if enabled
${lib.optionalString enableDesktopFile "mkdir -p $out/share"}
${lib.optionalString enableDesktopFile preDesktopFilePhase}
${lib.optionalString enableDesktopFile desktopFilePhase}
# Copy extra files if they are specified
${lib.optionalString (copyIntoSandbox != {}) copyIntoSandboxPhase}
'';
}