From 903e9d67ee6018380732df1e593ce85f7b36762c Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Sun, 30 Mar 2025 17:56:39 -0600 Subject: sys/mta: implement backup MX --- sys/mta/default.nix | 188 +++++++++++++++++++++++++++++++--------------------- sys/ns/mx.nix | 7 +- sys/preset/dmz.nix | 6 +- 3 files changed, 122 insertions(+), 79 deletions(-) (limited to 'sys') diff --git a/sys/mta/default.nix b/sys/mta/default.nix index 84afee5..35508e6 100644 --- a/sys/mta/default.nix +++ b/sys/mta/default.nix @@ -5,12 +5,25 @@ with lib; let inherit (config.local) domains virtual users; inherit (config.networking) domain; + isBackup = cfg.mode == "backup"; + isPrimary = cfg.mode == "primary"; + + allDomains = optional (! virtualDomains ? ${domain}) domain ++ attrNames virtualDomains; virtualDomains = filterAttrs (name: _: name != domain) virtual; + + mdaTransport = + if isPrimary + then "lmtp:inet:${cfg.mdaAddr}:${toString cfg.lmtpPort}" + else "error:bad transport"; in { options.local.mta = { enable = mkEnableOption "mail transfer agent"; + mode = mkOption { + type = types.enum [ "primary" "backup" ]; + }; + mdaAddr = mkOption { type = types.str; }; @@ -22,6 +35,10 @@ in lmtpPort = mkOption { type = types.port; }; + + relayListen = mkOption { + type = types.str; + }; }; config = mkIf cfg.enable { @@ -30,7 +47,7 @@ in filter = "postfix[mode=aggressive]"; }; - opendkim = { + opendkim = mkIf isPrimary { enable = true; group = "postfix"; @@ -45,45 +62,60 @@ in postfix = let - cert = config.security.acme.certs.${domains.smtp.main}.directory; + cert = config.security.acme.certs.${mtaDomain.main}.directory; + + mtaDomain = + if isPrimary + then domains.smtp + else domains.smtp-backup; in { enable = true; enableSmtp = true; - enableSubmissions = true; + enableSubmissions = isPrimary; inherit domain; - hostname = domains.smtp.main; + hostname = mtaDomain.main; + #TODO: check_recipient_access para rechazar localhost desde afuera - destination = [ "localhost" "$mydomain" ]; + destination = optionals isPrimary [ "localhost" "$mydomain" ]; origin = "$mydomain"; networksStyle = "host"; + relayHost = optionalString isBackup domains.smtp.main; + lookupMX = false; + + relayDomains = + if isBackup + then allDomains + else null; + sslKey = "${cert}/key.pem"; sslCert = "${cert}/fullchain.pem"; # También es postmaster rootAlias = config.local.sysadmin; - extraAliases = concatLines - (flatten (mapAttrsToList + extraAliases = optionalString isPrimary + (concatLines (flatten (mapAttrsToList (name: user: map (alias: "${alias}: ${name}") user.hardAliases) - users)); + users))); - localRecipients = map - (user: "${user}@${domain}") - (attrNames (users // virtual.${domain}.users)); + localRecipients = optionals isPrimary + (map (user: "${user}@${domain}") + (attrNames (users // virtual.${domain}.users))); - virtual = concatLines (flatten (mapAttrsToList - (name: virtual: mapAttrsToList - (alias: targets: "${alias}@${name} ${concatStringsSep ", " targets}") - virtual.aliases) - virtual)); + virtual = optionalString isPrimary + (concatLines (flatten (mapAttrsToList + (name: virtual: mapAttrsToList + (alias: targets: "${alias}@${name} ${concatStringsSep ", " targets}") + virtual.aliases) + virtual))); - mapFiles = { + mapFiles = optionalAttrs isPrimary { sender_ccerts = pkgs.writeText "postfix-sender_ccerts" (concatLines (flatten (mapAttrsToList @@ -118,63 +150,66 @@ in virtual))); }; - config = - let - mdaTransport = "lmtp:inet:${cfg.mdaAddr}:${toString cfg.lmtpPort}"; - in - { - # user+extension@domain.tld - recipient_delimiter = "+"; - - message_size_limit = toString (50 * 1048576); - - virtual_alias_maps = mkAfter [ "pcre:/etc/postfix/virtual_rules" ]; - virtual_mailbox_domains = attrNames virtualDomains; - virtual_mailbox_maps = [ "hash:/etc/postfix/virtual_recipients" ]; - - local_transport = mdaTransport; - virtual_transport = mdaTransport; - - smtpd_sasl_type = "dovecot"; - smtpd_sasl_path = "inet:${cfg.mdaAddr}:${toString cfg.saslPort}"; - smtpd_sasl_local_domain = "$mydomain"; - smtpd_sasl_security_options = [ "noanonymous" ]; - - smtpd_tls_auth_only = true; - # Nota: smtpd_tls_dh1024_param_file fue deprecado en 3.9 - - smtpd_tls_CAfile = "${config.local.pki.ca.mail.fullchain}"; - smtpd_tls_ccert_verifydepth = "1"; - tls_append_default_CA = false; # Crítico - - # Inventado, no es parámetro de postfix - local_submission_client_restrictions = [ - "permit_tls_all_clientcerts" - "permit_sasl_authenticated" - "reject" - ]; - - smtpd_relay_restrictions = [ - "permit_mynetworks" - "permit_tls_all_clientcerts" - "permit_sasl_authenticated" - "reject_unauth_destination" - ]; - - smtpd_sender_login_maps = [ "hash:/etc/postfix/sender_login" ]; - - smtpd_sender_restrictions = [ - "check_sender_access hash:/etc/postfix/sender_ccerts" - "reject_sender_login_mismatch" - ]; - - smtpd_milters = "unix:/run/opendkim/opendkim.sock"; - non_smtpd_milters = "$smtpd_milters"; - milter_default_action = "accept"; - }; + config = { + # user+extension@domain.tld + recipient_delimiter = optionalString isPrimary "+"; + + message_size_limit = toString (50 * 1048576); + + local_transport = mdaTransport; + virtual_transport = mdaTransport; + + smtpd_tls_auth_only = true; + # Nota: smtpd_tls_dh1024_param_file fue deprecado en 3.9 + + tls_append_default_CA = false; # Crítico + } // optionalAttrs isPrimary { + virtual_alias_maps = mkAfter [ "pcre:/etc/postfix/virtual_rules" ]; + virtual_mailbox_domains = attrNames virtualDomains; + virtual_mailbox_maps = [ "hash:/etc/postfix/virtual_recipients" ]; + + smtpd_sasl_type = "dovecot"; + smtpd_sasl_path = "inet:${cfg.mdaAddr}:${toString cfg.saslPort}"; + smtpd_sasl_local_domain = "$mydomain"; + smtpd_sasl_security_options = [ "noanonymous" ]; + + smtpd_tls_CAfile = "${config.local.pki.ca.mail.fullchain}"; + smtpd_tls_ccert_verifydepth = "1"; + + # Inventado, no es parámetro de postfix + local_submission_client_restrictions = [ + "permit_tls_all_clientcerts" + "permit_sasl_authenticated" + "reject" + ]; + + smtpd_sender_login_maps = [ "hash:/etc/postfix/sender_login" ]; + + smtpd_relay_restrictions = [ + "permit_mynetworks" + "permit_tls_all_clientcerts" + "permit_sasl_authenticated" + "reject_unauth_destination" + ]; + + smtpd_sender_restrictions = [ + "check_sender_access hash:/etc/postfix/sender_ccerts" + "reject_sender_login_mismatch" + ]; + + smtpd_milters = "unix:/run/opendkim/opendkim.sock"; + non_smtpd_milters = "$smtpd_milters"; + milter_default_action = "accept"; + } // optionalAttrs isBackup { + inet_interfaces = [ cfg.relayListen ]; + + smtpd_relay_restrictions = [ + "reject_unauth_destination" + ]; + }; # Importante: existe submissionOptions por aparte, no son iguales - submissionsOptions = { + submissionsOptions = optionalAttrs isPrimary { smtpd_client_restrictions = "$local_submission_client_restrictions"; smtpd_sasl_auth_enable = "yes"; smtpd_tls_ask_ccert = "yes"; @@ -183,15 +218,18 @@ in }; }; - networking.firewall.allowedTCPPorts = [ 25 465 ]; + #TODO: solo para las destination addresses necesarias + networking.firewall.allowedTCPPorts = optionals isPrimary [ 25 465 ]; local = { boot.impermanence.directories = [ - { directory = "/var/lib/opendkim"; user = "opendkim"; group = "postfix"; mode = "u=rwx,g=,o="; } { directory = "/var/lib/postfix"; user = "root"; group = "root"; mode = "u=rwx,g=rx,o=rx"; } + ] ++ optionals isPrimary [ + { directory = "/var/lib/opendkim"; user = "opendkim"; group = "postfix"; mode = "u=rwx,g=,o="; } ]; - certs.smtp.enable = true; + certs.smtp.enable = isPrimary; + certs.smtp-backup.enable = isBackup; }; }; } diff --git a/sys/ns/mx.nix b/sys/ns/mx.nix index 5c7d3d0..6126dd0 100644 --- a/sys/ns/mx.nix +++ b/sys/ns/mx.nix @@ -11,9 +11,10 @@ in config = mkIf config.localMX.enable { mx = [ - { name = "@"; priority = 10; host = "${domains.smtp.main}."; } - { name = "@"; priority = 20; host = "mxbackup1.junkemailfilter.com."; } - { name = "@"; priority = 30; host = "mxbackup2.junkemailfilter.com."; } + { name = "@"; priority = 10; host = "${domains.smtp.gated}."; } + { name = "@"; priority = 20; host = "${domains.smtp-backup.main}."; } + { name = "@"; priority = 30; host = "mxbackup1.junkemailfilter.com."; } + { name = "@"; priority = 40; host = "mxbackup2.junkemailfilter.com."; } ]; txt = [ diff --git a/sys/preset/dmz.nix b/sys/preset/dmz.nix index 4a3fc69..d740d14 100644 --- a/sys/preset/dmz.nix +++ b/sys/preset/dmz.nix @@ -29,7 +29,11 @@ in jobs.pkiExpiry.enable = mkDefault config.local.mta.enable; - mta.enable = mkDefault true; + mta = { + enable = mkDefault true; + + mode = "primary"; + }; net = { enable = true; -- cgit v1.2.3