diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index dc047426c0ca..eb21ab718578 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -394,7 +394,6 @@ ./security/ca.nix ./security/chromium-suid-sandbox.nix ./security/default.nix - ./security/dhparams.nix ./security/doas.nix ./security/duosec.nix ./security/google_oslogin.nix diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 4bca9ffcaaa0..bacb7275cea0 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -125,6 +125,9 @@ in (mkRemovedOptionModule [ "programs" "yabar" ] "programs.yabar has been removed from NixOS. This is because the yabar repository has been archived upstream." ) + (mkRemovedOptionModule [ "security" "dhparams" ] '' + The security.dhparams module has been removed as RFC 7919 has shown that generating your own params is problematic. + '') (mkRemovedOptionModule [ "security" "hideProcessInformation" ] '' The hidepid module was removed, since the underlying machinery is broken when using cgroups-v2. diff --git a/nixos/modules/security/dhparams.nix b/nixos/modules/security/dhparams.nix deleted file mode 100644 index a18af9f85ae7..000000000000 --- a/nixos/modules/security/dhparams.nix +++ /dev/null @@ -1,223 +0,0 @@ -{ - config, - lib, - options, - pkgs, - ... -}: - -let - inherit (lib) literalExpression mkOption types; - cfg = config.security.dhparams; - opt = options.security.dhparams; - - bitType = types.addCheck types.int (b: b >= 16) // { - name = "bits"; - description = "integer of at least 16 bits"; - }; - - paramsSubmodule = - { name, config, ... }: - { - options.bits = mkOption { - type = bitType; - default = cfg.defaultBitSize; - defaultText = literalExpression "config.${opt.defaultBitSize}"; - description = '' - The bit size for the prime that is used during a Diffie-Hellman - key exchange. - ''; - }; - - options.path = mkOption { - type = types.path; - readOnly = true; - description = '' - The resulting path of the generated Diffie-Hellman parameters - file for other services to reference. This could be either a - store path or a file inside the directory specified by - {option}`security.dhparams.path`. - ''; - }; - - config.path = - let - generated = pkgs.runCommand "dhparams-${name}.pem" { - nativeBuildInputs = [ pkgs.openssl ]; - } "openssl dhparam -out \"$out\" ${toString config.bits}"; - in - if cfg.stateful then "${cfg.path}/${name}.pem" else generated; - }; - -in -{ - options = { - security.dhparams = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Whether to generate new DH params and clean up old DH params. - ''; - }; - - params = mkOption { - type = - with types; - let - coerce = bits: { inherit bits; }; - in - attrsOf (coercedTo int coerce (submodule paramsSubmodule)); - default = { }; - example = lib.literalExpression "{ nginx.bits = 3072; }"; - description = '' - Diffie-Hellman parameters to generate. - - The value is the size (in bits) of the DH params to generate. The - generated DH params path can be found in - `config.security.dhparams.params.«name».path`. - - ::: {.note} - The name of the DH params is taken as being the name of - the service it serves and the params will be generated before the - said service is started. - ::: - - ::: {.warning} - If you are removing all dhparams from this list, you - have to leave {option}`security.dhparams.enable` for at - least one activation in order to have them be cleaned up. This also - means if you rollback to a version without any dhparams the - existing ones won't be cleaned up. Of course this only applies if - {option}`security.dhparams.stateful` is - `true`. - ::: - - ::: {.note} - **For module implementers:** It's recommended - to not set a specific bit size here, so that users can easily - override this by setting - {option}`security.dhparams.defaultBitSize`. - ::: - ''; - }; - - stateful = mkOption { - type = types.bool; - default = true; - description = '' - Whether generation of Diffie-Hellman parameters should be stateful or - not. If this is enabled, PEM-encoded files for Diffie-Hellman - parameters are placed in the directory specified by - {option}`security.dhparams.path`. Otherwise the files are - created within the Nix store. - - ::: {.note} - If this is `false` the resulting store - path will be non-deterministic and will be rebuilt every time the - `openssl` package changes. - ::: - ''; - }; - - defaultBitSize = mkOption { - type = bitType; - default = 2048; - description = '' - This allows to override the default bit size for all of the - Diffie-Hellman parameters set in - {option}`security.dhparams.params`. - ''; - }; - - path = mkOption { - type = types.str; - default = "/var/lib/dhparams"; - description = '' - Path to the directory in which Diffie-Hellman parameters will be - stored. This only is relevant if - {option}`security.dhparams.stateful` is - `true`. - ''; - }; - }; - }; - - config = lib.mkMerge [ - (lib.mkIf cfg.enable { - warnings = [ - '' - The `security.dhparams` module is deprecated and scheduled for removal in NixOS 26.11. - Generating your own params has been shown to be problematic in RFC 7919 (2016). - - Remove any uses of DHE and migrate to ECDHE (RFC 8422, 2018) and - Hybrid PQ (draft-ietf-tls-ecdhe-mlkem, 2026) key exchange algorithms. - '' - ]; - }) - (lib.mkIf (cfg.enable && cfg.stateful) { - systemd.services = { - dhparams-init = { - description = "Clean Up Old Diffie-Hellman Parameters"; - - # Clean up even when no DH params is set - wantedBy = [ "multi-user.target" ]; - - serviceConfig.RemainAfterExit = true; - serviceConfig.Type = "oneshot"; - - script = '' - if [ ! -d ${cfg.path} ]; then - mkdir -p ${cfg.path} - fi - - # Remove old dhparams - for file in ${cfg.path}/*; do - if [ ! -f "$file" ]; then - continue - fi - ${lib.concatStrings ( - lib.mapAttrsToList ( - name: - { bits, path, ... }: - '' - if [ "$file" = ${lib.escapeShellArg path} ] && \ - ${pkgs.openssl}/bin/openssl dhparam -in "$file" -text \ - | head -n 1 | grep "(${toString bits} bit)" > /dev/null; then - continue - fi - '' - ) cfg.params - )} - rm "$file" - done - - # TODO: Ideally this would be removing the *former* cfg.path, though - # this does not seem really important as changes to it are quite - # unlikely - rmdir --ignore-fail-on-non-empty ${cfg.path} - ''; - }; - } - // lib.mapAttrs' ( - name: - { bits, path, ... }: - lib.nameValuePair "dhparams-gen-${name}" { - description = "Generate Diffie-Hellman Parameters for ${name}"; - after = [ "dhparams-init.service" ]; - before = [ "${name}.service" ]; - requiredBy = [ "${name}.service" ]; - wantedBy = [ "multi-user.target" ]; - unitConfig.ConditionPathExists = "!${path}"; - serviceConfig.Type = "oneshot"; - script = '' - mkdir -p ${lib.escapeShellArg cfg.path} - ${pkgs.openssl}/bin/openssl dhparam -out ${lib.escapeShellArg path} \ - ${toString bits} - ''; - } - ) cfg.params; - }) - ]; - -} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 2791b39ca543..f0a98596de83 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -455,7 +455,6 @@ in dependency-track = runTest ./dependency-track.nix; devpi-server = runTest ./devpi-server.nix; dex-oidc = runTest ./dex-oidc.nix; - dhparams = runTest ./dhparams.nix; dictd = runTest ./dictd.nix; disable-installer-tools = runTest ./disable-installer-tools.nix; discourse = runTest { diff --git a/nixos/tests/dhparams.nix b/nixos/tests/dhparams.nix deleted file mode 100644 index 89df5b1a0154..000000000000 --- a/nixos/tests/dhparams.nix +++ /dev/null @@ -1,143 +0,0 @@ -{ - name = "dhparams"; - - nodes.machine = - { pkgs, ... }: - { - security.dhparams.enable = true; - environment.systemPackages = [ pkgs.openssl ]; - - specialisation = { - gen1.configuration = - { config, ... }: - { - security.dhparams.params = { - # Use low values here because we don't want the test to run for ages. - foo.bits = 1024; - # Also use the old format to make sure the type is coerced in the right - # way. - bar = 1025; - }; - - systemd.services.foo = { - description = "Check systemd Ordering"; - wantedBy = [ "multi-user.target" ]; - before = [ "shutdown.target" ]; - conflicts = [ "shutdown.target" ]; - unitConfig = { - # This is to make sure that the dhparams generation of foo occurs - # before this service so we need this service to start as early as - # possible to provoke a race condition. - DefaultDependencies = false; - - # We check later whether the service has been started or not. - ConditionPathExists = config.security.dhparams.params.foo.path; - }; - serviceConfig.Type = "oneshot"; - serviceConfig.RemainAfterExit = true; - # The reason we only provide an ExecStop here is to ensure that we don't - # accidentally trigger an error because a file system is not yet ready - # during very early startup (we might not even have the Nix store - # available, for example if future changes in NixOS use systemd mount - # units to do early file system initialisation). - serviceConfig.ExecStop = "${pkgs.coreutils}/bin/true"; - }; - }; - gen2.configuration = { - security.dhparams.params.foo.bits = 1026; - }; - gen3.configuration = { }; - gen4.configuration = { - security.dhparams.stateful = false; - security.dhparams.params.foo2.bits = 1027; - security.dhparams.params.bar2.bits = 1028; - }; - gen5.configuration = { - security.dhparams.defaultBitSize = 1029; - security.dhparams.params.foo3 = { }; - security.dhparams.params.bar3 = { }; - }; - }; - }; - - testScript = - { nodes, ... }: - let - getParamPath = - gen: name: - let - node = "gen${toString gen}"; - in - nodes.machine.config.specialisation.${node}.configuration.security.dhparams.params.${name}.path; - - switchToGeneration = - gen: - let - switchCmd = "${nodes.machine.config.system.build.toplevel}/specialisation/gen${toString gen}/bin/switch-to-configuration test"; - in - '' - with machine.nested("switch to generation ${toString gen}"): - machine.succeed("${switchCmd}") - ''; - - in - '' - import re - - - def assert_param_bits(path, bits): - with machine.nested(f"check bit size of {path}"): - output = machine.succeed(f"openssl dhparam -in {path} -text") - pattern = re.compile(r"^\s*DH Parameters:\s+\((\d+)\s+bit\)\s*$", re.M) - match = pattern.match(output) - if match is None: - raise Exception("bla") - if match[1] != str(bits): - raise Exception(f"bit size should be {bits} but it is {match[1]} instead.") - - machine.wait_for_unit("multi-user.target") - ${switchToGeneration 1} - - with subtest("verify startup order"): - machine.succeed("systemctl is-active foo.service") - - with subtest("check bit sizes of dhparam files"): - assert_param_bits("${getParamPath 1 "foo"}", 1024) - assert_param_bits("${getParamPath 1 "bar"}", 1025) - - ${switchToGeneration 2} - - with subtest("check whether bit size has changed"): - assert_param_bits("${getParamPath 2 "foo"}", 1026) - - with subtest("ensure that dhparams file for 'bar' was deleted"): - machine.fail("test -e ${getParamPath 1 "bar"}") - - ${switchToGeneration 3} - - with subtest("ensure that 'security.dhparams.path' has been deleted"): - machine.fail("test -e ${nodes.machine.config.specialisation.gen3.configuration.security.dhparams.path}") - - ${switchToGeneration 4} - - with subtest("check bit sizes dhparam files"): - assert_param_bits( - "${getParamPath 4 "foo2"}", 1027 - ) - assert_param_bits( - "${getParamPath 4 "bar2"}", 1028 - ) - - with subtest("check whether dhparam files are in the Nix store"): - machine.succeed( - "expr match ${getParamPath 4 "foo2"} ${builtins.storeDir}", - "expr match ${getParamPath 4 "bar2"} ${builtins.storeDir}", - ) - - ${switchToGeneration 5} - - with subtest("check whether defaultBitSize works as intended"): - assert_param_bits("${getParamPath 5 "foo3"}", 1029) - assert_param_bits("${getParamPath 5 "bar3"}", 1029) - ''; -}