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