1import ./make-test-python.nix ({ pkgs, ... }:
2
3let inherit (import ./ssh-keys.nix pkgs)
4 snakeOilPrivateKey snakeOilPublicKey snakeOilEd25519PrivateKey snakeOilEd25519PublicKey;
5in {
6 name = "openssh";
7 meta = with pkgs.lib.maintainers; {
8 maintainers = [ aszlig ];
9 };
10
11 nodes = {
12
13 server =
14 { ... }:
15
16 {
17 services.openssh.enable = true;
18 security.pam.services.sshd.limits =
19 [ { domain = "*"; item = "memlock"; type = "-"; value = 1024; } ];
20 users.users.root.openssh.authorizedKeys.keys = [
21 snakeOilPublicKey
22 ];
23 };
24
25 server-allowed-users =
26 { ... }:
27
28 {
29 services.openssh = { enable = true; settings.AllowUsers = [ "alice" "bob" ]; };
30 users.groups = { alice = { }; bob = { }; carol = { }; };
31 users.users = {
32 alice = { isNormalUser = true; group = "alice"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
33 bob = { isNormalUser = true; group = "bob"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
34 carol = { isNormalUser = true; group = "carol"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
35 };
36 };
37
38 server-lazy =
39 { ... }:
40
41 {
42 services.openssh = { enable = true; startWhenNeeded = true; };
43 security.pam.services.sshd.limits =
44 [ { domain = "*"; item = "memlock"; type = "-"; value = 1024; } ];
45 users.users.root.openssh.authorizedKeys.keys = [
46 snakeOilPublicKey
47 ];
48 };
49
50 server-lazy-socket = {
51 virtualisation.vlans = [ 1 2 ];
52 services.openssh = {
53 enable = true;
54 startWhenNeeded = true;
55 ports = [ 2222 ];
56 listenAddresses = [ { addr = "0.0.0.0"; } ];
57 };
58 users.users.root.openssh.authorizedKeys.keys = [
59 snakeOilPublicKey
60 ];
61 };
62
63 server-localhost-only =
64 { ... }:
65
66 {
67 services.openssh = {
68 enable = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } ];
69 };
70 };
71
72 server-localhost-only-lazy =
73 { ... }:
74
75 {
76 services.openssh = {
77 enable = true; startWhenNeeded = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } ];
78 };
79 };
80
81 server-match-rule =
82 { ... }:
83
84 {
85 services.openssh = {
86 enable = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } { addr = "[::]"; port = 22; } ];
87 extraConfig = ''
88 # Combined test for two (predictable) Match criterias
89 Match LocalAddress 127.0.0.1 LocalPort 22
90 PermitRootLogin yes
91
92 # Separate tests for Match criterias
93 Match User root
94 PermitRootLogin yes
95 Match Group root
96 PermitRootLogin yes
97 Match Host nohost.example
98 PermitRootLogin yes
99 Match LocalAddress 127.0.0.1
100 PermitRootLogin yes
101 Match LocalPort 22
102 PermitRootLogin yes
103 Match RDomain nohost.example
104 PermitRootLogin yes
105 Match Address 127.0.0.1
106 PermitRootLogin yes
107 '';
108 };
109 };
110
111 server-no-openssl =
112 { ... }:
113 {
114 programs.ssh.package = pkgs.opensshPackages.openssh.override {
115 linkOpenssl = false;
116 };
117 services.openssh = {
118 enable = true;
119 hostKeys = [
120 { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
121 ];
122 settings = {
123 # Must not specify the OpenSSL provided algorithms.
124 Ciphers = [ "chacha20-poly1305@openssh.com" ];
125 KexAlgorithms = [
126 "curve25519-sha256"
127 "curve25519-sha256@libssh.org"
128 ];
129 };
130 };
131 users.users.root.openssh.authorizedKeys.keys = [
132 snakeOilEd25519PublicKey
133 ];
134 };
135
136 server-no-pam =
137 { pkgs, ... }:
138 {
139 programs.ssh.package = pkgs.opensshPackages.openssh.override {
140 withPAM = false;
141 };
142 services.openssh = {
143 enable = true;
144 settings = {
145 UsePAM = false;
146 };
147 };
148 users.users.root.openssh.authorizedKeys.keys = [
149 snakeOilPublicKey
150 ];
151 };
152
153 client =
154 { ... }: {
155 virtualisation.vlans = [ 1 2 ];
156 };
157
158 };
159
160 testScript = ''
161 start_all()
162
163 server.wait_for_unit("sshd", timeout=30)
164 server_allowed_users.wait_for_unit("sshd", timeout=30)
165 server_localhost_only.wait_for_unit("sshd", timeout=30)
166 server_match_rule.wait_for_unit("sshd", timeout=30)
167 server_no_openssl.wait_for_unit("sshd", timeout=30)
168 server_no_pam.wait_for_unit("sshd", timeout=30)
169
170 server_lazy.wait_for_unit("sshd.socket", timeout=30)
171 server_localhost_only_lazy.wait_for_unit("sshd.socket", timeout=30)
172 server_lazy_socket.wait_for_unit("sshd.socket", timeout=30)
173
174 with subtest("manual-authkey"):
175 client.succeed("mkdir -m 700 /root/.ssh")
176 client.succeed(
177 '${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""'
178 )
179 public_key = client.succeed(
180 "${pkgs.openssh}/bin/ssh-keygen -y -f /root/.ssh/id_ed25519"
181 )
182 public_key = public_key.strip()
183 client.succeed("chmod 600 /root/.ssh/id_ed25519")
184
185 server.succeed("mkdir -m 700 /root/.ssh")
186 server.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
187 server_lazy.succeed("mkdir -m 700 /root/.ssh")
188 server_lazy.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
189
190 client.wait_for_unit("network.target")
191 client.succeed(
192 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2",
193 timeout=30
194 )
195 client.succeed(
196 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024",
197 timeout=30
198 )
199
200 client.succeed(
201 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server-lazy 'echo hello world' >&2",
202 timeout=30
203 )
204 client.succeed(
205 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server-lazy 'ulimit -l' | grep 1024",
206 timeout=30
207 )
208
209 with subtest("socket activation on a non-standard port"):
210 client.succeed(
211 "cat ${snakeOilPrivateKey} > privkey.snakeoil"
212 )
213 client.succeed("chmod 600 privkey.snakeoil")
214 # The final segment in this IP is allocated according to the alphabetical order of machines in this test.
215 client.succeed(
216 "ssh -p 2222 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.2.5 true",
217 timeout=30
218 )
219
220 with subtest("configured-authkey"):
221 client.succeed(
222 "cat ${snakeOilPrivateKey} > privkey.snakeoil"
223 )
224 client.succeed("chmod 600 privkey.snakeoil")
225 client.succeed(
226 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server true",
227 timeout=30
228 )
229 client.succeed(
230 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-lazy true",
231 timeout=30
232 )
233
234 with subtest("localhost-only"):
235 server_localhost_only.succeed("ss -nlt | grep '127.0.0.1:22'")
236 server_localhost_only_lazy.succeed("ss -nlt | grep '127.0.0.1:22'")
237
238 with subtest("match-rules"):
239 server_match_rule.succeed("ss -nlt | grep '127.0.0.1:22'")
240
241 with subtest("allowed-users"):
242 client.succeed(
243 "cat ${snakeOilPrivateKey} > privkey.snakeoil"
244 )
245 client.succeed("chmod 600 privkey.snakeoil")
246 client.succeed(
247 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil alice@server-allowed-users true",
248 timeout=30
249 )
250 client.succeed(
251 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil bob@server-allowed-users true",
252 timeout=30
253 )
254 client.fail(
255 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil carol@server-allowed-users true",
256 timeout=30
257 )
258
259 with subtest("no-openssl"):
260 client.succeed(
261 "cat ${snakeOilEd25519PrivateKey} > privkey.snakeoil"
262 )
263 client.succeed("chmod 600 privkey.snakeoil")
264 client.succeed(
265 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-no-openssl true",
266 timeout=30
267 )
268
269 with subtest("no-pam"):
270 client.succeed(
271 "cat ${snakeOilPrivateKey} > privkey.snakeoil"
272 )
273 client.succeed("chmod 600 privkey.snakeoil")
274 client.succeed(
275 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-no-pam true",
276 timeout=30
277 )
278 '';
279})