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