{ lib, pkgs, config, name, nodes, ... }: let getDomains = p: lib.mapAttrsToList (n: v: v.fqdn) (lib.filterAttrs (n: v: v.receive) p.emailPolicies); bydomain = builtins.mapAttrs (n: getDomains) nodes.eldiron.config.myServices.dns.zones; receiving_domains = lib.flatten (builtins.attrValues bydomain); in { options.myServices.mailBackup.enable = lib.mkEnableOption "enable MX backup services"; config = lib.mkIf config.myServices.mailBackup.enable { myServices.mail.milters.enable = true; security.acme.certs."mail" = { postRun = '' systemctl restart postfix.service ''; domain = config.hostEnv.fqdn; extraDomainNames = let zonesWithMx = builtins.attrNames (lib.filterAttrs (n: v: v.hasEmail) nodes.eldiron.config.myServices.dns.zones); mxs = map (n: "${config.myEnv.servers."${name}".mx.subdomain}.${n}") zonesWithMx; in mxs; }; secrets.keys = { "postfix/mysql_alias_maps" = { user = config.services.postfix.user; group = config.services.postfix.group; permissions = "0440"; text = '' # We need to specify that option to trigger ssl connection tls_ciphers = TLSv1.2 user = ${config.myEnv.mail.postfix.mysql.user} password = ${config.myEnv.mail.postfix.mysql.password} hosts = ${config.myEnv.mail.postfix.mysql.remoteHost} dbname = ${config.myEnv.mail.postfix.mysql.database} query = SELECT DISTINCT 1 FROM forwardings WHERE ((regex = 1 AND '%s' REGEXP CONCAT('^',source,'$') ) OR (regex = 0 AND source = '%s')) AND active = 1 AND '%s' NOT IN ( SELECT source FROM forwardings_blacklisted WHERE source = '%s' ) UNION SELECT 'devnull@immae.eu' FROM forwardings_blacklisted WHERE source = '%s' ''; }; "postfix/ldap_mailboxes" = { user = config.services.postfix.user; group = config.services.postfix.group; permissions = "0440"; text = '' server_host = ldaps://${config.myEnv.mail.dovecot.ldap.host}:636 search_base = ${config.myEnv.mail.dovecot.ldap.base} query_filter = ${config.myEnv.mail.dovecot.ldap.postfix_mailbox_filter} bind_dn = ${config.myEnv.mail.dovecot.ldap.dn} bind_pw = ${config.myEnv.mail.dovecot.ldap.password} result_attribute = immaePostfixAddress result_format = dummy version = 3 ''; }; "postfix/sympa_mailbox_maps" = { user = config.services.postfix.user; group = config.services.postfix.group; permissions = "0440"; text = '' hosts = ${config.myEnv.mail.sympa.postgresql.host} user = ${config.myEnv.mail.sympa.postgresql.user} password = ${config.myEnv.mail.sympa.postgresql.password} dbname = ${config.myEnv.mail.sympa.postgresql.database} query = SELECT DISTINCT 1 FROM list_table WHERE '%s' IN ( CONCAT(name_list, '@', robot_list), CONCAT(name_list, '-request@', robot_list), CONCAT(name_list, '-editor@', robot_list), CONCAT(name_list, '-unsubscribe@', robot_list), CONCAT(name_list, '-owner@', robot_list), CONCAT('sympa-request@', robot_list), CONCAT('sympa-owner@', robot_list), CONCAT('sympa@', robot_list), CONCAT('listmaster@', robot_list), CONCAT('bounce@', robot_list), CONCAT('abuse-feedback-report@', robot_list) ) ''; }; "postfix/ldap_ejabberd_users_immae_fr" = { user = config.services.postfix.user; group = config.services.postfix.group; permissions = "0440"; text = '' server_host = ldaps://${config.myEnv.jabber.ldap.host}:636 search_base = ${config.myEnv.jabber.ldap.base} query_filter = ${config.myEnv.jabber.postfix_user_filter} domain = immae.fr bind_dn = ${config.myEnv.jabber.ldap.dn} bind_pw = ${config.myEnv.jabber.ldap.password} result_attribute = immaeXmppUid result_format = ejabberd@localhost version = 3 ''; }; }; networking.firewall.allowedTCPPorts = [ 25 ]; users.users."${config.services.postfix.user}".extraGroups = [ "keys" ]; services.filesWatcher.postfix = { restart = true; paths = [ config.secrets.fullPaths."postfix/mysql_alias_maps" config.secrets.fullPaths."postfix/sympa_mailbox_maps" config.secrets.fullPaths."postfix/ldap_ejabberd_users_immae_fr" config.secrets.fullPaths."postfix/ldap_mailboxes" ]; }; services.postfix = { mapFiles = let virtual_map = { virtual = let cfg = config.myEnv.monitoring.email_check.eldiron; address = "${cfg.mail_address}@${cfg.mail_domain}"; aliases = config.myEnv.mail.postfix.common_aliases; in pkgs.writeText "postfix-virtual" ( builtins.concatStringsSep "\n" ( [ "${address} 1" ] ++ map (a: "${a} 1") config.myEnv.mail.postfix.other_aliases ++ lib.lists.flatten (map (domain: map (alias: "${alias}@${domain} 1") aliases) receiving_domains) ) ); }; in virtual_map; config = { ### postfix module overrides readme_directory = "${pkgs.postfix}/share/postfix/doc"; smtp_tls_CAfile = lib.mkForce ""; smtp_tls_cert_file = lib.mkForce ""; smtp_tls_key_file = lib.mkForce ""; message_size_limit = "1073741824"; # Don't put 0 here, it's not equivalent to "unlimited" mailbox_size_limit = "1073741825"; # Workaround, local delivered mails should all go through scripts alias_database = "\$alias_maps"; ### Relay domains relay_domains = receiving_domains; relay_recipient_maps = let virtual_alias_maps = [ "hash:/etc/postfix/virtual" "mysql:${config.secrets.fullPaths."postfix/mysql_alias_maps"}" "ldap:${config.secrets.fullPaths."postfix/ldap_ejabberd_users_immae_fr"}" ]; virtual_mailbox_maps = [ "ldap:${config.secrets.fullPaths."postfix/ldap_mailboxes"}" "pgsql:${config.secrets.fullPaths."postfix/sympa_mailbox_maps"}" ]; in virtual_alias_maps ++ virtual_mailbox_maps; smtpd_relay_restrictions = [ "defer_unauth_destination" ]; ### Additional smtpd configuration smtpd_tls_received_header = "yes"; smtpd_tls_loglevel = "1"; ### Email sending configuration smtp_tls_security_level = "may"; smtp_tls_loglevel = "1"; ### Force ip bind for smtp smtp_bind_address = builtins.head config.myEnv.servers."${name}".ips.main.ip4; smtp_bind_address6 = builtins.head config.myEnv.servers."${name}".ips.main.ip6; smtpd_milters = [ "unix:${config.myServices.mail.milters.sockets.opendkim}" "unix:${config.myServices.mail.milters.sockets.openarc}" "unix:${config.myServices.mail.milters.sockets.opendmarc}" ]; }; enable = true; enableSmtp = true; enableSubmission = false; destination = ["localhost"]; # This needs to reverse DNS hostname = config.hostEnv.fqdn; setSendmail = false; sslCert = "/var/lib/acme/mail/fullchain.pem"; sslKey = "/var/lib/acme/mail/key.pem"; recipientDelimiter = "+"; }; }; }