From 34f086a6a5bb4c2f5c4968b085b306f07c6913f2 Mon Sep 17 00:00:00 2001 From: Gabriel Arazas Date: Sat, 27 Jul 2024 11:46:15 +0800 Subject: [PATCH] wrapper-manager/sandboxing/bubblewrap: revise filesystem submodule --- .../sandboxing/bubblewrap/default.nix | 51 ------ .../sandboxing/bubblewrap/filesystem.nix | 148 ++++++++++++++---- 2 files changed, 120 insertions(+), 79 deletions(-) diff --git a/modules/wrapper-manager/sandboxing/bubblewrap/default.nix b/modules/wrapper-manager/sandboxing/bubblewrap/default.nix index 82464ffd..26f3b54f 100644 --- a/modules/wrapper-manager/sandboxing/bubblewrap/default.nix +++ b/modules/wrapper-manager/sandboxing/bubblewrap/default.nix @@ -45,54 +45,6 @@ let 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 { @@ -127,9 +79,6 @@ in "--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; diff --git a/modules/wrapper-manager/sandboxing/bubblewrap/filesystem.nix b/modules/wrapper-manager/sandboxing/bubblewrap/filesystem.nix index 59cf449a..f51a4a1b 100644 --- a/modules/wrapper-manager/sandboxing/bubblewrap/filesystem.nix +++ b/modules/wrapper-manager/sandboxing/bubblewrap/filesystem.nix @@ -6,6 +6,17 @@ let cfg = config.sandboxing.bubblewrap; + fileOperationsWithPerms = [ + "file" "dir" + "bind-data" "ro-bind-data" + ]; + fileOperationsWithoutPerms = [ + "symlink" + "bind" "bind-try" + "dev-bind" "dev-bind-try" + "ro-bind" "ro-bind-try" + ]; + bubblewrapModuleFactory = { isGlobal ? false }: let filesystemSubmodule = { config, lib, name, ... }: { options = { @@ -17,23 +28,81 @@ let example = lib.literalExpression "./files/example.file"; }; - perms = lib.mkOption { + permissions = lib.mkOption { type = with lib.types; nullOr (strMatch "[0-7]{0,4}"); description = '' - The permissions of the node in octal. + The permissions of the node in octal. If the value is `null`, it + will be handled by Bubblewrap executable. For more details for each + operation, see {manpage}`bwrap(1)`. ''; default = null; example = "0755"; }; - symlink = lib.mkEnableOption "create the file as a symlink"; - createDir = lib.mkEnableOption "create the directory in the Bubblewrap environment"; - bindMount = lib.mkEnableOption "bind-mount the given source to the Bubblewrap environment"; - bindMountReadOnly = lib.mkEnableOption "bind-mount read-only the given source to the Bubblewrap environment"; + operation = lib.mkOption { + type = lib.types.enum (fileOperationsWithPerms ++ fileOperationsWithoutPerms); + description = '' + Specify what filesystem-related operations to be done for the given + filesystem object. Only certain operations accept permissions given + from {option}`sandboxing.bubblewrap.filesystem..permissions`. + ''; + default = "ro-bind-try"; + example = "bind"; + }; + + lock = lib.mkEnableOption "locking the file"; }; }; in { - options.filesystem = lib.mkOption { + 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. + ''; + }; + }; + + filesystem = lib.mkOption { type = with lib.types; attrsOf (submodule filesystemSubmodule); description = if isGlobal then '' @@ -47,20 +116,22 @@ let { "/etc/hello" = { source = ./files/hello; - perms = "0700"; + permissions = "0700"; + operation = "file"; }; "/etc/xdg" = { source = ./configs; - perms = "0700"; + permissions = "0700"; + operation = "dir"; }; "/srv/data" = { source = "/srv/data"; - symlink = true; + operation = "symlink"; }; - "/srv/logs".createDir = true; + "/srv/logs".operation = "dir"; } ''; }; @@ -68,33 +139,54 @@ let in { options.sandboxing.bubblewrap = bubblewrapModuleFactory { isGlobal = true; }; + config.sandboxing.bubblewrap.filesystem = + let + makeFilesystemMapping = operation: bind: + lib.nameValuePair bind { inherit operation; source = bind; }; + filesystemMappings = + lib.lists.map (makeFilesystemMapping "ro-bind-try") cfg.binds.ro + ++ lib.lists.map (makeFilesystemMapping "bind") cfg.binds.rw + ++ lib.lists.map (makeFilesystemMapping "dev-bind-try") cfg.binds.dev; + in + builtins.listToAttrs filesystemMappings; options.wrappers = let bubblewrapModule = { config, lib, name, ... }: let - submoduleCfg = config; + submoduleCfg = config.sandboxing.bubblewrap; in { options.sandboxing.bubblewrap = bubblewrapModuleFactory { isGlobal = false; }; config = lib.mkIf (config.sandboxing.variant == "bubblewrap") { sandboxing.bubblewrap.filesystem = + let + makeFilesystemMapping = operation: bind: + lib.nameValuePair bind { inherit operation; source = bind; }; + filesystemMappings = + lib.lists.map (makeFilesystemMapping "ro-bind-try") submoduleCfg.binds.ro + ++ lib.lists.map (makeFilesystemMapping "bind") submoduleCfg.binds.rw + ++ lib.lists.map (makeFilesystemMapping "dev-bind-try") submoduleCfg.binds.dev; + in + builtins.listToAttrs filesystemMappings; + + sandboxing.bubblewrap.extraArgs = + let + makeFilesystemArgs = dst: metadata: + let + src = metadata.source; + hasPermissions = metadata.permissions != null; + isValidOperationWithPerms = lib.elem metadata.operation fileOperationsWithPerms; + in + lib.optionals (hasPermissions && isValidOperationWithPerms) [ "--perms ${metadata.permissions}" ] + ++ ( + if metadata.operation == "dir" + then [ "--${metadata.operation} ${dst}" ] + else [ "--${metadata.operation} ${src} ${dst}" ] + ) + ++ lib.optionals metadata.lock [ "--lock-file ${dst}" ]; + in lib.lists.flatten - (lib.mapAttrsToList - (dst: metadata: - lib.optionals (metadata.perms != null) [ "--perms ${metadata.perms}" ] - ++ (let - inherit (metadata) source; - in - if metadata.createDir - then [ "--dir ${dst}"] - else if metadata.symlink - then [ "--symlink ${source} ${dst}"] - else if metadata.bindMount - then [ "--bind-data ${source} ${dst}" ] - else if metadata.bindMountReadOnly - then [ "--ro-bind-data ${source} ${dst}" ] - else [ "--file ${source} ${dst}"])) - submoduleCfg.filesystem); + (lib.mapAttrsToList makeFilesystemArgs submoduleCfg.filesystem); }; }; in