1import ./make-test-python.nix {
2 name = "opensmtpd";
3
4 nodes = {
5 smtp1 =
6 { pkgs, ... }:
7 {
8 imports = [ common/user-account.nix ];
9 networking = {
10 firewall.allowedTCPPorts = [ 25 ];
11 useDHCP = false;
12 interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
13 {
14 address = "192.168.1.1";
15 prefixLength = 24;
16 }
17 ];
18 };
19 environment.systemPackages =
20 let
21 testSendmail = pkgs.writeScriptBin "test-sendmail" ''
22 #!/bin/sh
23 set -euxo pipefail
24 echo "========= SENDING" >&2
25 ${pkgs.system-sendmail}/bin/sendmail -v -f alice@smtp1 bob@smtp2 >&2 <<EOF
26 From: alice@smtp1
27 To: bob@smtp2
28 Subject: Sendmail Test
29
30 Hello World
31 EOF
32 echo "=========== FINISHED SENDING" >&2
33 '';
34 in
35 [
36 pkgs.opensmtpd
37 testSendmail
38 ];
39 services.opensmtpd = {
40 enable = true;
41 extraServerArgs = [ "-v" ];
42 serverConfiguration = ''
43 listen on 0.0.0.0
44 action relay_smtp2 relay host "smtp://192.168.1.2"
45 match from any for any action relay_smtp2
46 '';
47 };
48 };
49
50 smtp2 =
51 { pkgs, ... }:
52 {
53 imports = [ common/user-account.nix ];
54 networking = {
55 firewall.allowedTCPPorts = [
56 25
57 143
58 ];
59 useDHCP = false;
60 interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
61 {
62 address = "192.168.1.2";
63 prefixLength = 24;
64 }
65 ];
66 };
67 environment.systemPackages = [ pkgs.opensmtpd ];
68 services.opensmtpd = {
69 enable = true;
70 extraServerArgs = [ "-v" ];
71 serverConfiguration = ''
72 listen on 0.0.0.0
73 action dovecot_deliver mda \
74 "${pkgs.dovecot}/libexec/dovecot/deliver -d %{user.username}"
75 match from any for local action dovecot_deliver
76 '';
77 };
78 services.dovecot2 = {
79 enable = true;
80 enableImap = true;
81 mailLocation = "maildir:~/mail";
82 protocols = [ "imap" ];
83 };
84 };
85
86 client =
87 { pkgs, ... }:
88 {
89 networking = {
90 useDHCP = false;
91 interfaces.eth1.ipv4.addresses = pkgs.lib.mkOverride 0 [
92 {
93 address = "192.168.1.3";
94 prefixLength = 24;
95 }
96 ];
97 };
98 environment.systemPackages =
99 let
100 sendTestMail = pkgs.writeScriptBin "send-a-test-mail" ''
101 #!${pkgs.python3.interpreter}
102 import smtplib, sys
103
104 with smtplib.SMTP('192.168.1.1') as smtp:
105 smtp.sendmail('alice@smtp1', 'bob@smtp2', """
106 From: alice@smtp1
107 To: bob@smtp2
108 Subject: Test
109
110 Hello World
111 """)
112 '';
113
114 checkMailLanded = pkgs.writeScriptBin "check-mail-landed" ''
115 #!${pkgs.python3.interpreter}
116 import imaplib
117
118 with imaplib.IMAP4('192.168.1.2', 143) as imap:
119 imap.login('bob', 'foobar')
120 imap.select()
121 status, refs = imap.search(None, 'ALL')
122 assert status == 'OK'
123 assert len(refs) == 1 and refs[0] != ""
124 status, msg = imap.fetch(refs[0], '(BODY[TEXT])')
125 assert status == 'OK'
126 content = msg[0][1]
127 print("===> content:", content)
128 split = content.split(b'\r\n')
129 print("===> split:", split)
130 split.reverse()
131 lastline = next(filter(lambda x: x != b"", map(bytes.strip, split)))
132 print("===> lastline:", lastline)
133 assert lastline.strip() == b'Hello World'
134 imap.store(refs[0], '+FLAGS', '\\Deleted')
135 imap.expunge()
136 '';
137 in
138 [
139 sendTestMail
140 checkMailLanded
141 ];
142 };
143 };
144
145 testScript = ''
146 start_all()
147
148 client.systemctl("start network-online.target")
149 client.wait_for_unit("network-online.target")
150 smtp1.wait_for_unit("opensmtpd")
151 smtp2.wait_for_unit("opensmtpd")
152 smtp2.wait_for_unit("dovecot2")
153
154 # To prevent sporadic failures during daemon startup, make sure
155 # services are listening on their ports before sending requests
156 smtp1.wait_for_open_port(25)
157 smtp2.wait_for_open_port(25)
158 smtp2.wait_for_open_port(143)
159
160 client.succeed("send-a-test-mail")
161 smtp1.wait_until_fails("smtpctl show queue | egrep .")
162 smtp2.wait_until_fails("smtpctl show queue | egrep .")
163 client.succeed("check-mail-landed >&2")
164
165 smtp1.succeed("test-sendmail")
166 smtp1.wait_until_fails("smtpctl show queue | egrep .")
167 smtp2.wait_until_fails("smtpctl show queue | egrep .")
168 client.succeed("check-mail-landed >&2")
169 '';
170
171 meta.timeout = 1800;
172}