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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
{
nixos-lib,
pkgsUnstable,
nixpkgs-unstable,
vhackPackages,
pkgs,
extraModules,
nixLib,
...
}: let
mail_server = import ./nodes/mail_server.nix {inherit extraModules pkgs vhackPackages;};
inherit (mail_server) mkMailServer;
user = import ./nodes/user.nix {inherit pkgs vhackPackages;};
inherit (user) mkUser;
in
nixos-lib.runTest {
hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs
name = "email-dns";
node = {
specialArgs = {inherit pkgsUnstable vhackPackages nixpkgs-unstable nixLib;};
# Use the nixpkgs as constructed by the `nixpkgs.*` options
pkgs = null;
};
nodes = {
acme = {
nodes,
lib,
...
}: {
imports = [
../../../common/acme/server.nix
../../../common/dns/client.nix
];
};
name_server = import ./nodes/name_server.nix {inherit extraModules;};
mail1_server =
mkMailServer "mail1"
{
class = "individual";
name = "bob";
secret = "bob-password";
email = ["bob@bob.com"];
};
mail2_server =
mkMailServer "mail2"
{
class = "individual";
name = "alice";
secret = "alice-password";
email = ["alice@alice.com"];
};
bob = mkUser "bob" "mail1";
alice = mkUser "alice" "mail2";
};
testScript = {...}: let
checkEmailEmpty = pkgs.writeShellScript "assert-empty-emails" ''
set -xe
# fetchmail returns EXIT_CODE 1 when no new mail
fetchmail --verbose >&2 || [ "$?" -eq 1 ] || {
echo "Mail was not empty" >&2
exit 1
}
'';
checkEmailNotEmpty = pkgs.writeShellScript "assert-empty-emails" ''
set -xe
# fetchmail returns EXIT_CODE 1 when no new mail
fetchmail --verbose >&2 || [ "$?" -ne 1 ] || {
echo "No new mail" >&2
exit 1
}
'';
checkSpamEmailNotEmpty = pkgs.writeShellScript "assert-empty-emails" ''
set -xe
# fetchmail returns EXIT_CODE 1 when no new mail
fetchmail --folder JUNK --verbose >&2 || [ "$?" -ne 1 ] || {
echo "No new mail" >&2
exit 1
}
'';
acme_scripts = import ../../../common/acme/scripts.nix {inherit pkgs;};
in
/*
python
*/
''
from time import sleep
# Start dependencies for the other services
acme.start()
acme.wait_for_unit("pebble.service")
name_server.start()
name_server.wait_for_unit("nsd.service")
# Start the actual testing machines
start_all()
mail1_server.wait_for_unit("stalwart-mail.service")
mail1_server.wait_for_open_port(993) # imap
mail1_server.wait_for_open_port(465) # smtp
mail2_server.wait_for_unit("stalwart-mail.service")
mail2_server.wait_for_open_port(993) # imap
mail2_server.wait_for_open_port(465) # smtp
alice.wait_for_unit("multi-user.target")
bob.wait_for_unit("multi-user.target")
name_server.wait_until_succeeds("stat /var/lib/acme/mta-sts.alice.com/cert.pem")
name_server.wait_until_succeeds("stat /var/lib/acme/mta-sts.bob.com/cert.pem")
with subtest("Add pebble ca key to all services"):
for node in [name_server, mail1_server, mail2_server, alice, bob]:
node.succeed("${acme_scripts.add_pebble_acme_ca}")
with subtest("Both mailserver successfully started all services"):
import json
def all_services_running(host):
(status, output) = host.systemctl("list-units --state=failed --plain --no-pager --output=json")
host_failed = json.loads(output)
assert len(host_failed) == 0, f"Expected zero failing services, but found: {json.dumps(host_failed, indent=4)}"
all_services_running(mail1_server)
all_services_running(mail2_server)
with subtest("Both start without mail"):
alice.succeed("sudo -u alice ${checkEmailEmpty}")
bob.succeed("sudo -u bob ${checkEmailEmpty}")
with subtest("Alice can send an empty email to bob"):
alice.succeed("sudo -u alice ${pkgs.writeShellScript "alice-send" ''
set -xe
echo "" | msmtp --debug --account alice bob@bob.com >&2
''}")
# Give `mail2_server` some time to send the email.
sleep(160)
bob.succeed("sudo -u bob ${checkSpamEmailNotEmpty}")
with subtest("Alice can send an non-empty email to bob"):
alice.succeed("sudo -u alice ${pkgs.writeShellScript "alice-send" ''
set -xe
cat << EOF | msmtp --debug --account alice bob@bob.com >&2
Subject: Hi bob, I'm Alice!
Good day, Bob!
This is an email.
It contains a subject and a body.
I also assert utf8 support by including my last name in this very message.
XOXO
Alice van Dåligen.
.
EOF
''}")
# Give `mail2_server` some time to send the email.
sleep(120)
bob.succeed("sudo -u bob ${checkEmailNotEmpty}")
mail1_server.copy_from_vm("/var/lib/", "server1")
mail2_server.copy_from_vm("/var/lib/", "server2")
bob.copy_from_vm("/home/bob/mail", "bob")
'';
}
|