about summary refs log tree commit diff
path: root/nixos/modules/security/isolate.nix
blob: 3cc0176f3db30bc1cec9d438db1409b17dfc6a49 (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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
{ config, lib, pkgs, ... }:

let
  inherit (lib) mkEnableOption mkPackageOption mkOption types mkIf maintainers;

  cfg = config.security.isolate;
  configFile = pkgs.writeText "isolate-config.cf" ''
    box_root=${cfg.boxRoot}
    lock_root=${cfg.lockRoot}
    cg_root=${cfg.cgRoot}
    first_uid=${toString cfg.firstUid}
    first_gid=${toString cfg.firstGid}
    num_boxes=${toString cfg.numBoxes}
    restricted_init=${if cfg.restrictedInit then "1" else "0"}
    ${cfg.extraConfig}
  '';
  isolate = pkgs.symlinkJoin {
    name = "isolate-wrapped-${pkgs.isolate.version}";

    paths = [ pkgs.isolate ];

    nativeBuildInputs = [ pkgs.makeWrapper ];

    postBuild = ''
      wrapProgram $out/bin/isolate \
        --set ISOLATE_CONFIG_FILE ${configFile}

      wrapProgram $out/bin/isolate-cg-keeper \
        --set ISOLATE_CONFIG_FILE ${configFile}
    '';
  };
in
{
  options.security.isolate = {
    enable = mkEnableOption ''
      Sandbox for securely executing untrusted programs
    '';

    package = mkPackageOption pkgs "isolate-unwrapped" { };

    boxRoot = mkOption {
      type = types.path;
      default = "/var/lib/isolate/boxes";
      description = ''
        All sandboxes are created under this directory.
        To avoid symlink attacks, this directory and all its ancestors
        must be writeable only by root.
      '';
    };

    lockRoot = mkOption {
      type = types.path;
      default = "/run/isolate/locks";
      description = ''
        Directory where lock files are created.
      '';
    };

    cgRoot = mkOption {
      type = types.str;
      default = "auto:/run/isolate/cgroup";
      description = ''
        Control group which subgroups are placed under.
        Either an explicit path to a subdirectory in cgroupfs, or "auto:file" to read
        the path from "file", where it is put by `isolate-cg-helper`.
      '';
    };

    firstUid = mkOption {
      type = types.numbers.between 1000 65533;
      default = 60000;
      description = ''
        Start of block of UIDs reserved for sandboxes.
      '';
    };

    firstGid = mkOption {
      type = types.numbers.between 1000 65533;
      default = 60000;
      description = ''
        Start of block of GIDs reserved for sandboxes.
      '';
    };

    numBoxes = mkOption {
      type = types.numbers.between 1000 65533;
      default = 1000;
      description = ''
        Number of UIDs and GIDs to reserve, starting from
        {option}`firstUid` and {option}`firstGid`.
      '';
    };

    restrictedInit = mkOption {
      type = types.bool;
      default = false;
      description = ''
        If true, only root can create sandboxes.
      '';
    };

    extraConfig = mkOption {
      type = types.str;
      default = "";
      description = ''
        Extra configuration to append to the configuration file.
      '';
    };
  };

  config = mkIf cfg.enable {
    environment.systemPackages = [
      isolate
    ];

    systemd.services.isolate = {
      description = "Isolate control group hierarchy daemon";
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        Type = "notify";
        ExecStart = "${isolate}/bin/isolate-cg-keeper";
        Slice = "isolate.slice";
        Delegate = true;
      };
    };

    systemd.slices.isolate = {
      description = "Isolate sandbox slice";
    };

    meta.maintainers = with maintainers; [ virchau13 ];
  };
}