at 17.09-beta 7.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 = '' 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 = '' 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 = '' 46 Define the source of randomness to obtain a random key for encryption. 47 ''; 48 }; 49 50 }; 51 52 }; 53 54 swapCfg = {config, options, ...}: { 55 56 options = { 57 58 device = mkOption { 59 example = "/dev/sda3"; 60 type = types.str; 61 description = "Path of the device."; 62 }; 63 64 label = mkOption { 65 example = "swap"; 66 type = types.str; 67 description = '' 68 Label of the device. Can be used instead of <varname>device</varname>. 69 ''; 70 }; 71 72 size = mkOption { 73 default = null; 74 example = 2048; 75 type = types.nullOr types.int; 76 description = '' 77 If this option is set, device is interpreted as the 78 path of a swapfile that will be created automatically 79 with the indicated size (in megabytes). 80 ''; 81 }; 82 83 priority = mkOption { 84 default = null; 85 example = 2048; 86 type = types.nullOr types.int; 87 description = '' 88 Specify the priority of the swap device. Priority is a value between 0 and 32767. 89 Higher numbers indicate higher priority. 90 null lets the kernel choose a priority, which will show up as a negative value. 91 ''; 92 }; 93 94 randomEncryption = mkOption { 95 default = false; 96 example = { 97 enable = true; 98 cipher = "serpent-xts-plain64"; 99 source = "/dev/random"; 100 }; 101 type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts); 102 description = '' 103 Encrypt swap device with a random key. This way you won't have a persistent swap device. 104 105 HINT: run "cryptsetup benchmark" to test cipher performance on your machine. 106 107 WARNING: Don't try to hibernate when you have at least one swap partition with 108 this option enabled! We have no way to set the partition into which hibernation image 109 is saved, so if your image ends up on an encrypted one you would lose it! 110 111 WARNING #2: Do not use /dev/disk/by-uuid/ or /dev/disk/by-label/ as your swap device 112 when using randomEncryption as the UUIDs and labels will get erased on every boot when 113 the partition is encrypted. Best to use /dev/disk/by-partuuid/ 114 ''; 115 }; 116 117 deviceName = mkOption { 118 type = types.str; 119 internal = true; 120 }; 121 122 realDevice = mkOption { 123 type = types.path; 124 internal = true; 125 }; 126 127 }; 128 129 config = rec { 130 device = mkIf options.label.isDefined 131 "/dev/disk/by-label/${config.label}"; 132 deviceName = lib.replaceChars ["\\"] [""] (escapeSystemdPath config.device); 133 realDevice = if config.randomEncryption.enable then "/dev/mapper/${deviceName}" else config.device; 134 }; 135 136 }; 137 138in 139 140{ 141 142 ###### interface 143 144 options = { 145 146 swapDevices = mkOption { 147 default = []; 148 example = [ 149 { device = "/dev/hda7"; } 150 { device = "/var/swapfile"; } 151 { label = "bigswap"; } 152 ]; 153 description = '' 154 The swap devices and swap files. These must have been 155 initialised using <command>mkswap</command>. Each element 156 should be an attribute set specifying either the path of the 157 swap device or file (<literal>device</literal>) or the label 158 of the swap device (<literal>label</literal>, see 159 <command>mkswap -L</command>). Using a label is 160 recommended. 161 ''; 162 163 type = types.listOf (types.submodule swapCfg); 164 }; 165 166 }; 167 168 config = mkIf ((length config.swapDevices) != 0) { 169 170 system.requiredKernelConfig = with config.lib.kernelConfig; [ 171 (isYes "SWAP") 172 ]; 173 174 # Create missing swapfiles. 175 # FIXME: support changing the size of existing swapfiles. 176 systemd.services = 177 let 178 179 createSwapDevice = sw: 180 assert sw.device != ""; 181 assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-uuid" sw.device); 182 assert !(sw.randomEncryption.enable && lib.hasPrefix "/dev/disk/by-label" sw.device); 183 let realDevice' = escapeSystemdPath sw.realDevice; 184 in nameValuePair "mkswap-${sw.deviceName}" 185 { description = "Initialisation of swap device ${sw.device}"; 186 wantedBy = [ "${realDevice'}.swap" ]; 187 before = [ "${realDevice'}.swap" ]; 188 path = [ pkgs.utillinux ] ++ optional sw.randomEncryption.enable pkgs.cryptsetup; 189 190 script = 191 '' 192 ${optionalString (sw.size != null) '' 193 currentSize=$(( $(stat -c "%s" "${sw.device}" 2>/dev/null || echo 0) / 1024 / 1024 )) 194 if [ "${toString sw.size}" != "$currentSize" ]; then 195 fallocate -l ${toString sw.size}M "${sw.device}" || 196 dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size} 197 if [ "${toString sw.size}" -lt "$currentSize" ]; then 198 truncate --size "${toString sw.size}M" "${sw.device}" 199 fi 200 chmod 0600 ${sw.device} 201 ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"} 202 fi 203 ''} 204 ${optionalString sw.randomEncryption.enable '' 205 cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} ${sw.device} ${sw.deviceName} 206 mkswap ${sw.realDevice} 207 ''} 208 ''; 209 210 unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ]; 211 unitConfig.DefaultDependencies = false; # needed to prevent a cycle 212 serviceConfig.Type = "oneshot"; 213 serviceConfig.RemainAfterExit = sw.randomEncryption.enable; 214 serviceConfig.ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}"; 215 restartIfChanged = false; 216 }; 217 218 in listToAttrs (map createSwapDevice (filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices)); 219 220 }; 221 222}