From 619e4f46adc15e409122c4e0fa0e0a0b811bb32f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Bouya?= Date: Fri, 10 Jan 2020 00:01:45 +0100 Subject: Add backup MX --- modules/private/mail/relay.nix | 233 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 modules/private/mail/relay.nix (limited to 'modules/private/mail/relay.nix') diff --git a/modules/private/mail/relay.nix b/modules/private/mail/relay.nix new file mode 100644 index 0000000..9111350 --- /dev/null +++ b/modules/private/mail/relay.nix @@ -0,0 +1,233 @@ +{ lib, pkgs, config, nodes, name, ... }: +{ + config = lib.mkIf config.myServices.mailBackup.enable { + security.acme.certs."mail" = config.myServices.certificates.certConfig // { + postRun = '' + systemctl restart postfix.service + ''; + domain = config.hostEnv.fqdn; + extraDomains = let + zonesWithMx = builtins.filter (zone: + lib.attrsets.hasAttr "withEmail" zone && lib.lists.length zone.withEmail > 0 + ) config.myEnv.dns.masterZones; + mxs = map (zone: "${config.myEnv.servers."${name}".mx.subdomain}.${zone.name}") zonesWithMx; + in builtins.listToAttrs (map (mx: lib.attrsets.nameValuePair mx null) mxs); + }; + secrets.keys = [ + { + dest = "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_merge + 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' + ''; + } + { + dest = "postfix/mysql_mailbox_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 mailboxes + WHERE active = 1 + AND ( + (domain = '%d' AND user = '%u' AND regex = 0) + OR ( + regex = 1 + AND '%d' REGEXP CONCAT('^',domain,'$') + AND '%u' REGEXP CONCAT('^',user,'$') + ) + ) + LIMIT 1 + ''; + } + { + dest = "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 ]; + + nixpkgs.overlays = [ (self: super: { + postfix = super.postfix.override { withMySQL = true; }; + }) ]; + 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/mysql_mailbox_maps" + config.secrets.fullPaths."postfix/ldap_ejabberd_users_immae_fr" + ]; + }; + services.postfix = { + mapFiles = let + recipient_maps = let + name = n: i: "relay_${n}_${toString i}"; + pair = n: i: m: lib.attrsets.nameValuePair (name n i) ( + if m.type == "hash" + then pkgs.writeText (name n i) m.content + else null + ); + pairs = n: v: lib.imap1 (i: m: pair n i m) v.recipient_maps; + in lib.attrsets.filterAttrs (k: v: v != null) ( + lib.attrsets.listToAttrs (lib.flatten ( + lib.attrsets.mapAttrsToList pairs config.myEnv.mail.postfix.backup_domains + )) + ); + relay_restrictions = lib.attrsets.filterAttrs (k: v: v != null) ( + lib.attrsets.mapAttrs' (n: v: + lib.attrsets.nameValuePair "recipient_access_${n}" ( + if lib.attrsets.hasAttr "relay_restrictions" v + then pkgs.writeText "recipient_access_${n}" v.relay_restrictions + else null + ) + ) config.myEnv.mail.postfix.backup_domains + ); + virtual_map = { + virtual = pkgs.writeText "postfix-virtual" ( + builtins.concatStringsSep "\n" ( + lib.attrsets.mapAttrsToList ( + n: v: lib.optionalString v.external '' + script_${n}@mail.immae.eu 1 + '' + ) config.myEnv.mail.scripts + ) + ); + }; + sasl_access = { + host_dummy_mailboxes = pkgs.writeText "host-virtual-mailbox" + (builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (n: v: "${n}@immae.eu 1") nodes)); + }; + in + recipient_maps // relay_restrictions // virtual_map // sasl_access; + 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 = let + backups = lib.flatten (lib.attrsets.mapAttrsToList (n: v: v.domains or []) config.myEnv.mail.postfix.backup_domains); + virtual_domains = config.myEnv.mail.postfix.additional_mailbox_domains + ++ lib.remove "localhost.immae.eu" (lib.remove null (lib.flatten (map + (zone: map + (e: if e.receive + then "${e.domain}${lib.optionalString (e.domain != "") "."}${zone.name}" + else null + ) + (zone.withEmail or []) + ) + config.myEnv.dns.masterZones + ))); + in + backups ++ virtual_domains; + relay_recipient_maps = let + backup_recipients = lib.flatten (lib.attrsets.mapAttrsToList (n: v: + lib.imap1 (i: m: "${m.type}:/etc/postfix/relay_${n}_${toString i}") v.recipient_maps + ) config.myEnv.mail.postfix.backup_domains); + 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 = [ + "hash:/etc/postfix/host_dummy_mailboxes" + "mysql:${config.secrets.fullPaths."postfix/mysql_mailbox_maps"}" + ]; + in + backup_recipients ++ virtual_alias_maps ++ virtual_mailbox_maps; + smtpd_relay_restrictions = [ + "defer_unauth_destination" + ] ++ lib.flatten (lib.attrsets.mapAttrsToList (n: v: + if lib.attrsets.hasAttr "relay_restrictions" v + then [ "check_recipient_access hash:/etc/postfix/recipient_access_${n}" ] + else [] + ) config.myEnv.mail.postfix.backup_domains); + + ### 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 = 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; + # FIXME: Mail adressed to localhost.immae.eu will still have mx-1 as + # prioritized MX, which provokes "mail for localhost.immae.eu loops + # back to myself" errors. This transport entry forces to push + # e-mails to its right destination. + transport = '' + localhost.immae.eu smtp:[immae.eu]:25 + ''; + 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 = "+"; + }; + }; +} + -- cgit v1.2.3