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 spamassassin-init-pre = pkgs.writeText "init.pre" cfg.initPreConf;
9
10 spamdEnv = pkgs.buildEnv {
11 name = "spamd-env";
12 paths = [];
13 postBuild = ''
14 ln -sf ${spamassassin-init-pre} $out/init.pre
15 ln -sf ${spamassassin-local-cf} $out/local.cf
16 '';
17 };
18
19in
20
21{
22 options = {
23
24 services.spamassassin = {
25 enable = mkOption {
26 default = false;
27 description = "Whether to run the SpamAssassin daemon";
28 };
29
30 debug = mkOption {
31 default = false;
32 description = "Whether to run the SpamAssassin daemon in debug mode";
33 };
34
35 config = mkOption {
36 type = types.lines;
37 description = ''
38 The SpamAssassin local.cf config
39
40 If you are using this configuration:
41 add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_
42
43 Then you can Use this sieve filter:
44 require ["fileinto", "reject", "envelope"];
45
46 if header :contains "X-Spam-Flag" "YES" {
47 fileinto "spam";
48 }
49
50 Or this procmail filter:
51 :0:
52 * ^X-Spam-Flag: YES
53 /var/vpopmail/domains/lastlog.de/js/.maildir/.spam/new
54
55 To filter your messages based on the additional mail headers added by spamassassin.
56 '';
57 example = ''
58 #rewrite_header Subject [***** SPAM _SCORE_ *****]
59 required_score 5.0
60 use_bayes 1
61 bayes_auto_learn 1
62 add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_
63 '';
64 default = "";
65 };
66
67 initPreConf = mkOption {
68 type = types.str;
69 description = "The SpamAssassin init.pre config.";
70 default =
71 ''
72 #
73 # to update this list, run this command in the rules directory:
74 # grep 'loadplugin.*Mail::SpamAssassin::Plugin::.*' -o -h * | sort | uniq
75 #
76
77 #loadplugin Mail::SpamAssassin::Plugin::AccessDB
78 #loadplugin Mail::SpamAssassin::Plugin::AntiVirus
79 loadplugin Mail::SpamAssassin::Plugin::AskDNS
80 # loadplugin Mail::SpamAssassin::Plugin::ASN
81 loadplugin Mail::SpamAssassin::Plugin::AutoLearnThreshold
82 #loadplugin Mail::SpamAssassin::Plugin::AWL
83 loadplugin Mail::SpamAssassin::Plugin::Bayes
84 loadplugin Mail::SpamAssassin::Plugin::BodyEval
85 loadplugin Mail::SpamAssassin::Plugin::Check
86 #loadplugin Mail::SpamAssassin::Plugin::DCC
87 loadplugin Mail::SpamAssassin::Plugin::DKIM
88 loadplugin Mail::SpamAssassin::Plugin::DNSEval
89 loadplugin Mail::SpamAssassin::Plugin::FreeMail
90 loadplugin Mail::SpamAssassin::Plugin::Hashcash
91 loadplugin Mail::SpamAssassin::Plugin::HeaderEval
92 loadplugin Mail::SpamAssassin::Plugin::HTMLEval
93 loadplugin Mail::SpamAssassin::Plugin::HTTPSMismatch
94 loadplugin Mail::SpamAssassin::Plugin::ImageInfo
95 loadplugin Mail::SpamAssassin::Plugin::MIMEEval
96 loadplugin Mail::SpamAssassin::Plugin::MIMEHeader
97 # loadplugin Mail::SpamAssassin::Plugin::PDFInfo
98 #loadplugin Mail::SpamAssassin::Plugin::PhishTag
99 loadplugin Mail::SpamAssassin::Plugin::Pyzor
100 loadplugin Mail::SpamAssassin::Plugin::Razor2
101 # loadplugin Mail::SpamAssassin::Plugin::RelayCountry
102 loadplugin Mail::SpamAssassin::Plugin::RelayEval
103 loadplugin Mail::SpamAssassin::Plugin::ReplaceTags
104 # loadplugin Mail::SpamAssassin::Plugin::Rule2XSBody
105 # loadplugin Mail::SpamAssassin::Plugin::Shortcircuit
106 loadplugin Mail::SpamAssassin::Plugin::SpamCop
107 loadplugin Mail::SpamAssassin::Plugin::SPF
108 #loadplugin Mail::SpamAssassin::Plugin::TextCat
109 # loadplugin Mail::SpamAssassin::Plugin::TxRep
110 loadplugin Mail::SpamAssassin::Plugin::URIDetail
111 loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
112 loadplugin Mail::SpamAssassin::Plugin::URIEval
113 # loadplugin Mail::SpamAssassin::Plugin::URILocalBL
114 loadplugin Mail::SpamAssassin::Plugin::VBounce
115 loadplugin Mail::SpamAssassin::Plugin::WhiteListSubject
116 loadplugin Mail::SpamAssassin::Plugin::WLBLEval
117 '';
118 };
119 };
120 };
121
122 config = mkIf cfg.enable {
123
124 # Allow users to run 'spamc'.
125 environment.systemPackages = [ pkgs.spamassassin ];
126
127 users.extraUsers = singleton {
128 name = "spamd";
129 description = "Spam Assassin Daemon";
130 uid = config.ids.uids.spamd;
131 group = "spamd";
132 };
133
134 users.extraGroups = singleton {
135 name = "spamd";
136 gid = config.ids.gids.spamd;
137 };
138
139 systemd.services.sa-update = {
140 script = ''
141 set +e
142 ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/ --siteconfigpath=${spamdEnv}/" spamd
143
144 v=$?
145 set -e
146 if [ $v -gt 1 ]; then
147 echo "sa-update execution error"
148 exit $v
149 fi
150 if [ $v -eq 0 ]; then
151 systemctl reload spamd.service
152 fi
153 '';
154 };
155
156 systemd.timers.sa-update = {
157 description = "sa-update-service";
158 partOf = [ "sa-update.service" ];
159 wantedBy = [ "timers.target" ];
160 timerConfig = {
161 OnCalendar = "1:*";
162 Persistent = true;
163 };
164 };
165
166 systemd.services.spamd = {
167 description = "Spam Assassin Server";
168
169 wantedBy = [ "multi-user.target" ];
170 after = [ "network.target" ];
171
172 serviceConfig = {
173 ExecStart = "${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --siteconfigpath=${spamdEnv} --virtual-config-dir=/var/lib/spamassassin/user-%u --allow-tell --pidfile=/var/run/spamd.pid";
174 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
175 };
176
177 # 0 and 1 no error, exitcode > 1 means error:
178 # https://spamassassin.apache.org/full/3.1.x/doc/sa-update.html#exit_codes
179 preStart = ''
180 # this abstraction requires no centralized config at all
181 if [ -d /etc/spamassassin ]; then
182 echo "This spamassassin does not support global '/etc/spamassassin' folder for configuration as this would be impure. Merge your configs into 'services.spamassassin' and remove the '/etc/spamassassin' folder to make this service work. Also see 'https://github.com/NixOS/nixpkgs/pull/26470'.";
183 exit 1
184 fi
185 echo "Recreating '/var/lib/spamasassin' with creating '3.004001' (or similar) and 'sa-update-keys'"
186 mkdir -p /var/lib/spamassassin
187 chown spamd:spamd /var/lib/spamassassin -R
188 set +e
189 ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/ --siteconfigpath=${spamdEnv}/" spamd
190 v=$?
191 set -e
192 if [ $v -gt 1 ]; then
193 echo "sa-update execution error"
194 exit $v
195 fi
196 chown spamd:spamd /var/lib/spamassassin -R
197 '';
198 };
199 };
200}