1 { lib, pkgs, config, nodes, name, ... }:
2 let all_domains = config.myEnv.mail.postfix.additional_mailbox_domains
3 ++ lib.remove null (lib.flatten (map
6 then "${e.domain}${lib.optionalString (e.domain != "") "."}${zone.name}"
11 config.myEnv.dns.masterZones
15 config = lib.mkIf config.myServices.mailBackup.enable {
16 security.acme.certs."mail" = config.myServices.certificates.certConfig // {
18 systemctl restart postfix.service
20 domain = config.hostEnv.fqdn;
21 extraDomainNames = let
22 zonesWithMx = builtins.filter (zone:
23 lib.attrsets.hasAttr "withEmail" zone && lib.lists.length zone.withEmail > 0
24 ) config.myEnv.dns.masterZones;
25 mxs = map (zone: "${config.myEnv.servers."${name}".mx.subdomain}.${zone.name}") zonesWithMx;
29 "postfix/mysql_alias_maps" = {
30 user = config.services.postfix.user;
31 group = config.services.postfix.group;
34 # We need to specify that option to trigger ssl connection
36 user = ${config.myEnv.mail.postfix.mysql.user}
37 password = ${config.myEnv.mail.postfix.mysql.password}
38 hosts = ${config.myEnv.mail.postfix.mysql.remoteHost}
39 dbname = ${config.myEnv.mail.postfix.mysql.database}
40 query = SELECT DISTINCT 1
43 ((regex = 1 AND '%s' REGEXP CONCAT('^',source,'$') ) OR (regex = 0 AND source = '%s'))
48 FROM forwardings_blacklisted
51 SELECT 'devnull@immae.eu'
52 FROM forwardings_blacklisted
56 "postfix/ldap_mailboxes" = {
57 user = config.services.postfix.user;
58 group = config.services.postfix.group;
61 server_host = ldaps://${config.myEnv.mail.dovecot.ldap.host}:636
62 search_base = ${config.myEnv.mail.dovecot.ldap.base}
63 query_filter = ${config.myEnv.mail.dovecot.ldap.postfix_mailbox_filter}
64 bind_dn = ${config.myEnv.mail.dovecot.ldap.dn}
65 bind_pw = ${config.myEnv.mail.dovecot.ldap.password}
66 result_attribute = immaePostfixAddress
71 "postfix/sympa_mailbox_maps" = {
72 user = config.services.postfix.user;
73 group = config.services.postfix.group;
76 hosts = ${config.myEnv.mail.sympa.postgresql.host}
77 user = ${config.myEnv.mail.sympa.postgresql.user}
78 password = ${config.myEnv.mail.sympa.postgresql.password}
79 dbname = ${config.myEnv.mail.sympa.postgresql.database}
80 query = SELECT DISTINCT 1 FROM list_table WHERE '%s' IN (
81 CONCAT(name_list, '@', robot_list),
82 CONCAT(name_list, '-request@', robot_list),
83 CONCAT(name_list, '-editor@', robot_list),
84 CONCAT(name_list, '-unsubscribe@', robot_list),
85 CONCAT(name_list, '-owner@', robot_list),
86 CONCAT('sympa-request@', robot_list),
87 CONCAT('sympa-owner@', robot_list),
88 CONCAT('sympa@', robot_list),
89 CONCAT('listmaster@', robot_list),
90 CONCAT('bounce@', robot_list),
91 CONCAT('abuse-feedback-report@', robot_list)
95 "postfix/ldap_ejabberd_users_immae_fr" = {
96 user = config.services.postfix.user;
97 group = config.services.postfix.group;
100 server_host = ldaps://${config.myEnv.jabber.ldap.host}:636
101 search_base = ${config.myEnv.jabber.ldap.base}
102 query_filter = ${config.myEnv.jabber.postfix_user_filter}
104 bind_dn = ${config.myEnv.jabber.ldap.dn}
105 bind_pw = ${config.myEnv.jabber.ldap.password}
106 result_attribute = immaeXmppUid
107 result_format = ejabberd@localhost
113 networking.firewall.allowedTCPPorts = [ 25 ];
115 users.users."${config.services.postfix.user}".extraGroups = [ "keys" ];
116 services.filesWatcher.postfix = {
119 config.secrets.fullPaths."postfix/mysql_alias_maps"
120 config.secrets.fullPaths."postfix/sympa_mailbox_maps"
121 config.secrets.fullPaths."postfix/ldap_ejabberd_users_immae_fr"
122 config.secrets.fullPaths."postfix/ldap_mailboxes"
128 name = n: i: "relay_${n}_${toString i}";
129 pair = n: i: m: lib.attrsets.nameValuePair (name n i) (
131 then pkgs.writeText (name n i) m.content
134 pairs = n: v: lib.imap1 (i: m: pair n i m) v.recipient_maps;
135 in lib.attrsets.filterAttrs (k: v: v != null) (
136 lib.attrsets.listToAttrs (lib.flatten (
137 lib.attrsets.mapAttrsToList pairs config.myEnv.mail.postfix.backup_domains
140 relay_restrictions = lib.attrsets.filterAttrs (k: v: v != null) (
141 lib.attrsets.mapAttrs' (n: v:
142 lib.attrsets.nameValuePair "recipient_access_${n}" (
143 if lib.attrsets.hasAttr "relay_restrictions" v
144 then pkgs.writeText "recipient_access_${n}" v.relay_restrictions
147 ) config.myEnv.mail.postfix.backup_domains
151 cfg = config.myEnv.monitoring.email_check.eldiron;
152 address = "${cfg.mail_address}@${cfg.mail_domain}";
153 aliases = config.myEnv.mail.postfix.common_aliases;
154 in pkgs.writeText "postfix-virtual" (
155 builtins.concatStringsSep "\n" (
158 map (a: "${a} 1") config.myEnv.mail.postfix.other_aliases ++
159 lib.attrsets.mapAttrsToList (
160 n: v: lib.optionalString v.external ''
161 script_${n}@mail.immae.eu 1
163 ) config.myEnv.mail.scripts
164 ++ lib.lists.flatten (map (domain: map (alias: "${alias}@${domain} 1") aliases) all_domains)
169 recipient_maps // relay_restrictions // virtual_map;
171 ### postfix module overrides
172 readme_directory = "${pkgs.postfix}/share/postfix/doc";
173 smtp_tls_CAfile = lib.mkForce "";
174 smtp_tls_cert_file = lib.mkForce "";
175 smtp_tls_key_file = lib.mkForce "";
177 message_size_limit = "1073741824"; # Don't put 0 here, it's not equivalent to "unlimited"
178 mailbox_size_limit = "1073741825"; # Workaround, local delivered mails should all go through scripts
179 alias_database = "\$alias_maps";
183 backups = lib.flatten (lib.attrsets.mapAttrsToList (n: v: v.domains or []) config.myEnv.mail.postfix.backup_domains);
184 virtual_domains = config.myEnv.mail.postfix.additional_mailbox_domains
185 ++ lib.remove null (lib.flatten (map
188 then "${e.domain}${lib.optionalString (e.domain != "") "."}${zone.name}"
191 (zone.withEmail or [])
193 config.myEnv.dns.masterZones
196 backups ++ virtual_domains;
197 relay_recipient_maps = let
198 backup_recipients = lib.flatten (lib.attrsets.mapAttrsToList (n: v:
199 lib.imap1 (i: m: "${m.type}:/etc/postfix/relay_${n}_${toString i}") v.recipient_maps
200 ) config.myEnv.mail.postfix.backup_domains);
201 virtual_alias_maps = [
202 "hash:/etc/postfix/virtual"
203 "mysql:${config.secrets.fullPaths."postfix/mysql_alias_maps"}"
204 "ldap:${config.secrets.fullPaths."postfix/ldap_ejabberd_users_immae_fr"}"
206 virtual_mailbox_maps = [
207 "ldap:${config.secrets.fullPaths."postfix/ldap_mailboxes"}"
208 "pgsql:${config.secrets.fullPaths."postfix/sympa_mailbox_maps"}"
211 backup_recipients ++ virtual_alias_maps ++ virtual_mailbox_maps;
212 smtpd_relay_restrictions = [
213 "defer_unauth_destination"
214 ] ++ lib.flatten (lib.attrsets.mapAttrsToList (n: v:
215 if lib.attrsets.hasAttr "relay_restrictions" v
216 then [ "check_recipient_access hash:/etc/postfix/recipient_access_${n}" ]
218 ) config.myEnv.mail.postfix.backup_domains);
220 ### Additional smtpd configuration
221 smtpd_tls_received_header = "yes";
222 smtpd_tls_loglevel = "1";
224 ### Email sending configuration
225 smtp_tls_security_level = "may";
226 smtp_tls_loglevel = "1";
228 ### Force ip bind for smtp
229 smtp_bind_address = builtins.head config.myEnv.servers."${name}".ips.main.ip4;
230 smtp_bind_address6 = builtins.head config.myEnv.servers."${name}".ips.main.ip6;
233 "unix:${config.myServices.mail.milters.sockets.opendkim}"
234 "unix:${config.myServices.mail.milters.sockets.openarc}"
235 "unix:${config.myServices.mail.milters.sockets.opendmarc}"
240 enableSubmission = false;
241 destination = ["localhost"];
242 # This needs to reverse DNS
243 hostname = config.hostEnv.fqdn;
245 sslCert = "/var/lib/acme/mail/fullchain.pem";
246 sslKey = "/var/lib/acme/mail/key.pem";
247 recipientDelimiter = "+";