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