wrapper-manager/sandboxing/bubblewrap: init prototype

Not complete, just its options for now. We might even go into the
direction of NixPak and nix-bubblewrap creating our own specialized
launcher.
This commit is contained in:
Gabriel Arazas 2024-07-26 15:41:49 +08:00
parent 101e2da60c
commit d633fc2b38
No known key found for this signature in database
GPG Key ID: 62104B43D00AA360
2 changed files with 159 additions and 0 deletions

View File

@ -0,0 +1,156 @@
# Bubblewrap integration within wrapper-manager. Several parts were inspired
# from the source code's given examples (at `demo` directory) as well as a few
# Nix projects integrating with it such as `nix-bubblewrap`
# (https://git.sr.ht/~fgaz/nix-bubblewrap) and NixPak
# (https://github.com/nixpak/nixpak).
#
# Similar to most of them, this is basically a builder for the right arguments
# to be passed to `bwrap`.
{ config, lib, pkgs, ... }:
let
inherit (pkgs) stdenv;
cfg = config.sandboxing.bubblewrap;
bubblewrapModuleFactory = { isGlobal ? false }: {
package = lib.mkPackageOption pkgs "bubblewrap" { } // lib.optionalAttrs isGlobal {
default = cfg.package;
};
extraArgs = lib.mkOption {
type = with lib.types; listOf str;
default = [ ];
description =
if isGlobal
then ''
Global list of extra arguments to be given to all Bubblewrap-enabled
wrappers.
''
else ''
List of extra arguments to be given to the Bubblewrap executable.
'';
};
enableSharedNixStore = lib.mkEnableOption "sharing of the Nix store" // {
default = if isGlobal then true else cfg.enableSharedNixStore;
};
enableNetwork = lib.mkEnableOption "sharing of the host network" // lib.optionalAttrs isGlobal {
default = if isGlobal then true else cfg.enableNetwork;
};
enableIsolation = lib.mkEnableOption "unsharing most of the system" // {
default = if isGlobal then true else cfg.enableIsolation;
};
binds = {
ro = lib.mkOption {
type = with lib.types; listOf path;
default = if isGlobal then [ ] else cfg.binds.ro;
description =
if isGlobal
then ''
Global list of read-only mounts to be given to all Bubblewrap-enabled
wrappers.
''
else ''
List of read-only mounts to the Bubblewrap environment.
'';
example = [
"/etc/resolv.conf"
"/etc/ssh"
];
};
rw = lib.mkOption {
type = with lib.types; listOf path;
default = if isGlobal then [ ] else cfg.binds.rw;
description =
if isGlobal
then ''
Global list of read-write mounts to be given to all
Bubblewrap-enabled wrappers.
''
else ''
List of read-write mounts to the Bubblewrap environment.
'';
};
dev = lib.mkOption {
type = with lib.types; listOf path;
default = if isGlobal then [ ] else cfg.binds.dev;
description =
if isGlobal
then ''
Global list of devices to be mounted to all Bubblewrap-enabled
wrappers.
''
else ''
List of devices to be mounted inside of the Bubblewrap environment.
'';
};
};
};
in
{
options.sandboxing.bubblewrap = bubblewrapModuleFactory { isGlobal = true; };
options.wrappers =
let
bubblewrapModule = { name, config, lib, pkgs, ... }:
let
submoduleCfg = config.sandboxing.bubblewrap;
in
{
options.sandboxing.variant = lib.mkOption {
type = with lib.types; nullOr (enum [ "bubblewrap" ]);
};
options.sandboxing.bubblewrap = bubblewrapModuleFactory { isGlobal = false; };
config = lib.mkIf (config.sandboxing.variant == "bubblewrap") (lib.mkMerge [
{
# TODO: All of the Linux-exclusive flags could be handled by the
# launcher instead. ALSO MODULARIZE THIS CRAP!
# Ordering of the arguments here matter(?).
bubblewrap.extraArgs =
cfg.extraArgs
++ lib.optionals stdenv.isLinux [
"--proc" "/proc"
"--dev" "/dev"
]
++ builtins.map (bind: "--ro-bind-try ${bind}") submoduleCfg.binds.ro
++ builtins.map (bind: "--bind ${bind}") submoduleCfg.binds.rw
++ builtins.map (bind: "--dev-bind-try ${bind}") submoduleCfg.binds.dev
++ builtins.map (var: "--unsetenv ${var}") config.unset
++ lib.mapAttrsToList (var: value: "--setenv ${var} ${value}") config.env;
arg0 = lib.getExe' submoduleCfg.package "bwrap";
prependArgs = lib.mkBefore (submoduleCfg.extraArgs ++ [ "--" submoduleCfg.wraparound.executable ] ++ submoduleCfg.wraparound.extraArgs);
}
(lib.mkIf submoduleCfg.enableSharedNixStore {
bubblewrap.binds.ro = [ builtins.storeDir ] ++ lib.optionals (builtins.storeDir != "/nix/store") [ "/nix/store" ];
})
(lib.mkIf submoduleCfg.enableNetwork {
# In case isolation is also enabled, we'll have this still
# enabled at least.
bubblewrap.extraArgs = lib.mkAfter [ "--share-net" ];
bubblewrap.binds.ro = [
"/etc/ssh"
"/etc/hosts"
"/etc/resolv.conf"
];
})
(lib.mkIf submoduleCfg.enableIsolation {
bubblewrap.extraArgs = lib.mkBefore [ "--unshare-all" ];
})
]);
};
in
lib.mkOption {
type = with lib.types; attrsOf (submodule bubblewrapModule);
};
}

View File

@ -1,6 +1,9 @@
{ lib, ... }: { lib, ... }:
{ {
imports = [
./bubblewrap
];
options.wrappers = options.wrappers =
let let