{ config, pkgs, lib, ... }: let inherit (lib.modules) mkIf; inherit (lib.strings) concatStringsSep; inherit (lib.lists) flatten optional; inherit (lib.attrsets) mapAttrsToList; inherit (builtins) toFile; mailConfig = config.services.mailserver; tls_allowed = "TLSv1.3, TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3"; tls_disallow = "MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL"; sendingReceivingAliases = concatStringsSep "\n" (flatten [ (mapAttrsToList (email: config: map ( alias: "${alias} ${email} " ) config.aliases) mailConfig.accounts) (mapAttrsToList (email: _config: [ # i dont know if this is actually needed "${email} ${email}" ]) mailConfig.accounts) ]); sendingReceivingAliasesMappedName = "sending_receiving_aliases"; sendingReceivingAliasesFile = toFile "${sendingReceivingAliasesMappedName}" sendingReceivingAliases; extraAliasesCombinedFilePath = "/run/postfix_sending_receiving_aliases"; in { config = mkIf mailConfig.enable { systemd.tmpfiles.rules = mkIf (mailConfig.extraAliasesFile != null) [ "f ${extraAliasesCombinedFilePath} 660 root root" ]; systemd.services.postfix-extra-aliases-setup = mkIf (mailConfig.extraAliasesFile != null) { wantedBy = ["multi-user.target"]; partOf = ["postfix.service"]; before = ["postfix-setup.service"]; script = '' cat "${sendingReceivingAliasesFile}" > ${extraAliasesCombinedFilePath} echo >> ${extraAliasesCombinedFilePath} cat "${mailConfig.extraAliasesFile}" >> ${extraAliasesCombinedFilePath} ''; }; services.postfix = let mappedFile = name: "hash:/var/lib/postfix/conf/${name}"; sendingReceivingAliasesMappedFile = mappedFile sendingReceivingAliasesMappedName; submissionOptions = { smtpd_tls_security_level = "encrypt"; smtpd_sasl_auth_enable = "yes"; smtpd_sasl_type = "dovecot"; smtpd_sasl_path = "/run/dovecot2/auth"; smtpd_sasl_security_options = "noanonymous"; smtpd_sasl_local_domain = "$myhostname"; smtpd_client_restrictions = "permit_sasl_authenticated,reject"; smtpd_sender_login_maps = sendingReceivingAliasesMappedFile; smtpd_sender_restrictions = "reject_sender_login_mismatch"; smtpd_recipient_restrictions = "reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject"; cleanup_service_name = "submission-header-cleanup"; }; in { enable = true; hostname = "${mailConfig.fqdn}"; networksStyle = "host"; mapFiles = { "sending_receiving_aliases" = if (mailConfig.extraAliasesFile == null) then sendingReceivingAliasesFile else "${extraAliasesCombinedFilePath}"; }; enableSubmission = true; enableSubmissions = true; sslCert = mailConfig.sslConfig.cert; sslKey = mailConfig.sslConfig.key; config = { # Extra Config mydestination = ""; recipient_delimiter = "+"; smtpd_banner = "${mailConfig.fqdn} ESMTP NO UCE"; disable_vrfy_command = true; message_size_limit = "20971520"; virtual_uid_maps = "static:${toString mailConfig.vmail.userID}"; virtual_gid_maps = "static:${toString mailConfig.vmail.groupID}"; virtual_mailbox_base = "${mailConfig.vmail.directory}"; virtual_mailbox_domains = toFile "vhosts" (concatStringsSep "\n" mailConfig.domains); virtual_mailbox_maps = sendingReceivingAliasesMappedFile; virtual_alias_maps = sendingReceivingAliasesMappedFile; virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp"; lmtp_destination_recipient_limit = "1"; smtpd_sasl_type = "dovecot"; smtpd_sasl_path = "/run/dovecot2/auth"; smtpd_sasl_auth_enable = true; smtpd_relay_restrictions = [ "permit_mynetworks" "permit_sasl_authenticated" "reject_unauth_destination" ]; policy-spf_time_limit = mkIf mailConfig.spf.enable "3600s"; smtpd_recipient_restrictions = flatten [ (optional mailConfig.spf.enable "check_policy_service unix:private/policy-spf") ]; smtpd_tls_security_level = "may"; smtpd_tls_eecdh_grade = "ultra"; # Only Alow Modern TLS smtp_tls_protocols = tls_allowed; smtpd_tls_protocols = tls_allowed; smtp_tls_mandatory_protocols = tls_allowed; smtpd_tls_mandatory_protocols = tls_allowed; # Disable Old Ciphers smtp_tls_exclude_ciphers = tls_disallow; smtpd_tls_exclude_ciphers = tls_disallow; smtp_tls_mandatory_exclude_ciphers = tls_disallow; smtpd_tls_mandatory_exclude_ciphers = tls_disallow; smtp_tls_ciphers = "high"; smtpd_tls_ciphers = "high"; smtp_tls_mandatory_ciphers = "high"; smtpd_tls_mandatory_ciphers = "high"; tls_preempt_cipherlist = true; smtpd_tls_auth_only = true; smtpd_tls_loglevel = "1"; tls_random_source = "dev:/dev/urandom"; milter_default_action = "quarantine"; smtpd_milters = flatten [ (optional mailConfig.dkim.enable "unix:/run/opendkim/opendkim.sock") ]; non_smtpd_milters = flatten [ (optional mailConfig.dkim.enable "unix:/run/opendkim/opendkim.sock") ]; milter_protocol = "6"; milter_mail_macros = "i {mail_addr} {client_addr} {client_name} {auth_type} {auth_authen} {auth_author} {mail_addr} {mail_host} {mail_mailer}"; }; inherit submissionOptions; submissionsOptions = submissionOptions; masterConfig = { "lmtp" = { # Add headers when delivering, see http://www.postfix.org/smtp.8.html # D => Delivered-To, O => X-Original-To, R => Return-Path args = ["flags=O"]; }; "policy-spf" = mkIf mailConfig.spf.enable { type = "unix"; privileged = true; chroot = false; command = "spawn"; args = let policydConfig = toFile "policyd-spf.conf" mailConfig.spf.policydConfig; in [ "user=nobody" "argv=${pkgs.pypolicyd-spf}/bin/policyd-spf" "${policydConfig}" ]; }; "submission-header-cleanup" = { type = "unix"; private = false; chroot = false; maxproc = 0; command = "cleanup"; args = let submissionHeaderCleanupRules = toFile "submission_header_cleanup_rules" '' /^Received:/ IGNORE /^X-Originating-IP:/ IGNORE /^X-Mailer:/ IGNORE /^User-Agent:/ IGNORE /^X-Enigmail:/ IGNORE /^Message-ID:\s+<(.*?)@.*?>/ REPLACE Message-ID: <$1@${mailConfig.fqdn}> ''; in ["-o" "header_checks=pcre:${submissionHeaderCleanupRules}"]; }; }; }; }; }