sanebox: purge

This commit is contained in:
2024-10-29 05:59:01 +00:00
parent 51204fc494
commit 864e75afce
15 changed files with 35 additions and 1413 deletions

View File

@@ -625,7 +625,7 @@ in
gnome-calendar.sandbox.whitelistDbus = [ "user" ];
# gnome-disks
# XXX(2024-09-02): fails to show any disks even when run as `SANEBOX_DISABLE=1 sudo -E gnome-disks`.
# XXX(2024-09-02): fails to show any disks even when run as `BUNPEN_DISABLE=1 sudo -E gnome-disks`.
gnome-disk-utility.buildCost = 1;
gnome-disk-utility.sandbox.whitelistDbus = [ "system" ];
gnome-disk-utility.sandbox.whitelistWayland = true;

View File

@@ -157,7 +157,6 @@
./sane-secrets-unlock.nix
./sane-sysload.nix
./sane-theme.nix
./sanebox.nix
./satellite.nix
./schlock.nix
./seatd.nix

View File

@@ -22,7 +22,6 @@
mainProgram = "mmcli";
};
});
sandbox.method = "bwrap"; #< TODO: get it working with bunpen
sandbox.whitelistDbus = [
"system"

View File

@@ -74,7 +74,6 @@ in
"sane-scripts.clone".sandbox.method = null; #< TODO: sandbox
"sane-scripts.dev-cargo-loop".sandbox = {
method = "bwrap";
net = "clearnet";
whitelistPwd = true;
extraPaths = [
@@ -106,7 +105,6 @@ in
# because `mount` is a cap_sys_admin syscall, there's no great way to mount stuff dynamically like this.
# instead, we put ourselves in a mount namespace, do the mount, and drop into a shell or run a command.
# this actually has an OK side effect, that the mount isn't shared, and so we avoid contention/interleaving that would cause the ending `umount` to fail.
method = "bwrap";
# cap_sys_admin is needed to mount stuff.
# ordinarily /run/wrappers/bin/mount would do that via setuid, but sandboxes have no_new_privs by default.
capabilities = [ "sys_admin" ];
@@ -168,7 +166,6 @@ in
};
"sane-scripts.stop-all-servo".sandbox = {
method = "bwrap";
whitelistSystemctl = true;
};

View File

@@ -1,32 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs;
in
{
sane.programs.sanebox = {
packageUnwrapped = (pkgs.sanebox.override {
bubblewrap = cfg.bubblewrap.package;
iproute2 = cfg.iproute2.package;
iptables = cfg.iptables.package;
libcap = cfg.capsh.package; #< the sandboxer doesn't use any other libcap binaries
passt = cfg.passt.package;
landlock-sandboxer = cfg.landlock-sandboxer.package;
# landlock-sandboxer = pkgs.landlock-sandboxer.override {
# # not strictly necessary (landlock ABI is versioned), however when sandboxer version != kernel version,
# # the sandboxer may nag about one or the other wanting to be updated.
# linux = config.boot.kernelPackages.kernel;
# };
}).overrideAttrs (base: {
# create a directory which holds just the `sanebox` so that we
# can add sanebox as a dependency to binaries via `PATH=/run/current-system/libexec/sanebox` without forcing rebuild every time sanebox changes
postInstall = ''
mkdir -p $out/libexec/sanebox
ln -s $out/bin/sanebox $out/libexec/sanebox/sanebox
'';
});
sandbox.enable = false;
};
environment.pathsToLink = lib.mkIf cfg.sanebox.enabled [ "/libexec/sanebox" ];
}

View File

@@ -267,16 +267,6 @@ exec --no-startup-id mv $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY.lock $XDG_RUNTIME_DIR/
# the double-$ means to set the variable at *runtime*, not at "compile-time" (so that it doesn't impact the line immediately above us
set $$WAYLAND_DISPLAY "$(echo $DESIRED_WAYLAND_DISPLAY)"
# manually export PATH here, since all my user services need that, and sanebox implementation depends on it.
# also, manually export XDG_DATA_DIRS. glib fails in weird ways (e.g. thinks everything is application/x-octet-stream mime type) without it.
# for more, see: <repo:nixos/nixpkgs:nixos/modules/programs/wayland/sway.nix>
#
# XXX: dbus-update-activation-environment --systemd is ASYNCHRONOUS. it returns before the systemd environment is actually updated.
# hence, call `systemctl import-environment` ourselves. i could probably remove the dbus stuff and be safe, but at least for now it's an OK backup.
# exec --no-startup-id systemctl --user import-environment PATH XDG_DATA_DIRS DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
# exec --no-startup-id dbus-update-activation-environment --systemd PATH XDG_DATA_DIRS DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
# previously: `include /etc/sway/config.d/*` was needed for xdg-desktop-portal-* to work.
# stock nixos `programs.sway` would setup /etc/sway/config.d with additional variables to import to the dbus env.
# but now i'm doing that manually:

View File

@@ -65,7 +65,7 @@ lib.mkIf config.sane.persist.enable
sandbox.tryKeepUsers = true;
sandbox.keepPids = true;
sandbox.extraPaths = [
"/run/gocryptfs/private.key" #< TODO: teach sanebox about `-o FLAG1=VALUE1,FLAG2=VALUE2` style of argument passing, then use `existing` autodetect, and remove this
"/run/gocryptfs/private.key" #< TODO: teach sandbox about `-o FLAG1=VALUE1,FLAG2=VALUE2` style of argument passing, then use `existing` autodetect, and remove this
];
suggestedPrograms = [ "gocryptfs" ];
};

View File

@@ -100,13 +100,7 @@ let
allowedRunPaths = lib.unique allowedRunPaths;
};
in
(makeSandboxed.override {
sanebox = if sandbox.method == "bunpen" then
pkgs.bunpen
else
pkgs.sanebox
;
}) {
makeSandboxed {
inherit pkgName package;
inherit (sandbox)
embedSandboxer
@@ -293,7 +287,7 @@ let
'';
};
sandbox.method = mkOption {
type = types.nullOr (types.enum [ "bunpen" "bwrap" "capshonly" "pastaonly" "landlock" ]);
type = types.nullOr (types.enum [ "bunpen" ]);
default = "bunpen";
description = ''
how/whether to sandbox all binaries in the package.
@@ -495,8 +489,7 @@ let
description = ''
extra arguments to pass to the sandbox wrapper.
example: [
"--sanebox-dns"
"1.1.1.1"
"--bunpen-keep-pid"
]
'';
};
@@ -526,15 +519,7 @@ let
wrapPkg name config config.packageUnwrapped
;
suggestedPrograms = lib.mkIf saneCfg.sandbox.enable (
lib.optionals (config.sandbox.method == "bwrap") [
"sanebox" "bubblewrap" "passt" "iproute2" "iptables"
] ++ lib.optionals (config.sandbox.method == "landlock") [
"sanebox" "landlock-sandboxer" "capsh"
] ++ lib.optionals (config.sandbox.method == "pastaonly") [
"sanebox" "passt" "iproute2" "iptables" "capsh"
] ++ lib.optionals (config.sandbox.method == "capshonly") [
"sanebox" "capsh"
] ++ lib.optionals (config.sandbox.method == "bunpen") [
lib.optionals (config.sandbox.method == "bunpen") [
"bunpen"
]
);

View File

@@ -1,20 +1,5 @@
{ lib }:
let
saneboxGenerators = {
autodetectCliPaths = style: [ "--sanebox-autodetect" style ];
capability = cap: [ "--sanebox-cap" cap ];
dns = addr: [ "--sanebox-dns" addr ];
keepIpc = [ "--sanebox-keep-namespace" "ipc" ];
keepPids = [ "--sanebox-keep-namespace" "pid" ];
tryKeepUsers = [ "--sanebox-keep-namespace" "user" ];
method = method: [ "--sanebox-method" method ];
netDev = netDev: [ "--sanebox-net-dev" netDev ];
netGateway = netGateway: [ "--sanebox-net-gateway" netGateway ];
path.unqualified = p: [ "--sanebox-path" p ];
path.home = p: [ "--sanebox-home-path" p ];
path.run = p: [ "--sanebox-run-path" p ];
whitelistPwd = [ "--sanebox-add-pwd" ];
};
bunpenGenerators = {
autodetectCliPaths = style: [ "--bunpen-autodetect" style ];
capability = cap: [ "--bunpen-cap" cap ];
@@ -72,7 +57,7 @@ let
gen = if method == "bunpen" then
bunpenGenerators
else
saneboxGenerators
bunpenGenerators
;
allowPaths = flavor: paths: lib.flatten (builtins.map gen.path."${flavor}" paths);

View File

@@ -2,6 +2,7 @@
lib,
stdenv,
buildPackages,
bunpen,
file,
gnugrep,
gnused,
@@ -9,15 +10,14 @@
makeBinaryWrapper,
makeShellWrapper,
runCommandLocal,
sanebox,
writeShellScriptBin,
xorg,
}:
let
fakeSaneSandboxed = writeShellScriptBin sanebox.meta.mainProgram ''
# behave like the real sanebox with SANEBOX_DISABLE=1,
# but in a manner which avoids taking a dependency on the real sanebox.
# the primary use for this is to allow a package's `check` phase to work even when sanebox isn't available.
fakeSaneSandboxed = writeShellScriptBin bunpen.meta.mainProgram ''
# behave like the real bunpen with BUNPEN_DISABLE=1,
# but in a manner which avoids taking a dependency on the real bunpen.
# the primary use for this is to allow a package's `check` phase to work even when bunpen isn't available (which allows for faster iteration).
_origArgs=($@)
# throw away all arguments until we find the path to the binary which is being sandboxed
@@ -25,12 +25,12 @@ let
shift
done
if [ "$#" -eq 0 ]; then
>&2 echo "${sanebox.meta.mainProgram} (mock): failed to parse args: ''${_origArgs[*]}"
>&2 echo "${bunpen.meta.mainProgram} (mock): failed to parse args: ''${_origArgs[*]}"
exit 1
fi
if [ -z "$SANEBOX_DISABLE" ]; then
>&2 echo "${sanebox.meta.mainProgram} (mock): not called with SANEBOX_DISABLE=1; unsure how to sandbox: ''${_origArgs[*]}"
if [ -z "$BUNPEN_DISABLE" ]; then
>&2 echo "${bunpen.meta.mainProgram} (mock): not called with BUNPEN_DISABLE=1; unsure how to sandbox: ''${_origArgs[*]}"
exit 1
fi
# assume that every argument after the binary name is an argument for the binary and not for the sandboxer.
@@ -45,14 +45,14 @@ let
# take an existing package, which may have a `bin/` folder as well as `share/` etc,
# and patch the `bin/` items in-place
sandboxBinariesInPlace = sanebox': extraSandboxArgs: pkgName: pkg: pkg.overrideAttrs (unwrapped: {
sandboxBinariesInPlace = bunpen': extraSandboxArgs: pkgName: pkg: pkg.overrideAttrs (unwrapped: {
# disable the sandbox and inject a minimal fake sandboxer which understands that flag,
# in order to support packages which invoke sandboxed apps in their check phase.
# note that it's not just for packages which invoke their *own* binaries in check phase,
# but also packages which invoke OTHER PACKAGES' sandboxed binaries.
# hence, put the fake sandbox in nativeBuildInputs instead of nativeCheckInputs.
env = (unwrapped.env or {}) // {
SANEBOX_DISABLE = 1;
BUNPEN_DISABLE = 1;
};
outputs = unwrapped.outputs or [ "out" ];
nativeBuildInputs = [
@@ -64,14 +64,14 @@ let
makeShellWrapper
] ++ (unwrapped.nativeBuildInputs or []);
disallowedReferences = (unwrapped.disallowedReferences or []) ++ [
# the fake sandbox gates itself behind SANEBOX_DISABLE, so if it did end up deployed
# the fake sandbox gates itself behind BUNPEN_DISABLE, so if it did end up deployed
# then it wouldn't permit anything not already permitted. but it would still be annoying.
fakeSaneSandboxed
];
postFixup = (unwrapped.postFixup or "") + ''
assertExecutable() {
: # my programs refer to sanebox by name, not path, which triggers an over-eager assertion in nixpkgs (so, mask that)
: # my programs refer to bunpen by name, not path, which triggers an over-eager assertion in nixpkgs (so, mask that)
}
makeDocumentedCWrapper() {
# this is identical to nixpkgs' implementation, only replace execv with execvp, the latter which looks for the executable on PATH.
@@ -101,27 +101,11 @@ let
mv "$_dir/$_name" "$_dir/.sandboxed/"
fi
if [ -n "${sanebox.interpreter or ""}" ]; then
# N.B.: double `escapeShellArg`: once for the shell wrapper, and again for runtime because the shell wrapper doesn't escape.
# spotcheck this by seeing if animatch (requires a path "Holy Pangolin") works
makeShellWrapper ${sanebox'} "$_dir/$_name" \
--suffix PATH : /run/current-system/sw/libexec/${sanebox.pname} \
--inherit-argv0 \
${lib.escapeShellArgs (lib.flatten (builtins.map (f: [ "--add-flags" (lib.escapeShellArg f) ]) extraSandboxArgs))} \
--add-flags "$_dir/.sandboxed/$_name"
# `exec`ing a script with an interpreter will smash $0. instead, source it to preserve $0:
# - <https://github.com/NixOS/nixpkgs/issues/150841#issuecomment-995589961>
substituteInPlace "$_dir/$_name" \
--replace-fail 'exec -a "$0" ' 'source '
else
# we can use a binary shell wrapper since the wrapper's environment is capable of forwarding argv[0].
makeBinaryWrapper ${sanebox'} "$_dir/$_name" \
--suffix PATH : /run/current-system/sw/libexec/${sanebox.pname} \
--inherit-argv0 \
${lib.escapeShellArgs (lib.flatten (builtins.map (f: [ "--add-flags" f ]) extraSandboxArgs))} \
--add-flags "$_dir/.sandboxed/$_name"
fi
makeBinaryWrapper ${bunpen'} "$_dir/$_name" \
--suffix PATH : /run/current-system/sw/libexec/${bunpen.pname} \
--inherit-argv0 \
${lib.escapeShellArgs (lib.flatten (builtins.map (f: [ "--add-flags" f ]) extraSandboxArgs))} \
--add-flags "$_dir/.sandboxed/$_name"
}
derefWhileInSameOutput() {
@@ -415,7 +399,7 @@ let
};
passthru = (prevAttrs.passthru or {}) // extraPassthru // {
checkSandboxed = runCommandLocal "${pkgName}-check-sandboxed" {
nativeBuildInputs = [ file gnugrep sanebox ];
nativeBuildInputs = [ bunpen file gnugrep ];
buildInputs = builtins.map (out: finalAttrs.finalPackage."${out}") (finalAttrs.outputs or [ "out" ]);
} ''
set -e
@@ -426,7 +410,7 @@ let
local dir="$1"
local binname="$2"
echo "checking if $dir/$binname is sandboxed"
echo " sandboxer is ${sanebox.name}"
echo " sandboxer is ${bunpen.name}"
echo " PATH=$PATH"
# XXX: call by full path because some binaries (e.g. util-linux) would otherwise
# be shadowed by things the nix builder implicitly puts on PATH.
@@ -434,8 +418,7 @@ let
# if the file doesn't have an interpreter, assume it's directly invokable by qemu (hence, the intentional lack of quotes around `interpreter`)
set -x
local realbin="$(realpath $dir/$binname)"
local interpreter=$(file "$realbin" | grep --only-matching "a /nix/.* script" | cut -d" " -f2 || echo "")
echo 'echo "printing for test"' | ${stdenv.hostPlatform.emulator buildPackages} $interpreter "$dir/$binname" --sanebox-net-dev all --sanebox-dns default --sanebox-net-gateway default --sanebox-replace-cli /bin/sh --bunpen-drop-shell \
echo 'echo "printing for test"' | ${stdenv.hostPlatform.emulator buildPackages} "$dir/$binname" --bunpen-drop-shell \
| grep "printing for test"
_numExec=$(( $_numExec + 1 ))
}
@@ -482,12 +465,12 @@ let
make-sandboxed = { pkgName, package, wrapperType, embedSandboxer ? false, extraSandboxerArgs ? [], passthru ? {} }:
let
unsandboxed = package;
sanebox' = if embedSandboxer then
bunpen' = if embedSandboxer then
# optionally hard-code the sandboxer. this forces rebuilds, but allows deep iteration w/o deploys.
lib.getExe sanebox
lib.getExe bunpen
else
#v prefer to load by bin name to reduce rebuilds
sanebox.meta.mainProgram
bunpen.meta.mainProgram
;
# two ways i could wrap a package in a sandbox:
@@ -498,14 +481,14 @@ let
# regardless of which one is chosen here, all other options are exposed via `passthru`.
sandboxedBy = {
inplace = sandboxBinariesInPlace
sanebox'
bunpen'
extraSandboxerArgs
pkgName
(makeHookable unsandboxed);
wrappedDerivation = let
sandboxedBin = sandboxBinariesInPlace
sanebox'
bunpen'
extraSandboxerArgs
pkgName
(symlinkBinaries pkgName unsandboxed);

View File

@@ -175,7 +175,7 @@ in
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
#VVV relaxed because it uses bwrap sandboxing (sanebox)
#VVV relaxed because my sandbox wrapper uses namespaces
serviceConfig.RestrictNamespaces = false;
serviceConfig.ProcSubset = "all";
serviceConfig.ProtectHostname = false;

View File

@@ -22,7 +22,7 @@
# 3. to apply a VPN to internet traffic selectively, just proxy an applications traffic into the VPN device
# 3a. use a network namespace and a userspace TCP stack (e.g. pasta/slirp4netns).
# 3b. attach the VPN device to a bridge device, then connect that to a network namespace by using a veth pair.
# 3c. juse use `sanebox`, which abstracts the above options.
# 3c. juse use `bunpen`, which abstracts the above options.
{ config, lib, sane-lib, ... }:
let

View File

@@ -1,67 +0,0 @@
{ lib, stdenv
, bash
, bubblewrap
, coreutils
, iproute2
, iptables
, landlock-sandboxer
, libcap
, passt
, substituteAll
, profileDir ? "/share/sanebox/profiles"
}:
stdenv.mkDerivation {
pname = "sanebox";
version = "0.1";
src = ./sanebox;
dontUnpack = true;
buildInputs = [
bash # for cross builds, to ensure #!/bin/sh is substituted
];
buildPhase = ''
runHook preBuild
substitute $src sanebox \
--replace-fail '@bwrap@' '${lib.getExe bubblewrap}' \
--replace-fail '@capsh@' '${lib.getExe' libcap "capsh"}' \
--replace-fail '@env@' '${lib.getExe' coreutils "env"}' \
--replace-fail '@ip@' '${lib.getExe' iproute2 "ip"}' \
--replace-fail '@iptables@' '${lib.getExe' iptables "iptables"}' \
--replace-fail '@landlockSandboxer@' '${lib.getExe landlock-sandboxer}' \
--replace-fail '@pasta@' '${lib.getExe' passt "pasta"}' \
--replace-fail '@readlink@' '${lib.getExe' coreutils "readlink"}' \
runHook postBuild
'';
installPhase = ''
runHook preInstall
install -d "$out"
install -d "$out/bin"
install -m 755 sanebox $out/bin/sanebox
runHook postInstall
'';
passthru = {
interpreter = lib.getBin bash;
runtimeDeps = [
bubblewrap
coreutils
landlock-sandboxer
libcap
passt
];
};
meta = {
description = ''
helper program to run some other program in a sandbox.
factoring this out allows:
1. to abstract over the particular sandbox implementation (bwrap, landlock, ...).
2. to modify sandbox settings without forcing a rebuild of the sandboxed package.
'';
mainProgram = "sanebox";
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p nettools -p openssh -p rsync -p sane-scripts.vpn -p sanebox
#!nix-shell -i bash -p bash -p nettools -p openssh -p rsync -p sane-scripts.vpn
# secret should include RN_USER
source /run/secrets/rsync-net-env