blob: ddba233e3c5a61f6a6a9b89b5a16a8ff8a146a59 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
{ pkgs, ... }:
let
# Create a Python environment for the controller with all necessary test framework dependencies
controllerPython = pkgs.python3.withPackages (ps: [
ps.flaky
ps.jc
ps.passlib
ps.pytest
ps.pytest-mh
ps.pytest-ticket
]);
shadowHostName = "shadowhost";
in
{
name = "shadow-system-tests";
meta.maintainers = with pkgs.lib.maintainers; [ joaosreis ];
nodes = {
# The target host: runs sshd, has shadow and other test dependencies installed, mutable users, and some specific settings to match the expectations of the test suite
shadowhost =
{ pkgs, ... }:
{
networking.hostName = shadowHostName;
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "yes";
PasswordAuthentication = false;
};
};
users.mutableUsers = true;
environment.systemPackages = with pkgs; [
shadow
expect
vim
];
users.defaultUserShell = "/bin/sh";
security.loginDefs.settings = {
PASS_MAX_DAYS = 99999;
PASS_MIN_DAYS = 0;
PASS_WARN_AGE = 7;
USERGROUPS_ENAB = "yes";
CREATE_HOME = "yes";
UID_MIN = 1001;
GID_MIN = 1001;
};
security.pam.services = {
newusers.text = ''
auth required pam_permit.so
account required pam_permit.so
password required pam_permit.so
session required pam_permit.so
'';
};
services.envfs.enable = true;
};
# The controller: runs pytest-mh against the shadow host
controller =
{ pkgs, ... }:
{
environment.systemPackages = [
controllerPython
pkgs.openssh
];
};
};
testScript = ''
import textwrap
start_all()
# ------------------------------------------------------------------
# 1. Generate an SSH keypair on the controller and authorise it
# on the shadow host
# ------------------------------------------------------------------
controller.succeed("mkdir -p /root/.ssh && chmod 700 /root/.ssh")
controller.succeed(
"ssh-keygen -t ed25519 -N \'\' -f /root/.ssh/id_ed25519 2>&1"
)
pub_key = controller.succeed("cat /root/.ssh/id_ed25519.pub").strip()
# Inject the generated public key into the shadow host at runtime
shadowhost.succeed("mkdir -p /root/.ssh && chmod 700 /root/.ssh")
shadowhost.succeed(
f"echo '{pub_key}' >> /root/.ssh/authorized_keys && "
"chmod 600 /root/.ssh/authorized_keys"
)
# ------------------------------------------------------------------
# 2. Make sure the shadow host has a writable /etc/login.defs,
# since the test framework expects to be able to write to it.
# ------------------------------------------------------------------
shadowhost.succeed(
"cp --remove-destination $(readlink -f /etc/login.defs) /etc/login.defs && "
"chmod 644 /etc/login.defs"
)
# ------------------------------------------------------------------
# 3. Copy the upstream test suite onto the controller
# ------------------------------------------------------------------
controller.succeed(
"cp -r ${pkgs.shadow.passthru.testFramework} /root/shadow-tests && "
"chmod -R u+w /root/shadow-tests"
)
# ------------------------------------------------------------------
# 4. Write the mhc.yaml topology config
# This tells pytest-mh where the shadow host is and which role
# it plays. The hostname must match the NixOS node name.
# ------------------------------------------------------------------
controller.succeed(textwrap.dedent("""
cat > /root/shadow-tests/mhc.yaml << 'EOF'
domains:
- id: shadow
hosts:
- hostname: ${shadowHostName}
role: shadow
ssh:
user: root
private_key: /root/.ssh/id_ed25519
EOF
"""))
# ------------------------------------------------------------------
# 5. Run the upstream pytest-mh test suite from the controller
# ------------------------------------------------------------------
shadowhost.wait_for_unit("sshd.service")
# gpasswd tests are disabled, since they rely on specific behavior of the gpasswd command that is not applicable to NixOS
controller.succeed(
"cd /root/shadow-tests && "
"${controllerPython}/bin/pytest "
"--mh-config=mhc.yaml "
"--deselect=tests/test_gpasswd.py "
"-v tests/"
)
'';
}
|