1{ pkgs, ... }:
2let
3 sshKeys = import (pkgs.path + "/nixos/tests/ssh-keys.nix") pkgs;
4 sshUsername = "any-user";
5 serverName = "server";
6 clientName = "client";
7 sshAuditPort = 2222;
8in
9{
10 name = "ssh";
11
12 nodes = {
13 "${serverName}" = {
14 networking.firewall.allowedTCPPorts = [
15 sshAuditPort
16 ];
17 services.openssh.enable = true;
18 users.users."${sshUsername}" = {
19 isNormalUser = true;
20 openssh.authorizedKeys.keys = [
21 sshKeys.snakeOilPublicKey
22 ];
23 };
24 };
25 "${clientName}" = {
26 programs.ssh = {
27 ciphers = [
28 "aes128-ctr"
29 "aes128-gcm@openssh.com"
30 "aes192-ctr"
31 "aes256-ctr"
32 "aes256-gcm@openssh.com"
33 "chacha20-poly1305@openssh.com"
34 ];
35 extraConfig = ''
36 IdentitiesOnly yes
37 '';
38 hostKeyAlgorithms = [
39 "rsa-sha2-256"
40 "rsa-sha2-256-cert-v01@openssh.com"
41 "rsa-sha2-512"
42 "rsa-sha2-512-cert-v01@openssh.com"
43 "sk-ssh-ed25519-cert-v01@openssh.com"
44 "sk-ssh-ed25519@openssh.com"
45 "ssh-ed25519"
46 "ssh-ed25519-cert-v01@openssh.com"
47 ];
48 kexAlgorithms = [
49 "curve25519-sha256"
50 "curve25519-sha256@libssh.org"
51 "diffie-hellman-group-exchange-sha256"
52 "diffie-hellman-group16-sha512"
53 "diffie-hellman-group18-sha512"
54 "sntrup761x25519-sha512@openssh.com"
55 ];
56 macs = [
57 "hmac-sha2-256-etm@openssh.com"
58 "hmac-sha2-512-etm@openssh.com"
59 "umac-128-etm@openssh.com"
60 ];
61 };
62 };
63 };
64
65 testScript = ''
66 start_all()
67
68 ${serverName}.wait_for_open_port(22)
69
70 # Should pass SSH server audit
71 ${serverName}.succeed("${pkgs.ssh-audit}/bin/ssh-audit 127.0.0.1")
72
73 # Wait for client to be able to connect to the server
74 ${clientName}.systemctl("start network-online.target")
75 ${clientName}.wait_for_unit("network-online.target")
76
77 # Set up trusted private key
78 ${clientName}.succeed("cat ${sshKeys.snakeOilPrivateKey} > privkey.snakeoil")
79 ${clientName}.succeed("chmod 600 privkey.snakeoil")
80
81 # Fail fast and disable interactivity
82 ssh_options = "-o BatchMode=yes -o ConnectTimeout=1 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
83
84 # Should deny root user
85 ${clientName}.fail(f"ssh {ssh_options} root@${serverName} true")
86
87 # Should deny non-root user password login
88 ${clientName}.fail(f"ssh {ssh_options} -o PasswordAuthentication=yes ${sshUsername}@${serverName} true")
89
90 # Should allow non-root user certificate login
91 ${clientName}.succeed(f"ssh {ssh_options} -i privkey.snakeoil ${sshUsername}@${serverName} true")
92
93 # Should pass SSH client audit
94 service_name = "ssh-audit.service"
95 ${serverName}.succeed(f"systemd-run --unit={service_name} ${pkgs.ssh-audit}/bin/ssh-audit --client-audit --port=${toString sshAuditPort}")
96 ${clientName}.sleep(5) # We can't use wait_for_open_port because ssh-audit exits as soon as anything talks to it
97 ${clientName}.execute(
98 f"ssh {ssh_options} -i privkey.snakeoil -p ${toString sshAuditPort} ${sshUsername}@${serverName} true",
99 check_return=False,
100 timeout=10
101 )
102 ${serverName}.succeed(f"exit $(systemctl show --property=ExecMainStatus --value {service_name})")
103 '';
104}