at 25.11-pre 6.6 kB view raw
1{ 2 config, 3 lib, 4 options, 5 pkgs, 6 ... 7}: 8 9let 10 inherit (lib) literalExpression mkOption types; 11 cfg = config.security.dhparams; 12 opt = options.security.dhparams; 13 14 bitType = types.addCheck types.int (b: b >= 16) // { 15 name = "bits"; 16 description = "integer of at least 16 bits"; 17 }; 18 19 paramsSubmodule = 20 { name, config, ... }: 21 { 22 options.bits = mkOption { 23 type = bitType; 24 default = cfg.defaultBitSize; 25 defaultText = literalExpression "config.${opt.defaultBitSize}"; 26 description = '' 27 The bit size for the prime that is used during a Diffie-Hellman 28 key exchange. 29 ''; 30 }; 31 32 options.path = mkOption { 33 type = types.path; 34 readOnly = true; 35 description = '' 36 The resulting path of the generated Diffie-Hellman parameters 37 file for other services to reference. This could be either a 38 store path or a file inside the directory specified by 39 {option}`security.dhparams.path`. 40 ''; 41 }; 42 43 config.path = 44 let 45 generated = pkgs.runCommand "dhparams-${name}.pem" { 46 nativeBuildInputs = [ pkgs.openssl ]; 47 } "openssl dhparam -out \"$out\" ${toString config.bits}"; 48 in 49 if cfg.stateful then "${cfg.path}/${name}.pem" else generated; 50 }; 51 52in 53{ 54 options = { 55 security.dhparams = { 56 enable = mkOption { 57 type = types.bool; 58 default = false; 59 description = '' 60 Whether to generate new DH params and clean up old DH params. 61 ''; 62 }; 63 64 params = mkOption { 65 type = 66 with types; 67 let 68 coerce = bits: { inherit bits; }; 69 in 70 attrsOf (coercedTo int coerce (submodule paramsSubmodule)); 71 default = { }; 72 example = lib.literalExpression "{ nginx.bits = 3072; }"; 73 description = '' 74 Diffie-Hellman parameters to generate. 75 76 The value is the size (in bits) of the DH params to generate. The 77 generated DH params path can be found in 78 `config.security.dhparams.params.«name».path`. 79 80 ::: {.note} 81 The name of the DH params is taken as being the name of 82 the service it serves and the params will be generated before the 83 said service is started. 84 ::: 85 86 ::: {.warning} 87 If you are removing all dhparams from this list, you 88 have to leave {option}`security.dhparams.enable` for at 89 least one activation in order to have them be cleaned up. This also 90 means if you rollback to a version without any dhparams the 91 existing ones won't be cleaned up. Of course this only applies if 92 {option}`security.dhparams.stateful` is 93 `true`. 94 ::: 95 96 ::: {.note} 97 **For module implementers:** It's recommended 98 to not set a specific bit size here, so that users can easily 99 override this by setting 100 {option}`security.dhparams.defaultBitSize`. 101 ::: 102 ''; 103 }; 104 105 stateful = mkOption { 106 type = types.bool; 107 default = true; 108 description = '' 109 Whether generation of Diffie-Hellman parameters should be stateful or 110 not. If this is enabled, PEM-encoded files for Diffie-Hellman 111 parameters are placed in the directory specified by 112 {option}`security.dhparams.path`. Otherwise the files are 113 created within the Nix store. 114 115 ::: {.note} 116 If this is `false` the resulting store 117 path will be non-deterministic and will be rebuilt every time the 118 `openssl` package changes. 119 ::: 120 ''; 121 }; 122 123 defaultBitSize = mkOption { 124 type = bitType; 125 default = 2048; 126 description = '' 127 This allows to override the default bit size for all of the 128 Diffie-Hellman parameters set in 129 {option}`security.dhparams.params`. 130 ''; 131 }; 132 133 path = mkOption { 134 type = types.str; 135 default = "/var/lib/dhparams"; 136 description = '' 137 Path to the directory in which Diffie-Hellman parameters will be 138 stored. This only is relevant if 139 {option}`security.dhparams.stateful` is 140 `true`. 141 ''; 142 }; 143 }; 144 }; 145 146 config = lib.mkIf (cfg.enable && cfg.stateful) { 147 systemd.services = 148 { 149 dhparams-init = { 150 description = "Clean Up Old Diffie-Hellman Parameters"; 151 152 # Clean up even when no DH params is set 153 wantedBy = [ "multi-user.target" ]; 154 155 serviceConfig.RemainAfterExit = true; 156 serviceConfig.Type = "oneshot"; 157 158 script = '' 159 if [ ! -d ${cfg.path} ]; then 160 mkdir -p ${cfg.path} 161 fi 162 163 # Remove old dhparams 164 for file in ${cfg.path}/*; do 165 if [ ! -f "$file" ]; then 166 continue 167 fi 168 ${lib.concatStrings ( 169 lib.mapAttrsToList ( 170 name: 171 { bits, path, ... }: 172 '' 173 if [ "$file" = ${lib.escapeShellArg path} ] && \ 174 ${pkgs.openssl}/bin/openssl dhparam -in "$file" -text \ 175 | head -n 1 | grep "(${toString bits} bit)" > /dev/null; then 176 continue 177 fi 178 '' 179 ) cfg.params 180 )} 181 rm "$file" 182 done 183 184 # TODO: Ideally this would be removing the *former* cfg.path, though 185 # this does not seem really important as changes to it are quite 186 # unlikely 187 rmdir --ignore-fail-on-non-empty ${cfg.path} 188 ''; 189 }; 190 } 191 // lib.mapAttrs' ( 192 name: 193 { bits, path, ... }: 194 lib.nameValuePair "dhparams-gen-${name}" { 195 description = "Generate Diffie-Hellman Parameters for ${name}"; 196 after = [ "dhparams-init.service" ]; 197 before = [ "${name}.service" ]; 198 wantedBy = [ "multi-user.target" ]; 199 unitConfig.ConditionPathExists = "!${path}"; 200 serviceConfig.Type = "oneshot"; 201 script = '' 202 mkdir -p ${lib.escapeShellArg cfg.path} 203 ${pkgs.openssl}/bin/openssl dhparam -out ${lib.escapeShellArg path} \ 204 ${toString bits} 205 ''; 206 } 207 ) cfg.params; 208 }; 209 210 meta.maintainers = with lib.maintainers; [ ekleog ]; 211}