grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / backup / snapraid.nix
blob72b4d9caef3c3a336a84e51241eb8f05ea4563c5
1 { config, lib, pkgs, ... }:
2 let cfg = config.services.snapraid;
3 in
5   imports = [
6     # Should have never been on the top-level.
7     (lib.mkRenamedOptionModule [ "snapraid" ] [ "services" "snapraid" ])
8   ];
10   options.services.snapraid = with lib.types; {
11     enable = lib.mkEnableOption "SnapRAID";
12     dataDisks = lib.mkOption {
13       default = { };
14       example = {
15         d1 = "/mnt/disk1/";
16         d2 = "/mnt/disk2/";
17         d3 = "/mnt/disk3/";
18       };
19       description = "SnapRAID data disks.";
20       type = attrsOf str;
21     };
22     parityFiles = lib.mkOption {
23       default = [ ];
24       example = [
25         "/mnt/diskp/snapraid.parity"
26         "/mnt/diskq/snapraid.2-parity"
27         "/mnt/diskr/snapraid.3-parity"
28         "/mnt/disks/snapraid.4-parity"
29         "/mnt/diskt/snapraid.5-parity"
30         "/mnt/disku/snapraid.6-parity"
31       ];
32       description = "SnapRAID parity files.";
33       type = listOf str;
34     };
35     contentFiles = lib.mkOption {
36       default = [ ];
37       example = [
38         "/var/snapraid.content"
39         "/mnt/disk1/snapraid.content"
40         "/mnt/disk2/snapraid.content"
41       ];
42       description = "SnapRAID content list files.";
43       type = listOf str;
44     };
45     exclude = lib.mkOption {
46       default = [ ];
47       example = [ "*.unrecoverable" "/tmp/" "/lost+found/" ];
48       description = "SnapRAID exclude directives.";
49       type = listOf str;
50     };
51     touchBeforeSync = lib.mkOption {
52       default = true;
53       example = false;
54       description =
55         "Whether {command}`snapraid touch` should be run before {command}`snapraid sync`.";
56       type = bool;
57     };
58     sync.interval = lib.mkOption {
59       default = "01:00";
60       example = "daily";
61       description = "How often to run {command}`snapraid sync`.";
62       type = str;
63     };
64     scrub = {
65       interval = lib.mkOption {
66         default = "Mon *-*-* 02:00:00";
67         example = "weekly";
68         description = "How often to run {command}`snapraid scrub`.";
69         type = str;
70       };
71       plan = lib.mkOption {
72         default = 8;
73         example = 5;
74         description =
75           "Percent of the array that should be checked by {command}`snapraid scrub`.";
76         type = int;
77       };
78       olderThan = lib.mkOption {
79         default = 10;
80         example = 20;
81         description =
82           "Number of days since data was last scrubbed before it can be scrubbed again.";
83         type = int;
84       };
85     };
86     extraConfig = lib.mkOption {
87       default = "";
88       example = ''
89         nohidden
90         blocksize 256
91         hashsize 16
92         autosave 500
93         pool /pool
94       '';
95       description = "Extra config options for SnapRAID.";
96       type = lines;
97     };
98   };
100   config =
101     let
102       nParity = builtins.length cfg.parityFiles;
103       mkPrepend = pre: s: pre + s;
104     in
105     lib.mkIf cfg.enable {
106       assertions = [
107         {
108           assertion = nParity <= 6;
109           message = "You can have no more than six SnapRAID parity files.";
110         }
111         {
112           assertion = builtins.length cfg.contentFiles >= nParity + 1;
113           message =
114             "There must be at least one SnapRAID content file for each SnapRAID parity file plus one.";
115         }
116       ];
118       environment = {
119         systemPackages = with pkgs; [ snapraid ];
121         etc."snapraid.conf" = {
122           text = with cfg;
123             let
124               prependData = mkPrepend "data ";
125               prependContent = mkPrepend "content ";
126               prependExclude = mkPrepend "exclude ";
127             in
128             lib.concatStringsSep "\n"
129               (map prependData
130                 ((lib.mapAttrsToList (name: value: name + " " + value)) dataDisks)
131               ++ lib.zipListsWith (a: b: a + b)
132                 ([ "parity " ] ++ map (i: toString i + "-parity ") (lib.range 2 6))
133                 parityFiles ++ map prependContent contentFiles
134               ++ map prependExclude exclude) + "\n" + extraConfig;
135         };
136       };
138       systemd.services = with cfg; {
139         snapraid-scrub = {
140           description = "Scrub the SnapRAID array";
141           startAt = scrub.interval;
142           serviceConfig = {
143             Type = "oneshot";
144             ExecStart = "${pkgs.snapraid}/bin/snapraid scrub -p ${
145               toString scrub.plan
146             } -o ${toString scrub.olderThan}";
147             Nice = 19;
148             IOSchedulingPriority = 7;
149             CPUSchedulingPolicy = "batch";
151             LockPersonality = true;
152             MemoryDenyWriteExecute = true;
153             NoNewPrivileges = true;
154             PrivateDevices = true;
155             PrivateTmp = true;
156             ProtectClock = true;
157             ProtectControlGroups = true;
158             ProtectHostname = true;
159             ProtectKernelLogs = true;
160             ProtectKernelModules = true;
161             ProtectKernelTunables = true;
162             RestrictAddressFamilies = "none";
163             RestrictNamespaces = true;
164             RestrictRealtime = true;
165             RestrictSUIDSGID = true;
166             SystemCallArchitectures = "native";
167             SystemCallFilter = "@system-service";
168             SystemCallErrorNumber = "EPERM";
169             CapabilityBoundingSet = "CAP_DAC_OVERRIDE";
171             ProtectSystem = "strict";
172             ProtectHome = "read-only";
173             ReadWritePaths =
174               # scrub requires access to directories containing content files
175               # to remove them if they are stale
176               let
177                 contentDirs = map dirOf contentFiles;
178               in
179               lib.unique (
180                 lib.attrValues dataDisks ++ contentDirs
181               );
182           };
183           unitConfig.After = "snapraid-sync.service";
184         };
185         snapraid-sync = {
186           description = "Synchronize the state of the SnapRAID array";
187           startAt = sync.interval;
188           serviceConfig = {
189             Type = "oneshot";
190             ExecStart = "${pkgs.snapraid}/bin/snapraid sync";
191             Nice = 19;
192             IOSchedulingPriority = 7;
193             CPUSchedulingPolicy = "batch";
195             LockPersonality = true;
196             MemoryDenyWriteExecute = true;
197             NoNewPrivileges = true;
198             PrivateTmp = true;
199             ProtectClock = true;
200             ProtectControlGroups = true;
201             ProtectHostname = true;
202             ProtectKernelLogs = true;
203             ProtectKernelModules = true;
204             ProtectKernelTunables = true;
205             RestrictAddressFamilies = "none";
206             RestrictNamespaces = true;
207             RestrictRealtime = true;
208             RestrictSUIDSGID = true;
209             SystemCallArchitectures = "native";
210             SystemCallFilter = "@system-service";
211             SystemCallErrorNumber = "EPERM";
212             CapabilityBoundingSet = "CAP_DAC_OVERRIDE" +
213               lib.optionalString cfg.touchBeforeSync " CAP_FOWNER";
215             ProtectSystem = "strict";
216             ProtectHome = "read-only";
217             ReadWritePaths =
218               # sync requires access to directories containing content files
219               # to remove them if they are stale
220               let
221                 contentDirs = map dirOf contentFiles;
222                 # Multiple "split" parity files can be specified in a single
223                 # "parityFile", separated by a comma.
224                 # https://www.snapraid.it/manual#7.1
225                 splitParityFiles = map (s: lib.splitString "," s) parityFiles;
226               in
227               lib.unique (
228                 lib.attrValues dataDisks ++ splitParityFiles ++ contentDirs
229               );
230           } // lib.optionalAttrs touchBeforeSync {
231             ExecStartPre = "${pkgs.snapraid}/bin/snapraid touch";
232           };
233         };
234       };
235     };