about summary refs log tree commit diff
path: root/pkgs/profpatsch/sandbox.nix
blob: c0c8ce7012d041b81e4f4a77ccbce0b734ddf966 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{ pkgs, writeExecline }:

let

  # remove everything but a few selected environment variables
  runInEmptyEnv = keepVars:
    let
        importas = pkgs.lib.concatMap (var: [ "importas" "-i" var var ]) keepVars;
        # we have to explicitely call export here, because PATH is probably empty
        export = pkgs.lib.concatMap (var: [ "${pkgs.execline}/bin/export" var ''''${${var}}'' ]) keepVars;
    in writeExecline "empty-env" {}
         (importas ++ [ "env" ] ++ export ++ [ "${pkgs.execline}/bin/exec" "$@" ]);


  # lightweight sandbox; execute any command in an unshared
  # namespace that only has access to /nix and the specified
  # directories from `extraMounts`.
  sandbox = { extraMounts ? [] }:
    let
      pathsToMount = [
        "/nix"
        "/dev" "/proc" "/sys"
      ] ++ extraMounts;
      # chain execlines and exit immediately if one fails
      all = builtins.concatMap (c: [ "if" c ]);
      mount = "${pkgs.util-linux}/bin/mount";
      unshare = "${pkgs.util-linux}/bin/unshare";
      # this is the directory the sandbox runs under (in a separate mount namespace)
      newroot = pkgs.runCommandLocal "sandbox-root" {} ''mkdir "$out"'';
      # this runs in a separate namespace, sets up a chroot root
      # and then chroots into the new root.
      sandbox = writeExecline "sandbox" {} (builtins.concatLists [
        # first, unshare the mount namespace and make us root
        # -> requires user namespaces!
        [ unshare "--mount" "--map-root-user" ]
        (all
          # mount a temporary file system which we can chroot to;
          # we can use the fixed path newroot here, because the resulting
          # tmpfs cannot be seen from the outside world (we are in an unshared
          # mount )
          ([ [ mount "-t" "tmpfs" "container_root" newroot ] ]
          # now mount the file system parts we need into the chroot
          ++ builtins.concatMap
               (rootPath: [
                 [ "${pkgs.coreutils}/bin/mkdir" "-p" "${newroot}${rootPath}" ]
                 [ mount "--rbind" rootPath "${newroot}${rootPath}" ]
               ])
               pathsToMount))
        [ # finally, chroot into our new root directory
          "${pkgs.coreutils}/bin/chroot" newroot
          # drop root permissions, become user nobody;
          # This is because many programs don’t like to be root
          # TODO: this unshare does not work, because we don’t have
          # the right permissions to do that here, unfortunately :(
          # unshare "--user" "--"
          "$@"
        ]
      ]);
    in sandbox;

in {
  inherit sandbox runInEmptyEnv;
}