1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.spamassassin;
7 spamassassin-local-cf = pkgs.writeText "local.cf" cfg.config;
8
9in
10
11{
12 options = {
13
14 services.spamassassin = {
15 enable = mkEnableOption (lib.mdDoc "the SpamAssassin daemon");
16
17 debug = mkOption {
18 type = types.bool;
19 default = false;
20 description = lib.mdDoc "Whether to run the SpamAssassin daemon in debug mode";
21 };
22
23 config = mkOption {
24 type = types.lines;
25 description = lib.mdDoc ''
26 The SpamAssassin local.cf config
27
28 If you are using this configuration:
29
30 add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_
31
32 Then you can Use this sieve filter:
33
34 require ["fileinto", "reject", "envelope"];
35
36 if header :contains "X-Spam-Flag" "YES" {
37 fileinto "spam";
38 }
39
40 Or this procmail filter:
41
42 :0:
43 * ^X-Spam-Flag: YES
44 /var/vpopmail/domains/lastlog.de/js/.maildir/.spam/new
45
46 To filter your messages based on the additional mail headers added by spamassassin.
47 '';
48 example = ''
49 #rewrite_header Subject [***** SPAM _SCORE_ *****]
50 required_score 5.0
51 use_bayes 1
52 bayes_auto_learn 1
53 add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_
54 '';
55 default = "";
56 };
57
58 initPreConf = mkOption {
59 type = with types; either str path;
60 description = lib.mdDoc "The SpamAssassin init.pre config.";
61 apply = val: if builtins.isPath val then val else pkgs.writeText "init.pre" val;
62 default =
63 ''
64 #
65 # to update this list, run this command in the rules directory:
66 # grep 'loadplugin.*Mail::SpamAssassin::Plugin::.*' -o -h * | sort | uniq
67 #
68
69 #loadplugin Mail::SpamAssassin::Plugin::AccessDB
70 #loadplugin Mail::SpamAssassin::Plugin::AntiVirus
71 loadplugin Mail::SpamAssassin::Plugin::AskDNS
72 # loadplugin Mail::SpamAssassin::Plugin::ASN
73 loadplugin Mail::SpamAssassin::Plugin::AutoLearnThreshold
74 #loadplugin Mail::SpamAssassin::Plugin::AWL
75 loadplugin Mail::SpamAssassin::Plugin::Bayes
76 loadplugin Mail::SpamAssassin::Plugin::BodyEval
77 loadplugin Mail::SpamAssassin::Plugin::Check
78 #loadplugin Mail::SpamAssassin::Plugin::DCC
79 loadplugin Mail::SpamAssassin::Plugin::DKIM
80 loadplugin Mail::SpamAssassin::Plugin::DNSEval
81 loadplugin Mail::SpamAssassin::Plugin::FreeMail
82 loadplugin Mail::SpamAssassin::Plugin::Hashcash
83 loadplugin Mail::SpamAssassin::Plugin::HeaderEval
84 loadplugin Mail::SpamAssassin::Plugin::HTMLEval
85 loadplugin Mail::SpamAssassin::Plugin::HTTPSMismatch
86 loadplugin Mail::SpamAssassin::Plugin::ImageInfo
87 loadplugin Mail::SpamAssassin::Plugin::MIMEEval
88 loadplugin Mail::SpamAssassin::Plugin::MIMEHeader
89 # loadplugin Mail::SpamAssassin::Plugin::PDFInfo
90 #loadplugin Mail::SpamAssassin::Plugin::PhishTag
91 loadplugin Mail::SpamAssassin::Plugin::Pyzor
92 loadplugin Mail::SpamAssassin::Plugin::Razor2
93 # loadplugin Mail::SpamAssassin::Plugin::RelayCountry
94 loadplugin Mail::SpamAssassin::Plugin::RelayEval
95 loadplugin Mail::SpamAssassin::Plugin::ReplaceTags
96 # loadplugin Mail::SpamAssassin::Plugin::Rule2XSBody
97 # loadplugin Mail::SpamAssassin::Plugin::Shortcircuit
98 loadplugin Mail::SpamAssassin::Plugin::SpamCop
99 loadplugin Mail::SpamAssassin::Plugin::SPF
100 #loadplugin Mail::SpamAssassin::Plugin::TextCat
101 # loadplugin Mail::SpamAssassin::Plugin::TxRep
102 loadplugin Mail::SpamAssassin::Plugin::URIDetail
103 loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
104 loadplugin Mail::SpamAssassin::Plugin::URIEval
105 # loadplugin Mail::SpamAssassin::Plugin::URILocalBL
106 loadplugin Mail::SpamAssassin::Plugin::VBounce
107 loadplugin Mail::SpamAssassin::Plugin::WhiteListSubject
108 loadplugin Mail::SpamAssassin::Plugin::WLBLEval
109 '';
110 };
111 };
112 };
113
114 config = mkIf cfg.enable {
115 environment.etc."mail/spamassassin/init.pre".source = cfg.initPreConf;
116 environment.etc."mail/spamassassin/local.cf".source = spamassassin-local-cf;
117
118 # Allow users to run 'spamc'.
119 environment.systemPackages = [ pkgs.spamassassin ];
120
121 users.users.spamd = {
122 description = "Spam Assassin Daemon";
123 uid = config.ids.uids.spamd;
124 group = "spamd";
125 };
126
127 users.groups.spamd = {
128 gid = config.ids.gids.spamd;
129 };
130
131 systemd.services.sa-update = {
132 # Needs to be able to contact the update server.
133 wants = [ "network-online.target" ];
134 after = [ "network-online.target" ];
135
136 serviceConfig = {
137 Type = "oneshot";
138 User = "spamd";
139 Group = "spamd";
140 StateDirectory = "spamassassin";
141 ExecStartPost = "+${config.systemd.package}/bin/systemctl -q --no-block try-reload-or-restart spamd.service";
142 };
143
144 script = ''
145 set +e
146 ${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=/var/lib/spamassassin/sa-update-keys/
147 rc=$?
148 set -e
149
150 if [[ $rc -gt 1 ]]; then
151 # sa-update failed.
152 exit $rc
153 fi
154
155 if [[ $rc -eq 1 ]]; then
156 # No update was available, exit successfully.
157 exit 0
158 fi
159
160 # An update was available and installed. Compile the rules.
161 ${pkgs.spamassassin}/bin/sa-compile
162 '';
163 };
164
165 systemd.timers.sa-update = {
166 description = "sa-update-service";
167 partOf = [ "sa-update.service" ];
168 wantedBy = [ "timers.target" ];
169 timerConfig = {
170 OnCalendar = "1:*";
171 Persistent = true;
172 };
173 };
174
175 systemd.services.spamd = {
176 description = "SpamAssassin Server";
177
178 wantedBy = [ "multi-user.target" ];
179 wants = [ "sa-update.service" ];
180 after = [
181 "network.target"
182 "sa-update.service"
183 ];
184
185 serviceConfig = {
186 User = "spamd";
187 Group = "spamd";
188 ExecStart = "+${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=%S/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid";
189 ExecReload = "+${pkgs.coreutils}/bin/kill -HUP $MAINPID";
190 StateDirectory = "spamassassin";
191 };
192 };
193 };
194}