1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 rspamdCfg = config.services.rspamd;
8 postfixCfg = config.services.postfix;
9 cfg = config.services.rmilter;
10
11 inetSocket = addr: port: "inet:[${toString port}@${addr}]";
12 unixSocket = sock: "unix:${sock}";
13
14 systemdSocket = if cfg.bindSocket.type == "unix" then cfg.bindSocket.path
15 else "${cfg.bindSocket.address}:${toString cfg.bindSocket.port}";
16 rmilterSocket = if cfg.bindSocket.type == "unix" then unixSocket cfg.bindSocket.path
17 else inetSocket cfg.bindSocket.address cfg.bindSocket.port;
18
19 rmilterConf = ''
20 pidfile = /run/rmilter/rmilter.pid;
21 bind_socket = ${if cfg.socketActivation then "fd:3" else rmilterSocket};
22 tempdir = /tmp;
23 '' + (with cfg.rspamd; if enable then ''
24 spamd {
25 servers = ${concatStringsSep ", " servers};
26 connect_timeout = 1s;
27 results_timeout = 20s;
28 error_time = 10;
29 dead_time = 300;
30 maxerrors = 10;
31 reject_message = "${rejectMessage}";
32 ${optionalString (length whitelist != 0) "whitelist = ${concatStringsSep ", " whitelist};"}
33
34 # rspamd_metric - metric for using with rspamd
35 # Default: "default"
36 rspamd_metric = "default";
37 ${extraConfig}
38 };
39 '' else "") + cfg.extraConfig;
40
41 rmilterConfigFile = pkgs.writeText "rmilter.conf" rmilterConf;
42
43in
44
45{
46
47 ###### interface
48
49 options = {
50
51 services.rmilter = {
52
53 enable = mkOption {
54 type = types.bool;
55 default = cfg.rspamd.enable;
56 description = "Whether to run the rmilter daemon.";
57 };
58
59 debug = mkOption {
60 type = types.bool;
61 default = false;
62 description = "Whether to run the rmilter daemon in debug mode.";
63 };
64
65 user = mkOption {
66 type = types.string;
67 default = "rmilter";
68 description = ''
69 User to use when no root privileges are required.
70 '';
71 };
72
73 group = mkOption {
74 type = types.string;
75 default = "rmilter";
76 description = ''
77 Group to use when no root privileges are required.
78 '';
79 };
80
81 bindSocket.type = mkOption {
82 type = types.enum [ "unix" "inet" ];
83 default = "unix";
84 description = ''
85 What kind of socket rmilter should listen on. Either "unix"
86 for an Unix domain socket or "inet" for a TCP socket.
87 '';
88 };
89
90 bindSocket.path = mkOption {
91 type = types.str;
92 default = "/run/rmilter/rmilter.sock";
93 description = ''
94 Path to Unix domain socket to listen on.
95 '';
96 };
97
98 bindSocket.address = mkOption {
99 type = types.str;
100 default = "::1";
101 example = "0.0.0.0";
102 description = ''
103 Inet address to listen on.
104 '';
105 };
106
107 bindSocket.port = mkOption {
108 type = types.int;
109 default = 11990;
110 description = ''
111 Inet port to listen on.
112 '';
113 };
114
115 socketActivation = mkOption {
116 type = types.bool;
117 default = true;
118 description = ''
119 Enable systemd socket activation for rmilter.
120
121 Disabling socket activation is not recommended when a Unix
122 domain socket is used and could lead to incorrect
123 permissions.
124 '';
125 };
126
127 rspamd = {
128 enable = mkOption {
129 type = types.bool;
130 default = rspamdCfg.enable;
131 description = "Whether to use rspamd to filter mails";
132 };
133
134 servers = mkOption {
135 type = types.listOf types.str;
136 default = ["r:/run/rspamd/rspamd.sock"];
137 description = ''
138 Spamd socket definitions.
139 Is server name is prefixed with r: it is rspamd server.
140 '';
141 };
142
143 whitelist = mkOption {
144 type = types.listOf types.str;
145 default = [ ];
146 description = "list of ips or nets that should be not checked with spamd";
147 };
148
149 rejectMessage = mkOption {
150 type = types.str;
151 default = "Spam message rejected; If this is not spam contact abuse";
152 description = "reject message for spam";
153 };
154
155 extraConfig = mkOption {
156 type = types.lines;
157 default = "";
158 description = "Custom snippet to append to end of `spamd' section";
159 };
160 };
161
162 extraConfig = mkOption {
163 type = types.lines;
164 default = "";
165 description = "Custom snippet to append to rmilter config";
166 };
167
168 postfix = {
169 enable = mkOption {
170 type = types.bool;
171 default = false;
172 description = "Add rmilter to postfix main.conf";
173 };
174
175 configFragment = mkOption {
176 type = types.str;
177 description = "Addon to postfix configuration";
178 default = ''
179 smtpd_milters = ${rmilterSocket}
180 milter_protocol = 6
181 milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
182 '';
183 };
184 };
185
186 };
187
188 };
189
190
191 ###### implementation
192
193 config = mkMerge [
194
195 (mkIf cfg.enable {
196
197 users.users = singleton {
198 name = cfg.user;
199 description = "rmilter daemon";
200 uid = config.ids.uids.rmilter;
201 group = cfg.group;
202 };
203
204 users.groups = singleton {
205 name = cfg.group;
206 gid = config.ids.gids.rmilter;
207 };
208
209 systemd.services.rmilter = {
210 description = "Rmilter Service";
211
212 wantedBy = [ "multi-user.target" ];
213 after = [ "network.target" ];
214
215 serviceConfig = {
216 ExecStart = "${pkgs.rmilter}/bin/rmilter ${optionalString cfg.debug "-d"} -n -c ${rmilterConfigFile}";
217 ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
218 User = cfg.user;
219 Group = cfg.group;
220 PermissionsStartOnly = true;
221 Restart = "always";
222 RuntimeDirectory = "rmilter";
223 RuntimeDirectoryMode = "0750";
224 };
225
226 };
227
228 systemd.sockets.rmilter = mkIf cfg.socketActivation {
229 description = "Rmilter service socket";
230 wantedBy = [ "sockets.target" ];
231 socketConfig = {
232 ListenStream = systemdSocket;
233 SocketUser = cfg.user;
234 SocketGroup = cfg.group;
235 SocketMode = "0660";
236 };
237 };
238 })
239
240 (mkIf (cfg.enable && cfg.rspamd.enable && rspamdCfg.enable) {
241 users.users.${cfg.user}.extraGroups = [ rspamdCfg.group ];
242 })
243
244 (mkIf (cfg.enable && cfg.postfix.enable) {
245 services.postfix.extraConfig = cfg.postfix.configFragment;
246 users.users.${postfixCfg.user}.extraGroups = [ cfg.group ];
247 })
248 ];
249}