1{ pkgs, ... }:
2let
3 certs = import ./common/acme/server/snakeoil-certs.nix;
4 domain = certs.domain;
5in
6{
7 name = "rspamd-trainer";
8 meta = with pkgs.lib.maintainers; {
9 maintainers = [ onny ];
10 };
11
12 nodes = {
13 machine =
14 { options, config, ... }:
15 {
16
17 security.pki.certificateFiles = [
18 certs.ca.cert
19 ];
20
21 networking.extraHosts = ''
22 127.0.0.1 ${domain}
23 '';
24
25 services.rspamd-trainer = {
26 enable = true;
27 settings = {
28 HOST = domain;
29 USERNAME = "spam@${domain}";
30 INBOXPREFIX = "INBOX/";
31 };
32 secrets = [
33 # Do not use this in production. This will make passwords
34 # world-readable in the Nix store
35 "${pkgs.writeText "secrets" ''
36 PASSWORD = test123
37 ''}"
38 ];
39 };
40
41 services.maddy = {
42 enable = true;
43 hostname = domain;
44 primaryDomain = domain;
45 ensureAccounts = [ "spam@${domain}" ];
46 ensureCredentials = {
47 # Do not use this in production. This will make passwords world-readable
48 # in the Nix store
49 "spam@${domain}".passwordFile = "${pkgs.writeText "postmaster" "test123"}";
50 };
51 tls = {
52 loader = "file";
53 certificates = [
54 {
55 certPath = "${certs.${domain}.cert}";
56 keyPath = "${certs.${domain}.key}";
57 }
58 ];
59 };
60 config =
61 builtins.replaceStrings
62 [
63 "imap tcp://0.0.0.0:143"
64 "submission tcp://0.0.0.0:587"
65 ]
66 [
67 "imap tls://0.0.0.0:993 tcp://0.0.0.0:143"
68 "submission tls://0.0.0.0:465 tcp://0.0.0.0:587"
69 ]
70 options.services.maddy.config.default;
71 };
72
73 services.rspamd = {
74 enable = true;
75 locals = {
76 "redis.conf".text = ''
77 servers = "${config.services.redis.servers.rspamd.unixSocket}";
78 '';
79 "classifier-bayes.conf".text = ''
80 backend = "redis";
81 autolearn = true;
82 '';
83 };
84 };
85
86 services.redis.servers.rspamd = {
87 enable = true;
88 port = 0;
89 unixSocket = "/run/redis-rspamd/redis.sock";
90 user = config.services.rspamd.user;
91 };
92
93 environment.systemPackages = [
94 (pkgs.writers.writePython3Bin "send-testmail" { } ''
95 import smtplib
96 import ssl
97 from email.mime.text import MIMEText
98 context = ssl.create_default_context()
99 msg = MIMEText("Hello World")
100 msg['Subject'] = 'Test'
101 msg['From'] = "spam@${domain}"
102 msg['To'] = "spam@${domain}"
103 with smtplib.SMTP_SSL(host='${domain}', port=465, context=context) as smtp:
104 smtp.login('spam@${domain}', 'test123')
105 smtp.sendmail(
106 'spam@${domain}', 'spam@${domain}', msg.as_string()
107 )
108 '')
109 (pkgs.writers.writePython3Bin "create-mail-dirs" { } ''
110 import imaplib
111 with imaplib.IMAP4_SSL('${domain}') as imap:
112 imap.login('spam@${domain}', 'test123')
113 imap.create("\"INBOX/report_spam\"")
114 imap.create("\"INBOX/report_ham\"")
115 imap.create("\"INBOX/report_spam_reply\"")
116 imap.select("INBOX")
117 imap.copy("1", "\"INBOX/report_ham\"")
118 imap.logout()
119 '')
120 (pkgs.writers.writePython3Bin "test-imap" { } ''
121 import imaplib
122 with imaplib.IMAP4_SSL('${domain}') as imap:
123 imap.login('spam@${domain}', 'test123')
124 imap.select("INBOX/learned_ham")
125 status, refs = imap.search(None, 'ALL')
126 assert status == 'OK'
127 assert len(refs) == 1
128 status, msg = imap.fetch(refs[0], 'BODY[TEXT]')
129 assert status == 'OK'
130 assert msg[0][1].strip() == b"Hello World"
131 imap.logout()
132 '')
133 ];
134
135 };
136
137 };
138
139 testScript =
140 { nodes }:
141 ''
142 start_all()
143 machine.wait_for_unit("maddy.service")
144 machine.wait_for_open_port(143)
145 machine.wait_for_open_port(993)
146 machine.wait_for_open_port(587)
147 machine.wait_for_open_port(465)
148
149 # Send test mail to spam@domain
150 machine.succeed("send-testmail")
151
152 # Create mail directories required for rspamd-trainer and copy mail from
153 # INBOX into INBOX/report_ham
154 machine.succeed("create-mail-dirs")
155
156 # Start rspamd-trainer. It should read mail from INBOX/report_ham
157 machine.wait_for_unit("rspamd.service")
158 machine.wait_for_unit("redis-rspamd.service")
159 machine.wait_for_file("/run/rspamd/rspamd.sock")
160 machine.succeed("systemctl start rspamd-trainer.service")
161
162 # Check if mail got processed by rspamd-trainer successfully and check for
163 # it in INBOX/learned_ham
164 machine.succeed("test-imap")
165 '';
166}