1import ./make-test-python.nix {
2 name = "dhparams";
3
4 nodes.machine =
5 { pkgs, ... }:
6 {
7 security.dhparams.enable = true;
8 environment.systemPackages = [ pkgs.openssl ];
9
10 specialisation = {
11 gen1.configuration =
12 { config, ... }:
13 {
14 security.dhparams.params = {
15 # Use low values here because we don't want the test to run for ages.
16 foo.bits = 1024;
17 # Also use the old format to make sure the type is coerced in the right
18 # way.
19 bar = 1025;
20 };
21
22 systemd.services.foo = {
23 description = "Check systemd Ordering";
24 wantedBy = [ "multi-user.target" ];
25 before = [ "shutdown.target" ];
26 conflicts = [ "shutdown.target" ];
27 unitConfig = {
28 # This is to make sure that the dhparams generation of foo occurs
29 # before this service so we need this service to start as early as
30 # possible to provoke a race condition.
31 DefaultDependencies = false;
32
33 # We check later whether the service has been started or not.
34 ConditionPathExists = config.security.dhparams.params.foo.path;
35 };
36 serviceConfig.Type = "oneshot";
37 serviceConfig.RemainAfterExit = true;
38 # The reason we only provide an ExecStop here is to ensure that we don't
39 # accidentally trigger an error because a file system is not yet ready
40 # during very early startup (we might not even have the Nix store
41 # available, for example if future changes in NixOS use systemd mount
42 # units to do early file system initialisation).
43 serviceConfig.ExecStop = "${pkgs.coreutils}/bin/true";
44 };
45 };
46 gen2.configuration = {
47 security.dhparams.params.foo.bits = 1026;
48 };
49 gen3.configuration = { };
50 gen4.configuration = {
51 security.dhparams.stateful = false;
52 security.dhparams.params.foo2.bits = 1027;
53 security.dhparams.params.bar2.bits = 1028;
54 };
55 gen5.configuration = {
56 security.dhparams.defaultBitSize = 1029;
57 security.dhparams.params.foo3 = { };
58 security.dhparams.params.bar3 = { };
59 };
60 };
61 };
62
63 testScript =
64 { nodes, ... }:
65 let
66 getParamPath =
67 gen: name:
68 let
69 node = "gen${toString gen}";
70 in
71 nodes.machine.config.specialisation.${node}.configuration.security.dhparams.params.${name}.path;
72
73 switchToGeneration =
74 gen:
75 let
76 switchCmd = "${nodes.machine.config.system.build.toplevel}/specialisation/gen${toString gen}/bin/switch-to-configuration test";
77 in
78 ''
79 with machine.nested("switch to generation ${toString gen}"):
80 machine.succeed("${switchCmd}")
81 '';
82
83 in
84 ''
85 import re
86
87
88 def assert_param_bits(path, bits):
89 with machine.nested(f"check bit size of {path}"):
90 output = machine.succeed(f"openssl dhparam -in {path} -text")
91 pattern = re.compile(r"^\s*DH Parameters:\s+\((\d+)\s+bit\)\s*$", re.M)
92 match = pattern.match(output)
93 if match is None:
94 raise Exception("bla")
95 if match[1] != str(bits):
96 raise Exception(f"bit size should be {bits} but it is {match[1]} instead.")
97
98 machine.wait_for_unit("multi-user.target")
99 ${switchToGeneration 1}
100
101 with subtest("verify startup order"):
102 machine.succeed("systemctl is-active foo.service")
103
104 with subtest("check bit sizes of dhparam files"):
105 assert_param_bits("${getParamPath 1 "foo"}", 1024)
106 assert_param_bits("${getParamPath 1 "bar"}", 1025)
107
108 ${switchToGeneration 2}
109
110 with subtest("check whether bit size has changed"):
111 assert_param_bits("${getParamPath 2 "foo"}", 1026)
112
113 with subtest("ensure that dhparams file for 'bar' was deleted"):
114 machine.fail("test -e ${getParamPath 1 "bar"}")
115
116 ${switchToGeneration 3}
117
118 with subtest("ensure that 'security.dhparams.path' has been deleted"):
119 machine.fail("test -e ${nodes.machine.config.specialisation.gen3.configuration.security.dhparams.path}")
120
121 ${switchToGeneration 4}
122
123 with subtest("check bit sizes dhparam files"):
124 assert_param_bits(
125 "${getParamPath 4 "foo2"}", 1027
126 )
127 assert_param_bits(
128 "${getParamPath 4 "bar2"}", 1028
129 )
130
131 with subtest("check whether dhparam files are in the Nix store"):
132 machine.succeed(
133 "expr match ${getParamPath 4 "foo2"} ${builtins.storeDir}",
134 "expr match ${getParamPath 4 "bar2"} ${builtins.storeDir}",
135 )
136
137 ${switchToGeneration 5}
138
139 with subtest("check whether defaultBitSize works as intended"):
140 assert_param_bits("${getParamPath 5 "foo3"}", 1029)
141 assert_param_bits("${getParamPath 5 "bar3"}", 1029)
142 '';
143}