1import ./make-test-python.nix ({ pkgs, ... }:
2
3let inherit (import ./ssh-keys.nix pkgs)
4 snakeOilPrivateKey snakeOilPublicKey;
5in {
6 name = "openssh";
7 meta = with pkgs.lib.maintainers; {
8 maintainers = [ aszlig eelco ];
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-lazy =
26 { ... }:
27
28 {
29 services.openssh = { enable = true; startWhenNeeded = true; };
30 security.pam.services.sshd.limits =
31 [ { domain = "*"; item = "memlock"; type = "-"; value = 1024; } ];
32 users.users.root.openssh.authorizedKeys.keys = [
33 snakeOilPublicKey
34 ];
35 };
36
37 server-localhost-only =
38 { ... }:
39
40 {
41 services.openssh = {
42 enable = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } ];
43 };
44 };
45
46 server-localhost-only-lazy =
47 { ... }:
48
49 {
50 services.openssh = {
51 enable = true; startWhenNeeded = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } ];
52 };
53 };
54
55 server-match-rule =
56 { ... }:
57
58 {
59 services.openssh = {
60 enable = true; listenAddresses = [ { addr = "127.0.0.1"; port = 22; } { addr = "[::]"; port = 22; } ];
61 extraConfig = ''
62 # Combined test for two (predictable) Match criterias
63 Match LocalAddress 127.0.0.1 LocalPort 22
64 PermitRootLogin yes
65
66 # Separate tests for Match criterias
67 Match User root
68 PermitRootLogin yes
69 Match Group root
70 PermitRootLogin yes
71 Match Host nohost.example
72 PermitRootLogin yes
73 Match LocalAddress 127.0.0.1
74 PermitRootLogin yes
75 Match LocalPort 22
76 PermitRootLogin yes
77 Match RDomain nohost.example
78 PermitRootLogin yes
79 Match Address 127.0.0.1
80 PermitRootLogin yes
81 '';
82 };
83 };
84
85 server_allowedusers =
86 { ... }:
87
88 {
89 services.openssh = { enable = true; settings.AllowUsers = [ "alice" "bob" ]; };
90 users.groups = { alice = { }; bob = { }; carol = { }; };
91 users.users = {
92 alice = { isNormalUser = true; group = "alice"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
93 bob = { isNormalUser = true; group = "bob"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
94 carol = { isNormalUser = true; group = "carol"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
95 };
96 };
97
98 client =
99 { ... }: { };
100
101 };
102
103 testScript = ''
104 start_all()
105
106 server.wait_for_unit("sshd", timeout=30)
107 server_localhost_only.wait_for_unit("sshd", timeout=30)
108 server_match_rule.wait_for_unit("sshd", timeout=30)
109
110 server_lazy.wait_for_unit("sshd.socket", timeout=30)
111 server_localhost_only_lazy.wait_for_unit("sshd.socket", timeout=30)
112
113 with subtest("manual-authkey"):
114 client.succeed("mkdir -m 700 /root/.ssh")
115 client.succeed(
116 '${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""'
117 )
118 public_key = client.succeed(
119 "${pkgs.openssh}/bin/ssh-keygen -y -f /root/.ssh/id_ed25519"
120 )
121 public_key = public_key.strip()
122 client.succeed("chmod 600 /root/.ssh/id_ed25519")
123
124 server.succeed("mkdir -m 700 /root/.ssh")
125 server.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
126 server_lazy.succeed("mkdir -m 700 /root/.ssh")
127 server_lazy.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
128
129 client.wait_for_unit("network.target")
130 client.succeed(
131 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2",
132 timeout=30
133 )
134 client.succeed(
135 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024",
136 timeout=30
137 )
138
139 client.succeed(
140 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server-lazy 'echo hello world' >&2",
141 timeout=30
142 )
143 client.succeed(
144 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server-lazy 'ulimit -l' | grep 1024",
145 timeout=30
146 )
147
148 with subtest("configured-authkey"):
149 client.succeed(
150 "cat ${snakeOilPrivateKey} > privkey.snakeoil"
151 )
152 client.succeed("chmod 600 privkey.snakeoil")
153 client.succeed(
154 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server true",
155 timeout=30
156 )
157 client.succeed(
158 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-lazy true",
159 timeout=30
160 )
161
162 with subtest("localhost-only"):
163 server_localhost_only.succeed("ss -nlt | grep '127.0.0.1:22'")
164 server_localhost_only_lazy.succeed("ss -nlt | grep '127.0.0.1:22'")
165
166 with subtest("match-rules"):
167 server_match_rule.succeed("ss -nlt | grep '127.0.0.1:22'")
168
169 with subtest("allowed-users"):
170 client.succeed(
171 "cat ${snakeOilPrivateKey} > privkey.snakeoil"
172 )
173 client.succeed("chmod 600 privkey.snakeoil")
174 client.succeed(
175 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil alice@server_allowedusers true",
176 timeout=30
177 )
178 client.succeed(
179 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil bob@server_allowedusers true",
180 timeout=30
181 )
182 client.fail(
183 "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil carol@server_allowedusers true",
184 timeout=30
185 )
186 '';
187})