sane-sandboxed: add more autodetect options, and a "withEmbeddedSandboxer" package output (for dev)
This commit is contained in:
parent
24e6e6cacc
commit
3439ca34b8
|
@ -43,7 +43,17 @@ let
|
|||
in
|
||||
makeSandboxed {
|
||||
inherit pkgName package;
|
||||
inherit (sandbox) autodetectCliPaths binMap capabilities embedProfile embedSandboxer extraConfig method whitelistPwd wrapperType;
|
||||
inherit (sandbox)
|
||||
autodetectCliPaths
|
||||
binMap
|
||||
capabilities
|
||||
embedProfile
|
||||
embedSandboxer
|
||||
extraConfig
|
||||
method
|
||||
whitelistPwd
|
||||
wrapperType
|
||||
;
|
||||
vpn = if net == "vpn" then vpn else null;
|
||||
allowedHomePaths = builtins.attrNames fs ++ builtins.attrNames persist.byPath ++ sandbox.extraHomePaths ++ [
|
||||
".config/mimeo" #< TODO: required, until i fully integrate xdg-open into sandboxing. else, `xdg-open https://...` inifinite-loops.
|
||||
|
@ -259,10 +269,15 @@ let
|
|||
'';
|
||||
};
|
||||
sandbox.autodetectCliPaths = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
type = types.coercedTo types.bool
|
||||
(b: if b then "existing" else null)
|
||||
(types.nullOr (types.enum [ "existing" "existingFileOrParent" ]));
|
||||
default = null;
|
||||
description = ''
|
||||
if a CLI argument looks like a PATH, should we add it to the sandbox?
|
||||
- null => never
|
||||
- "existing" => only if the file exists
|
||||
- "existingFileOrParent" => add the file if it exists; if not, add its parent if that exists. useful for programs which create files.
|
||||
'';
|
||||
};
|
||||
sandbox.whitelistPwd = mkOption {
|
||||
|
|
|
@ -199,87 +199,89 @@ let
|
|||
};
|
||||
});
|
||||
|
||||
in
|
||||
{ pkgName, package, method, wrapperType, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], autodetectCliPaths ? false, binMap ? {}, capabilities ? [], embedProfile ? false, embedSandboxer ? false, extraConfig ? [], whitelistPwd ? false }:
|
||||
let
|
||||
sane-sandboxed' = if embedSandboxer then
|
||||
# optionally hard-code the sandboxer. this forces rebuilds, but allows deep iteration w/o deploys.
|
||||
lib.getExe sane-sandboxed
|
||||
else
|
||||
#v prefer to load by bin name to reduce rebuilds
|
||||
sane-sandboxed.meta.mainProgram
|
||||
;
|
||||
make-sandboxed = { pkgName, package, method, wrapperType, vpn ? null, allowedHomePaths ? [], allowedRootPaths ? [], autodetectCliPaths ? null, binMap ? {}, capabilities ? [], embedProfile ? false, embedSandboxer ? false, extraConfig ? [], whitelistPwd ? false }@args:
|
||||
let
|
||||
sane-sandboxed' = if embedSandboxer then
|
||||
# optionally hard-code the sandboxer. this forces rebuilds, but allows deep iteration w/o deploys.
|
||||
lib.getExe sane-sandboxed
|
||||
else
|
||||
#v prefer to load by bin name to reduce rebuilds
|
||||
sane-sandboxed.meta.mainProgram
|
||||
;
|
||||
|
||||
allowPath = p: [
|
||||
"--sane-sandbox-path"
|
||||
p
|
||||
];
|
||||
allowHomePath = p: [
|
||||
"--sane-sandbox-home-path"
|
||||
p
|
||||
];
|
||||
allowPaths = paths: lib.flatten (builtins.map allowPath paths);
|
||||
allowHomePaths = paths: lib.flatten (builtins.map allowHomePath paths);
|
||||
allowPath = p: [
|
||||
"--sane-sandbox-path"
|
||||
p
|
||||
];
|
||||
allowHomePath = p: [
|
||||
"--sane-sandbox-home-path"
|
||||
p
|
||||
];
|
||||
allowPaths = paths: lib.flatten (builtins.map allowPath paths);
|
||||
allowHomePaths = paths: lib.flatten (builtins.map allowHomePath paths);
|
||||
|
||||
capabilityFlags = lib.flatten (builtins.map (c: [ "--sane-sandbox-cap" c ]) capabilities);
|
||||
capabilityFlags = lib.flatten (builtins.map (c: [ "--sane-sandbox-cap" c ]) capabilities);
|
||||
|
||||
vpnItems = [
|
||||
"--sane-sandbox-net"
|
||||
vpn.bridgeDevice
|
||||
] ++ lib.flatten (builtins.map (addr: [
|
||||
"--sane-sandbox-dns"
|
||||
addr
|
||||
]) vpn.dns);
|
||||
vpnItems = [
|
||||
"--sane-sandbox-net"
|
||||
vpn.bridgeDevice
|
||||
] ++ lib.flatten (builtins.map (addr: [
|
||||
"--sane-sandbox-dns"
|
||||
addr
|
||||
]) vpn.dns);
|
||||
|
||||
sandboxFlags = [
|
||||
"--sane-sandbox-method" method
|
||||
] ++ allowPaths allowedRootPaths
|
||||
++ allowHomePaths allowedHomePaths
|
||||
++ capabilityFlags
|
||||
++ lib.optionals autodetectCliPaths [ "--sane-sandbox-autodetect" ]
|
||||
++ lib.optionals whitelistPwd [ "--sane-sandbox-add-pwd" ]
|
||||
++ lib.optionals (vpn != null) vpnItems
|
||||
++ extraConfig;
|
||||
sandboxFlags = [
|
||||
"--sane-sandbox-method" method
|
||||
] ++ allowPaths allowedRootPaths
|
||||
++ allowHomePaths allowedHomePaths
|
||||
++ capabilityFlags
|
||||
++ lib.optionals (autodetectCliPaths != null) [ "--sane-sandbox-autodetect" autodetectCliPaths ]
|
||||
++ lib.optionals whitelistPwd [ "--sane-sandbox-add-pwd" ]
|
||||
++ lib.optionals (vpn != null) vpnItems
|
||||
++ extraConfig;
|
||||
|
||||
sandboxProfilesPkg = writeTextFile {
|
||||
name = "${pkgName}-sandbox-profiles";
|
||||
destination = "/share/sane-sandboxed/profiles/${pkgName}.profile";
|
||||
text = builtins.concatStringsSep "\n" sandboxFlags;
|
||||
};
|
||||
sandboxProfileDir = "${sandboxProfilesPkg}/share/sane-sandboxed/profiles";
|
||||
sandboxProfilesPkg = writeTextFile {
|
||||
name = "${pkgName}-sandbox-profiles";
|
||||
destination = "/share/sane-sandboxed/profiles/${pkgName}.profile";
|
||||
text = builtins.concatStringsSep "\n" sandboxFlags;
|
||||
};
|
||||
sandboxProfileDir = "${sandboxProfilesPkg}/share/sane-sandboxed/profiles";
|
||||
|
||||
maybeEmbedProfilesDir = lib.optionalString embedProfile ''"--sane-sandbox-profile-dir" "${sandboxProfileDir}"'';
|
||||
maybeEmbedProfilesDir = lib.optionalString embedProfile ''"--sane-sandbox-profile-dir" "${sandboxProfileDir}"'';
|
||||
|
||||
# two ways i could wrap a package in a sandbox:
|
||||
# 1. package.overrideAttrs, with `postFixup`.
|
||||
# 2. pkgs.symlinkJoin, creating an entirely new package which calls into the inner binaries.
|
||||
#
|
||||
# here we switch between the options.
|
||||
# regardless of which one is chosen here, all other options are exposed via `passthru`.
|
||||
sandboxedBy = {
|
||||
inplace = sandboxBinariesInPlace
|
||||
binMap
|
||||
sane-sandboxed'
|
||||
maybeEmbedProfilesDir
|
||||
pkgName
|
||||
(makeHookable package);
|
||||
|
||||
wrappedDerivation = let
|
||||
binaries = sandboxBinariesInPlace
|
||||
# two ways i could wrap a package in a sandbox:
|
||||
# 1. package.overrideAttrs, with `postFixup`.
|
||||
# 2. pkgs.symlinkJoin, creating an entirely new package which calls into the inner binaries.
|
||||
#
|
||||
# here we switch between the options.
|
||||
# regardless of which one is chosen here, all other options are exposed via `passthru`.
|
||||
sandboxedBy = {
|
||||
inplace = sandboxBinariesInPlace
|
||||
binMap
|
||||
sane-sandboxed'
|
||||
maybeEmbedProfilesDir
|
||||
pkgName
|
||||
(symlinkBinaries pkgName package);
|
||||
nonBinaries = copyNonBinaries pkgName package binaries;
|
||||
in symlinkJoin {
|
||||
name = "${pkgName}-sandboxed-all";
|
||||
paths = [ binaries nonBinaries ];
|
||||
passthru = { inherit binaries nonBinaries; };
|
||||
(makeHookable package);
|
||||
|
||||
wrappedDerivation = let
|
||||
binaries = sandboxBinariesInPlace
|
||||
binMap
|
||||
sane-sandboxed'
|
||||
maybeEmbedProfilesDir
|
||||
pkgName
|
||||
(symlinkBinaries pkgName package);
|
||||
nonBinaries = copyNonBinaries pkgName package binaries;
|
||||
in symlinkJoin {
|
||||
name = "${pkgName}-sandboxed-all";
|
||||
paths = [ binaries nonBinaries ];
|
||||
passthru = { inherit binaries nonBinaries; };
|
||||
};
|
||||
};
|
||||
};
|
||||
packageWrapped = sandboxedBy."${wrapperType}";
|
||||
in
|
||||
fixupMetaAndPassthru pkgName packageWrapped sandboxProfilesPkg {
|
||||
inherit sandboxedBy;
|
||||
}
|
||||
packageWrapped = sandboxedBy."${wrapperType}";
|
||||
in
|
||||
fixupMetaAndPassthru pkgName packageWrapped sandboxProfilesPkg {
|
||||
inherit sandboxedBy;
|
||||
withEmbeddedSandboxer = make-sandboxed (args // { embedSandboxer = true; });
|
||||
}
|
||||
;
|
||||
in make-sandboxed
|
||||
|
|
|
@ -63,11 +63,17 @@ urldecode() {
|
|||
echo -e "${_//%/\\x}"
|
||||
}
|
||||
|
||||
# return the path to this file or directory's parent, even if the input doesn't exist.
|
||||
parent() {
|
||||
realpath --logical --no-symlinks --canonicalize-missing "$1/.."
|
||||
}
|
||||
|
||||
# if the argument looks path-like, then add it to cliPathArgs.
|
||||
# this function ingests absolute, relative, or file:///-type URIs.
|
||||
# but it converts any such path into an absolute path before adding it to cliPathArgs.
|
||||
tryArgAsPath() {
|
||||
_arg="$1"
|
||||
_how="$2"
|
||||
_path=
|
||||
if [ "${_arg:0:1}" = "/" ]; then
|
||||
# absolute path
|
||||
|
@ -84,6 +90,14 @@ tryArgAsPath() {
|
|||
|
||||
if [ -e "$_path" ]; then
|
||||
cliPathArgs+=("$_path")
|
||||
elif [ "$_how" = "existingFileOrParent" ]; then
|
||||
# the path doesn't exist, but that's because the program might create it.
|
||||
if [ "${_path:0:1}" = "-" ]; then
|
||||
# 99% chance it's a CLI argument. if not, use `./-<...>`
|
||||
return
|
||||
elif [ -e "$(parent "$_path/..")" ]; then
|
||||
cliPathArgs+=("$_path/..")
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -148,7 +162,8 @@ parseArgs() {
|
|||
# this is handy for e.g. media players or document viewers.
|
||||
# it's best combined with some two-tiered thing.
|
||||
# e.g. first drop to the broadest path set of interest (Music,Videos,tmp, ...), then drop via autodetect.
|
||||
autodetect=1
|
||||
autodetect="$1"
|
||||
shift
|
||||
;;
|
||||
(--sane-sandbox-cap)
|
||||
_cap="$1"
|
||||
|
@ -381,7 +396,7 @@ capshonlyExec() {
|
|||
maybeAutodetectPaths() {
|
||||
if [ -n "$autodetect" ]; then
|
||||
for _arg in "${cliArgs[@]:1}"; do
|
||||
tryArgAsPath "$_arg"
|
||||
tryArgAsPath "$_arg" "$autodetect"
|
||||
done
|
||||
for _path in "${cliPathArgs[@]}"; do
|
||||
# TODO: might want to also mount the directory *above* this file,
|
||||
|
|
Loading…
Reference in New Issue
Block a user