about summary refs log tree commit diff
path: root/nixos/modules/services/mail/spamassassin.nix
blob: fbe4cc82adb935393ed73b4edb98f1da6585c3cd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
{ config, lib, pkgs, ... }:
let
  cfg = config.services.spamassassin;
  spamassassin-local-cf = pkgs.writeText "local.cf" cfg.config;

in

{
  options = {

    services.spamassassin = {
      enable = lib.mkEnableOption "the SpamAssassin daemon";

      debug = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = "Whether to run the SpamAssassin daemon in debug mode";
      };

      config = lib.mkOption {
        type = lib.types.lines;
        description = ''
          The SpamAssassin local.cf config

          If you are using this configuration:

              add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_

          Then you can Use this sieve filter:

              require ["fileinto", "reject", "envelope"];

              if header :contains "X-Spam-Flag" "YES" {
                fileinto "spam";
              }

          Or this procmail filter:

              :0:
              * ^X-Spam-Flag: YES
              /var/vpopmail/domains/lastlog.de/js/.maildir/.spam/new

          To filter your messages based on the additional mail headers added by spamassassin.
        '';
        example = ''
          #rewrite_header Subject [***** SPAM _SCORE_ *****]
          required_score          5.0
          use_bayes               1
          bayes_auto_learn        1
          add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_
        '';
        default = "";
      };

      initPreConf = lib.mkOption {
        type = with lib.types; either str path;
        description = "The SpamAssassin init.pre config.";
        apply = val: if builtins.isPath val then val else pkgs.writeText "init.pre" val;
        default =
        ''
          #
          # to update this list, run this command in the rules directory:
          # grep 'loadplugin.*Mail::SpamAssassin::Plugin::.*' -o -h * | sort | uniq
          #

          #loadplugin Mail::SpamAssassin::Plugin::AccessDB
          #loadplugin Mail::SpamAssassin::Plugin::AntiVirus
          loadplugin Mail::SpamAssassin::Plugin::AskDNS
          # loadplugin Mail::SpamAssassin::Plugin::ASN
          loadplugin Mail::SpamAssassin::Plugin::AutoLearnThreshold
          #loadplugin Mail::SpamAssassin::Plugin::AWL
          loadplugin Mail::SpamAssassin::Plugin::Bayes
          loadplugin Mail::SpamAssassin::Plugin::BodyEval
          loadplugin Mail::SpamAssassin::Plugin::Check
          #loadplugin Mail::SpamAssassin::Plugin::DCC
          loadplugin Mail::SpamAssassin::Plugin::DKIM
          loadplugin Mail::SpamAssassin::Plugin::DMARC
          loadplugin Mail::SpamAssassin::Plugin::DNSEval
          loadplugin Mail::SpamAssassin::Plugin::FreeMail
          loadplugin Mail::SpamAssassin::Plugin::HeaderEval
          loadplugin Mail::SpamAssassin::Plugin::HTMLEval
          loadplugin Mail::SpamAssassin::Plugin::HTTPSMismatch
          loadplugin Mail::SpamAssassin::Plugin::ImageInfo
          loadplugin Mail::SpamAssassin::Plugin::MIMEEval
          loadplugin Mail::SpamAssassin::Plugin::MIMEHeader
          # loadplugin Mail::SpamAssassin::Plugin::PDFInfo
          #loadplugin Mail::SpamAssassin::Plugin::PhishTag
          loadplugin Mail::SpamAssassin::Plugin::Pyzor
          loadplugin Mail::SpamAssassin::Plugin::Razor2
          # loadplugin Mail::SpamAssassin::Plugin::RelayCountry
          loadplugin Mail::SpamAssassin::Plugin::RelayEval
          loadplugin Mail::SpamAssassin::Plugin::ReplaceTags
          # loadplugin Mail::SpamAssassin::Plugin::Rule2XSBody
          # loadplugin Mail::SpamAssassin::Plugin::Shortcircuit
          loadplugin Mail::SpamAssassin::Plugin::SpamCop
          loadplugin Mail::SpamAssassin::Plugin::SPF
          #loadplugin Mail::SpamAssassin::Plugin::TextCat
          # loadplugin Mail::SpamAssassin::Plugin::TxRep
          loadplugin Mail::SpamAssassin::Plugin::URIDetail
          loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
          loadplugin Mail::SpamAssassin::Plugin::URIEval
          # loadplugin Mail::SpamAssassin::Plugin::URILocalBL
          loadplugin Mail::SpamAssassin::Plugin::VBounce
          loadplugin Mail::SpamAssassin::Plugin::WhiteListSubject
          loadplugin Mail::SpamAssassin::Plugin::WLBLEval
        '';
      };
    };
  };

  config = lib.mkIf cfg.enable {
    environment.etc."mail/spamassassin/init.pre".source = cfg.initPreConf;
    environment.etc."mail/spamassassin/local.cf".source = spamassassin-local-cf;

    # Allow users to run 'spamc'.
    environment.systemPackages = [ pkgs.spamassassin ];

    users.users.spamd = {
      description = "Spam Assassin Daemon";
      uid = config.ids.uids.spamd;
      group = "spamd";
    };

    users.groups.spamd = {
      gid = config.ids.gids.spamd;
    };

    systemd.services.sa-update = {
      # Needs to be able to contact the update server.
      wants = [ "network-online.target" ];
      after = [ "network-online.target" ];

      serviceConfig = {
        Type = "oneshot";
        User = "spamd";
        Group = "spamd";
        StateDirectory = "spamassassin";
        ExecStartPost = "+${config.systemd.package}/bin/systemctl -q --no-block try-reload-or-restart spamd.service";
      };

      script = ''
        set +e
        ${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=/var/lib/spamassassin/sa-update-keys/
        rc=$?
        set -e

        if [[ $rc -gt 1 ]]; then
          # sa-update failed.
          exit $rc
        fi

        if [[ $rc -eq 1 ]]; then
          # No update was available, exit successfully.
          exit 0
        fi

        # An update was available and installed. Compile the rules.
        ${pkgs.spamassassin}/bin/sa-compile
      '';
    };

    systemd.timers.sa-update = {
      description = "sa-update-service";
      partOf      = [ "sa-update.service" ];
      wantedBy    = [ "timers.target" ];
      timerConfig = {
        OnCalendar = "1:*";
        Persistent = true;
      };
    };

    systemd.services.spamd = {
      description = "SpamAssassin Server";

      wantedBy = [ "multi-user.target" ];
      wants = [ "sa-update.service" ];
      after = [
        "network.target"
        "sa-update.service"
      ];

      serviceConfig = {
        User = "spamd";
        Group = "spamd";
        ExecStart = "+${pkgs.spamassassin}/bin/spamd ${lib.optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=%S/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid";
        ExecReload = "+${pkgs.coreutils}/bin/kill -HUP $MAINPID";
        StateDirectory = "spamassassin";
      };
    };
  };
}