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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
{ config, pkgs, lib, ... }:
with lib; let
cfg = config.krebs.exim-retiolum;
# Due to improvements to the JSON notation, braces around top-level objects
# are not necessary^Wsupported by rspamd's parser when including files:
# https://github.com/rspamd/rspamd/issues/2674
toMostlyJSON = value:
assert typeOf value == "set";
(s: substring 1 (stringLength s - 2) s)
(toJSON value);
to-lsearch = concatMapStrings ({ from, to, ... }: "${from}: ${to}\n");
lsearch = mapAttrs (name: set: toFile name (to-lsearch set)) ({
inherit (cfg) system-aliases;
});
in {
options.krebs.exim-retiolum = {
enable = mkEnableOption "krebs.exim-retiolum";
local_domains = mkOption {
type = with types; listOf hostname;
default = ["localhost"] ++ config.krebs.build.host.nets.retiolum.aliases;
};
primary_hostname = mkOption {
type = types.str;
default = let x = "${config.krebs.build.host.name}.r"; in
assert elem x config.krebs.build.host.nets.retiolum.aliases;
x;
};
relay_to_domains = mkOption {
# TODO hostname with wildcards
type = with types; listOf str;
default = [
"*.r"
];
};
rspamd = {
enable = mkEnableOption "krebs.exim-retiolum.rspamd" // {
default = false;
};
locals = {
logging = {
level = mkOption {
type = types.enum [
"error"
"warning"
"notice"
"info"
"debug"
"silent"
];
default = "notice";
};
};
options = {
local_networks = mkOption {
type = types.listOf types.cidr;
default = [
config.krebs.build.host.nets.retiolum.ip4.prefix
config.krebs.build.host.nets.retiolum.ip6.prefix
];
};
};
};
};
system-aliases = mkOption {
type = types.listOf (types.submodule ({
options = {
from = mkOption {
type = types.str; # TODO e-mail address
};
to = mkOption {
type = types.str; # TODO e-mail address / TODO listOf
};
};
}));
default = [];
};
};
imports = [
{
config = lib.mkIf cfg.rspamd.enable {
services.rspamd.enable = true;
services.rspamd.locals =
mapAttrs'
(name: value: nameValuePair "${name}.inc" {
text = toMostlyJSON value;
})
cfg.rspamd.locals;
users.users.${config.krebs.exim.user.name}.extraGroups = [
config.services.rspamd.group
];
};
}
];
config = lib.mkIf cfg.enable {
krebs.exim = {
enable = true;
config =
# This configuration makes only sense for retiolum-enabled hosts.
# TODO modular configuration
assert config.krebs.tinc.retiolum.enable;
/* exim */ ''
keep_environment =
primary_hostname = ${cfg.primary_hostname}
domainlist local_domains = ${concatStringsSep ":" cfg.local_domains}
domainlist relay_to_domains = ${concatStringsSep ":" cfg.relay_to_domains}
${optionalString cfg.rspamd.enable /* exim */ ''
spamd_address = /run/rspamd/rspamd.sock variant=rspamd
''}
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
host_lookup = *
rfc1413_hosts = *
rfc1413_query_timeout = 5s
log_file_path = syslog
syslog_timestamp = false
syslog_duplication = false
tls_advertise_hosts =
begin acl
acl_check_rcpt:
deny
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
message = restricted characters in address
accept
domains = +local_domains : +relay_to_domains
deny
message = relay not permitted
acl_check_data:
${optionalString cfg.rspamd.enable /* exim */ ''
accept condition = ''${if eq{$interface_port}{587}}
warn remove_header = ${concatStringsSep " : " [
"x-spam"
"x-spam-report"
"x-spam-score"
]}
warn
spam = nobody:true
warn
condition = ''${if !eq{$spam_action}{no action}}
add_header = X-Spam: Yes
add_header = X-Spam-Report: $spam_report
add_header = X-Spam-Score: $spam_score
''}
accept
begin routers
system_aliases:
debug_print = "R: system_aliases for $local_part@$domain"
driver = redirect
data = ''${lookup{$local_part}lsearch{${lsearch.system-aliases}}}
local:
driver = accept
domains = +local_domains
check_local_user
# local_part_suffix = +*
# local_part_suffix_optional
transport = home_maildir
remote:
driver = manualroute
domains = +relay_to_domains
transport = remote_smtp
route_list = ^.* $0 byname
begin transports
remote_smtp:
driver = smtp
home_maildir:
driver = appendfile
maildir_format
directory = $home/Maildir
directory_mode = 0700
delivery_date_add
envelope_to_add
return_path_add
# group = mail
# mode = 0660
begin retry
${concatMapStringsSep "\n" (k: "${k} * F,42d,1m") cfg.relay_to_domains}
* * F,2h,15m; G,16h,1h,1.5; F,4d,6h
begin rewrite
begin authenticators
'';
};
};
}
|