at 23.05-pre 8.5 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with utils; 4with lib; 5 6let 7 8 randomEncryptionCoerce = enable: { inherit enable; }; 9 10 randomEncryptionOpts = { ... }: { 11 12 options = { 13 14 enable = mkOption { 15 default = false; 16 type = types.bool; 17 description = lib.mdDoc '' 18 Encrypt swap device with a random key. This way you won't have a persistent swap device. 19 20 WARNING: Don't try to hibernate when you have at least one swap partition with 21 this option enabled! We have no way to set the partition into which hibernation image 22 is saved, so if your image ends up on an encrypted one you would lose it! 23 24 WARNING #2: Do not use /dev/disk/by-uuid/ or /dev/disk/by-label/ as your swap device 25 when using randomEncryption as the UUIDs and labels will get erased on every boot when 26 the partition is encrypted. Best to use /dev/disk/by-partuuid/ 27 ''; 28 }; 29 30 cipher = mkOption { 31 default = "aes-xts-plain64"; 32 example = "serpent-xts-plain64"; 33 type = types.str; 34 description = lib.mdDoc '' 35 Use specified cipher for randomEncryption. 36 37 Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine. 38 ''; 39 }; 40 41 source = mkOption { 42 default = "/dev/urandom"; 43 example = "/dev/random"; 44 type = types.str; 45 description = lib.mdDoc '' 46 Define the source of randomness to obtain a random key for encryption. 47 ''; 48 }; 49 50 allowDiscards = mkOption { 51 default = false; 52 type = types.bool; 53 description = lib.mdDoc '' 54 Whether to allow TRIM requests to the underlying device. This option 55 has security implications; please read the LUKS documentation before 56 activating it. 57 ''; 58 }; 59 }; 60 61 }; 62 63 swapCfg = {config, options, ...}: { 64 65 options = { 66 67 device = mkOption { 68 example = "/dev/sda3"; 69 type = types.str; 70 description = lib.mdDoc "Path of the device or swap file."; 71 }; 72 73 label = mkOption { 74 example = "swap"; 75 type = types.str; 76 description = lib.mdDoc '' 77 Label of the device. Can be used instead of {var}`device`. 78 ''; 79 }; 80 81 size = mkOption { 82 default = null; 83 example = 2048; 84 type = types.nullOr types.int; 85 description = lib.mdDoc '' 86 If this option is set, device is interpreted as the 87 path of a swapfile that will be created automatically 88 with the indicated size (in megabytes). 89 ''; 90 }; 91 92 priority = mkOption { 93 default = null; 94 example = 2048; 95 type = types.nullOr types.int; 96 description = lib.mdDoc '' 97 Specify the priority of the swap device. Priority is a value between 0 and 32767. 98 Higher numbers indicate higher priority. 99 null lets the kernel choose a priority, which will show up as a negative value. 100 ''; 101 }; 102 103 randomEncryption = mkOption { 104 default = false; 105 example = { 106 enable = true; 107 cipher = "serpent-xts-plain64"; 108 source = "/dev/random"; 109 }; 110 type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts); 111 description = lib.mdDoc '' 112 Encrypt swap device with a random key. This way you won't have a persistent swap device. 113 114 HINT: run "cryptsetup benchmark" to test cipher performance on your machine. 115 116 WARNING: Don't try to hibernate when you have at least one swap partition with 117 this option enabled! We have no way to set the partition into which hibernation image 118 is saved, so if your image ends up on an encrypted one you would lose it! 119 120 WARNING #2: Do not use /dev/disk/by-uuid/ or /dev/disk/by-label/ as your swap device 121 when using randomEncryption as the UUIDs and labels will get erased on every boot when 122 the partition is encrypted. Best to use /dev/disk/by-partuuid/ 123 ''; 124 }; 125 126 discardPolicy = mkOption { 127 default = null; 128 example = "once"; 129 type = types.nullOr (types.enum ["once" "pages" "both" ]); 130 description = lib.mdDoc '' 131 Specify the discard policy for the swap device. If "once", then the 132 whole swap space is discarded at swapon invocation. If "pages", 133 asynchronous discard on freed pages is performed, before returning to 134 the available pages pool. With "both", both policies are activated. 135 See swapon(8) for more information. 136 ''; 137 }; 138 139 options = mkOption { 140 default = [ "defaults" ]; 141 example = [ "nofail" ]; 142 type = types.listOf types.nonEmptyStr; 143 description = lib.mdDoc '' 144 Options used to mount the swap. 145 ''; 146 }; 147 148 deviceName = mkOption { 149 type = types.str; 150 internal = true; 151 }; 152 153 realDevice = mkOption { 154 type = types.path; 155 internal = true; 156 }; 157 158 }; 159 160 config = rec { 161 device = mkIf options.label.isDefined 162 "/dev/disk/by-label/${config.label}"; 163 deviceName = lib.replaceChars ["\\"] [""] (escapeSystemdPath config.device); 164 realDevice = if config.randomEncryption.enable then "/dev/mapper/${deviceName}" else config.device; 165 }; 166 167 }; 168 169in 170 171{ 172 173 ###### interface 174 175 options = { 176 177 swapDevices = mkOption { 178 default = []; 179 example = [ 180 { device = "/dev/hda7"; } 181 { device = "/var/swapfile"; } 182 { label = "bigswap"; } 183 ]; 184 description = lib.mdDoc '' 185 The swap devices and swap files. These must have been 186 initialised using {command}`mkswap`. Each element 187 should be an attribute set specifying either the path of the 188 swap device or file (`device`) or the label 189 of the swap device (`label`, see 190 {command}`mkswap -L`). Using a label is 191 recommended. 192 ''; 193 194 type = types.listOf (types.submodule swapCfg); 195 }; 196 197 }; 198 199 config = mkIf ((length config.swapDevices) != 0) { 200 201 system.requiredKernelConfig = with config.lib.kernelConfig; [ 202 (isYes "SWAP") 203 ]; 204 205 # Create missing swapfiles. 206 systemd.services = 207 let 208 209 createSwapDevice = sw: 210 assert sw.device != ""; 211 assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-uuid" sw.device); 212 assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-label" sw.device); 213 let realDevice' = escapeSystemdPath sw.realDevice; 214 in nameValuePair "mkswap-${sw.deviceName}" 215 { description = "Initialisation of swap device ${sw.device}"; 216 wantedBy = [ "${realDevice'}.swap" ]; 217 before = [ "${realDevice'}.swap" ]; 218 path = [ pkgs.util-linux ] ++ optional sw.randomEncryption.enable pkgs.cryptsetup; 219 220 script = 221 '' 222 ${optionalString (sw.size != null) '' 223 currentSize=$(( $(stat -c "%s" "${sw.device}" 2>/dev/null || echo 0) / 1024 / 1024 )) 224 if [ "${toString sw.size}" != "$currentSize" ]; then 225 dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size} 226 chmod 0600 ${sw.device} 227 ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"} 228 fi 229 ''} 230 ${optionalString sw.randomEncryption.enable '' 231 cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \ 232 ${optionalString sw.randomEncryption.allowDiscards "--allow-discards"} ${sw.device} ${sw.deviceName} 233 mkswap ${sw.realDevice} 234 ''} 235 ''; 236 237 unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ]; 238 unitConfig.DefaultDependencies = false; # needed to prevent a cycle 239 serviceConfig.Type = "oneshot"; 240 serviceConfig.RemainAfterExit = sw.randomEncryption.enable; 241 serviceConfig.ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}"; 242 restartIfChanged = false; 243 }; 244 245 in listToAttrs (map createSwapDevice (filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices)); 246 247 }; 248 249}