From 1e67f6a2bc496cb5014915a71e323603e4b41662 Mon Sep 17 00:00:00 2001 From: Daiderd Jordan Date: Sat, 16 Feb 2019 16:18:52 +0100 Subject: [PATCH] sandbox: add module for sandbox profiles This could be used outside of nix-darwin, but this is mainly useful for services since all of the inputs are known there. { # $ /usr/bin/sandbox-exec -f $profile $coreutils/bin/ls / # ls: cannot access '/': Operation not permitted security.sandbox.profiles.example.closure = [ pkgs.coreutils ]; } --- default.nix | 1 + modules/security/sandbox/default.nix | 131 +++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 modules/security/sandbox/default.nix diff --git a/default.nix b/default.nix index 37f804de..b7c564f8 100644 --- a/default.nix +++ b/default.nix @@ -20,6 +20,7 @@ let packages ./modules/alias.nix ./modules/security/pki + ./modules/security/sandbox ./modules/system ./modules/system/checks.nix ./modules/system/activation-scripts.nix diff --git a/modules/security/sandbox/default.nix b/modules/security/sandbox/default.nix new file mode 100644 index 00000000..4ad453a2 --- /dev/null +++ b/modules/security/sandbox/default.nix @@ -0,0 +1,131 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.security.sandbox; + + profile = + { config, name, ... }: + { + options = { + profile = mkOption { + type = types.lines; + apply = text: pkgs.runCommandNoCC "sandbox.sb" {} '' + for f in $(< ${config.closure}/store-paths); do + storePaths+="(subpath \"$f\")" + done + + cat <<-EOF > $out + ${text} + EOF + ''; + }; + + closure = mkOption { + type = types.listOf types.package; + default = []; + apply = paths: pkgs.closureInfo { rootPaths = paths; }; + description = "List of store paths to make accessible."; + }; + + readablePaths = mkOption { + type = types.listOf types.path; + default = []; + description = "List of paths that should be read-only inside the sandbox."; + }; + + writablePaths = mkOption { + type = types.listOf types.path; + default = []; + description = "List of paths that should be read/write inside the sandbox."; + }; + + allowSystemPaths = mkOption { + type = types.bool; + default = false; + }; + + allowLocalNetworking = mkOption { + type = types.bool; + default = false; + description = "Whether to allow localhost network access inside the sandbox."; + }; + + allowNetworking = mkOption { + type = types.bool; + default = false; + description = "Whether to allow network access inside the sandbox."; + }; + }; + + config = { + + profile = mkOrder 0 '' + (version 1) + (deny default) + + (allow file-read* + (subpath "/usr/lib") + (subpath "/System/Library/Frameworks") + (subpath "/System/Library/PrivateFrameworks")) + + (allow file-read-metadata + (literal "/dev")) + (allow file* + (literal "/dev/null") + (literal "/dev/random") + (literal "/dev/stdin") + (literal "/dev/stdout") + (literal "/dev/tty") + (literal "/dev/urandom") + (literal "/dev/zero") + (subpath "/dev/fd")) + + (allow process-fork) + (allow signal (target same-sandbox)) + (deny file-write* (subpath "/nix/store")) + (allow file-read* process-exec + $storePaths) + + ${optionalString (config.readablePaths != []) '' + (allow file-read* + ${concatMapStrings (x: ''(subpath "${x}")'') config.readablePaths}) + ''} + ${optionalString (config.writablePaths != []) '' + (allow file* + ${concatMapStrings (x: ''(subpath "${x}")'') config.writablePaths}) + ''} + ${optionalString config.allowSystemPaths '' + (allow file-read* process-exec + (subpath "/bin") + (subpath "/usr/bin")) + ''} + ${optionalString config.allowLocalNetworking '' + (allow network* (local ip) (local tcp) (local udp)) + ''} + ${optionalString config.allowNetworking '' + (allow network* + (local ip) + (remote ip)) + (allow network-outbound + (remote unix-socket (path-literal "/private/var/run/mDNSResponder"))) + ''} + ''; + + }; + }; +in + +{ + options = { + security.sandbox.profiles = mkOption { + type = types.attrsOf (types.submodule profile); + default = {}; + description = "Definition of sandbox profiles."; + }; + }; + + config = { + }; +}