]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - modules/private/mail/relay.nix
Use ldap instead of mysql to fetch mailboxes
[perso/Immae/Config/Nix.git] / modules / private / mail / relay.nix
1 { lib, pkgs, config, nodes, name, ... }:
2 {
3 config = lib.mkIf config.myServices.mailBackup.enable {
4 security.acme.certs."mail" = config.myServices.certificates.certConfig // {
5 postRun = ''
6 systemctl restart postfix.service
7 '';
8 domain = config.hostEnv.fqdn;
9 extraDomains = let
10 zonesWithMx = builtins.filter (zone:
11 lib.attrsets.hasAttr "withEmail" zone && lib.lists.length zone.withEmail > 0
12 ) config.myEnv.dns.masterZones;
13 mxs = map (zone: "${config.myEnv.servers."${name}".mx.subdomain}.${zone.name}") zonesWithMx;
14 in builtins.listToAttrs (map (mx: lib.attrsets.nameValuePair mx null) mxs);
15 };
16 secrets.keys = [
17 {
18 dest = "postfix/mysql_alias_maps";
19 user = config.services.postfix.user;
20 group = config.services.postfix.group;
21 permissions = "0440";
22 text = ''
23 # We need to specify that option to trigger ssl connection
24 tls_ciphers = TLSv1.2
25 user = ${config.myEnv.mail.postfix.mysql.user}
26 password = ${config.myEnv.mail.postfix.mysql.password}
27 hosts = ${config.myEnv.mail.postfix.mysql.remoteHost}
28 dbname = ${config.myEnv.mail.postfix.mysql.database}
29 query = SELECT DISTINCT 1
30 FROM forwardings
31 WHERE
32 ((regex = 1 AND '%s' REGEXP CONCAT('^',source,'$') ) OR (regex = 0 AND source = '%s'))
33 AND active = 1
34 AND '%s' NOT IN
35 (
36 SELECT source
37 FROM forwardings_blacklisted
38 WHERE source = '%s'
39 ) UNION
40 SELECT 'devnull@immae.eu'
41 FROM forwardings_blacklisted
42 WHERE source = '%s'
43 '';
44 }
45 {
46 dest = "postfix/ldap_mailboxes";
47 user = config.services.postfix.user;
48 group = config.services.postfix.group;
49 permissions = "0440";
50 text = ''
51 server_host = ldaps://${config.myEnv.mail.dovecot.ldap.host}:636
52 search_base = ${config.myEnv.mail.dovecot.ldap.base}
53 query_filter = ${config.myEnv.mail.dovecot.ldap.postfix_mailbox_filter}
54 bind_dn = ${config.myEnv.mail.dovecot.ldap.dn}
55 bind_pw = ${config.myEnv.mail.dovecot.ldap.password}
56 result_attribute = immaePostfixAddress
57 result_format = dummy
58 version = 3
59 '';
60 }
61 {
62 dest = "postfix/sympa_mailbox_maps";
63 user = config.services.postfix.user;
64 group = config.services.postfix.group;
65 permissions = "0440";
66 text = ''
67 hosts = ${config.myEnv.mail.sympa.postgresql.host}
68 user = ${config.myEnv.mail.sympa.postgresql.user}
69 password = ${config.myEnv.mail.sympa.postgresql.password}
70 dbname = ${config.myEnv.mail.sympa.postgresql.database}
71 query = SELECT DISTINCT 1 FROM list_table WHERE '%s' IN (
72 CONCAT(name_list, '@', robot_list),
73 CONCAT(name_list, '-request@', robot_list),
74 CONCAT(name_list, '-editor@', robot_list),
75 CONCAT(name_list, '-unsubscribe@', robot_list),
76 CONCAT(name_list, '-owner@', robot_list),
77 CONCAT('sympa-request@', robot_list),
78 CONCAT('sympa-owner@', robot_list),
79 CONCAT('sympa@', robot_list),
80 CONCAT('listmaster@', robot_list),
81 CONCAT('bounce@', robot_list),
82 CONCAT('abuse-feedback-report@', robot_list)
83 )
84 '';
85 }
86 {
87 dest = "postfix/ldap_ejabberd_users_immae_fr";
88 user = config.services.postfix.user;
89 group = config.services.postfix.group;
90 permissions = "0440";
91 text = ''
92 server_host = ldaps://${config.myEnv.jabber.ldap.host}:636
93 search_base = ${config.myEnv.jabber.ldap.base}
94 query_filter = ${config.myEnv.jabber.postfix_user_filter}
95 domain = immae.fr
96 bind_dn = ${config.myEnv.jabber.ldap.dn}
97 bind_pw = ${config.myEnv.jabber.ldap.password}
98 result_attribute = immaeXmppUid
99 result_format = ejabberd@localhost
100 version = 3
101 '';
102 }
103 ];
104
105 networking.firewall.allowedTCPPorts = [ 25 ];
106
107 users.users."${config.services.postfix.user}".extraGroups = [ "keys" ];
108 services.filesWatcher.postfix = {
109 restart = true;
110 paths = [
111 config.secrets.fullPaths."postfix/mysql_alias_maps"
112 config.secrets.fullPaths."postfix/sympa_mailbox_maps"
113 config.secrets.fullPaths."postfix/ldap_ejabberd_users_immae_fr"
114 config.secrets.fullPaths."postfix/ldap_mailboxes"
115 ];
116 };
117 services.postfix = {
118 mapFiles = let
119 recipient_maps = let
120 name = n: i: "relay_${n}_${toString i}";
121 pair = n: i: m: lib.attrsets.nameValuePair (name n i) (
122 if m.type == "hash"
123 then pkgs.writeText (name n i) m.content
124 else null
125 );
126 pairs = n: v: lib.imap1 (i: m: pair n i m) v.recipient_maps;
127 in lib.attrsets.filterAttrs (k: v: v != null) (
128 lib.attrsets.listToAttrs (lib.flatten (
129 lib.attrsets.mapAttrsToList pairs config.myEnv.mail.postfix.backup_domains
130 ))
131 );
132 relay_restrictions = lib.attrsets.filterAttrs (k: v: v != null) (
133 lib.attrsets.mapAttrs' (n: v:
134 lib.attrsets.nameValuePair "recipient_access_${n}" (
135 if lib.attrsets.hasAttr "relay_restrictions" v
136 then pkgs.writeText "recipient_access_${n}" v.relay_restrictions
137 else null
138 )
139 ) config.myEnv.mail.postfix.backup_domains
140 );
141 virtual_map = {
142 virtual = let
143 cfg = config.myEnv.monitoring.email_check.eldiron;
144 address = "${cfg.mail_address}@${cfg.mail_domain}";
145 in pkgs.writeText "postfix-virtual" (
146 builtins.concatStringsSep "\n" (
147 ["${address} 1"] ++
148 lib.attrsets.mapAttrsToList (
149 n: v: lib.optionalString v.external ''
150 script_${n}@mail.immae.eu 1
151 ''
152 ) config.myEnv.mail.scripts
153 )
154 );
155 };
156 in
157 recipient_maps // relay_restrictions // virtual_map;
158 config = {
159 ### postfix module overrides
160 readme_directory = "${pkgs.postfix}/share/postfix/doc";
161 smtp_tls_CAfile = lib.mkForce "";
162 smtp_tls_cert_file = lib.mkForce "";
163 smtp_tls_key_file = lib.mkForce "";
164
165 message_size_limit = "1073741824"; # Don't put 0 here, it's not equivalent to "unlimited"
166 mailbox_size_limit = "1073741825"; # Workaround, local delivered mails should all go through scripts
167 alias_database = "\$alias_maps";
168
169 ### Relay domains
170 relay_domains = let
171 backups = lib.flatten (lib.attrsets.mapAttrsToList (n: v: v.domains or []) config.myEnv.mail.postfix.backup_domains);
172 virtual_domains = config.myEnv.mail.postfix.additional_mailbox_domains
173 ++ lib.remove null (lib.flatten (map
174 (zone: map
175 (e: if e.receive
176 then "${e.domain}${lib.optionalString (e.domain != "") "."}${zone.name}"
177 else null
178 )
179 (zone.withEmail or [])
180 )
181 config.myEnv.dns.masterZones
182 ));
183 in
184 backups ++ virtual_domains;
185 relay_recipient_maps = let
186 backup_recipients = lib.flatten (lib.attrsets.mapAttrsToList (n: v:
187 lib.imap1 (i: m: "${m.type}:/etc/postfix/relay_${n}_${toString i}") v.recipient_maps
188 ) config.myEnv.mail.postfix.backup_domains);
189 virtual_alias_maps = [
190 "hash:/etc/postfix/virtual"
191 "mysql:${config.secrets.fullPaths."postfix/mysql_alias_maps"}"
192 "ldap:${config.secrets.fullPaths."postfix/ldap_ejabberd_users_immae_fr"}"
193 ];
194 virtual_mailbox_maps = [
195 "ldap:${config.secrets.fullPaths."postfix/ldap_mailboxes"}"
196 "pgsql:${config.secrets.fullPaths."postfix/sympa_mailbox_maps"}"
197 ];
198 in
199 backup_recipients ++ virtual_alias_maps ++ virtual_mailbox_maps;
200 smtpd_relay_restrictions = [
201 "defer_unauth_destination"
202 ] ++ lib.flatten (lib.attrsets.mapAttrsToList (n: v:
203 if lib.attrsets.hasAttr "relay_restrictions" v
204 then [ "check_recipient_access hash:/etc/postfix/recipient_access_${n}" ]
205 else []
206 ) config.myEnv.mail.postfix.backup_domains);
207
208 ### Additional smtpd configuration
209 smtpd_tls_received_header = "yes";
210 smtpd_tls_loglevel = "1";
211
212 ### Email sending configuration
213 smtp_tls_security_level = "may";
214 smtp_tls_loglevel = "1";
215
216 ### Force ip bind for smtp
217 smtp_bind_address = config.myEnv.servers."${name}".ips.main.ip4;
218 smtp_bind_address6 = builtins.head config.myEnv.servers."${name}".ips.main.ip6;
219
220 smtpd_milters = [
221 "unix:${config.myServices.mail.milters.sockets.opendkim}"
222 "unix:${config.myServices.mail.milters.sockets.openarc}"
223 "unix:${config.myServices.mail.milters.sockets.opendmarc}"
224 ];
225 };
226 enable = true;
227 enableSmtp = true;
228 enableSubmission = false;
229 destination = ["localhost"];
230 # This needs to reverse DNS
231 hostname = config.hostEnv.fqdn;
232 setSendmail = false;
233 sslCert = "/var/lib/acme/mail/fullchain.pem";
234 sslKey = "/var/lib/acme/mail/key.pem";
235 recipientDelimiter = "+";
236 };
237 };
238 }
239