{ config, pkgs, lib, ... }: with lib; let postfix_map = tableType: tableName: "${tableType}:/run/postfix/maps/${tableName}"; postfix_hash = postfix_map "hash"; in { options = { services.postfix.mapFilesRun = mkOption { type = types.attrsOf (types.either types.path (types.submodule { options = { type = mkOption { type = types.str; default = "hash"; }; path = mkOption { type = types.nullOr types.path; default = null; }; text = mkOption { type = types.nullOr types.lines; default = null; }; }; })); default = {}; }; }; config = { services.postfix = { enable = true; hostname = "surtr.yggdrasil.li"; recipientDelimiter = "+"; setSendmail = true; postmasterAlias = ""; rootAlias = ""; extraAliases = ""; destination = []; sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; networks = ["127.0.0.0/8" "[::ffff:127.0.0.0]/104" "[::1]/128" "10.141.0.0/16"]; mapFilesRun = { "relay_ccert" = { text = ""; }; "sni" = { text = '' bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem mailin.bouncy.email /run/credentials/postfix.service/mailin.bouncy.email.sni.pem mailsub.bouncy.email /run/credentials/postfix.service/mailsub.bouncy.email.sni.pem .bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem '';}; "esmtp_access" = { type = "cidr"; text = '' # Allow DSN requests from local subnet only 192.168.0.0/16 silent-discard 172.16.0.0/12 silent-discard 10.0.0.0/8 silent-discard 0.0.0.0/0 silent-discard, dsn fd00::/8 silent-discard ::/0 silent-discard, dsn '';}; }; config = { #the dh params smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; #enable ECDH smtpd_tls_eecdh_grade = "strong"; #enabled SSL protocols, don't allow SSLv2 and SSLv3 smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; #allowed ciphers for smtpd_tls_security_level=encrypt smtpd_tls_mandatory_ciphers = "high"; #allowed ciphers for smtpd_tls_security_level=may #smtpd_tls_ciphers = high #enforce the server cipher preference tls_preempt_cipherlist = true; #disable following ciphers for smtpd_tls_security_level=encrypt smtpd_tls_mandatory_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; #disable following ciphers for smtpd_tls_security_level=may smtpd_tls_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; #enable TLS logging to see the ciphers for inbound connections smtpd_tls_loglevel = "1"; #enable TLS logging to see the ciphers for outbound connections smtp_tls_loglevel = "1"; smtpd_tls_ask_ccert = true; smtpd_tls_CAfile = toString ./ca/ca.crt; smtp_tls_security_level = "dane"; smtp_dns_support_level = "dnssec"; tls_server_sni_maps = postfix_hash "sni"; local_recipient_maps = ""; # 10 GiB message_size_limit = "10737418240"; # 10 GiB mailbox_size_limit = "10737418240"; smtpd_delay_reject = true; smtpd_helo_required = true; smtpd_helo_restrictions = "permit"; smtpd_recipient_restrictions = [ "reject_unauth_pipelining" "reject_non_fqdn_recipient" "reject_unknown_recipient_domain" "permit_mynetworks" "check_ccert_access ${postfix_hash "relay_ccert"}" "reject_non_fqdn_helo_hostname" "reject_invalid_helo_hostname" "reject_unauth_destination" "reject_unknown_recipient_domain" "reject_unverified_recipient" ]; smtpd_relay_restrictions = [ "permit_mynetworks" "check_ccert_access ${postfix_hash "relay_ccert"}" "reject_unauth_destination" ]; propagate_unmatched_extensions = ["canonical" "virtual" "alias"]; smtpd_authorized_verp_clients = "$authorized_verp_clients"; authorized_verp_clients = "$mynetworks"; milter_default_action = "accept"; smtpd_milters = [config.services.opendkim.socket]; non_smtpd_milters = [config.services.opendkim.socket]; alias_maps = ""; queue_run_delay = "10s"; minimal_backoff_time = "1m"; maximal_backoff_time = "10m"; maximal_queue_lifetime = "100m"; bounce_queue_lifetime = "20m"; smtpd_discard_ehlo_keyword_address_maps = postfix_map "cidr" "esmtp_access"; sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; sender_canonical_classes = "envelope_sender"; recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; }; masterConfig = { smtps = { type = "inet"; command = "smtpd"; args = [ "-o" "smtpd_tls_wrappermode=yes" "-o" "smtpd_tls_req_ccert=yes" "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject" "-o" "smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_tls_all_clientcerts,reject" ]; }; }; }; services.postsrsd = { enable = true; domain = "srs.surtr.yggdrasil.li"; separator = "+"; excludeDomains = [ "surtr.yggdrasil.li" ".bouncy.email" "bouncy.email" ]; }; services.opendkim = { enable = true; # user = "postfix"; group = "postfix"; # socket = "local:/run/opendkim/opendkim.sock"; domains = ''csl:${concatStringsSep "," ["surtr.yggdrasil.li" "bouncy.email"]}''; selector = "surtr"; configFile = builtins.toFile "opendkim.conf" '' Syslog true MTACommand ${config.security.wrapperDir}/sendmail LogResults true ''; }; security.dhparams = { params = { "postfix-512".bits = 512; "postfix-1024".bits = 2048; }; }; security.acme.domains = let mkSNI = '' cat key.pem full.pem > sni.pem ''; in { "bouncy.email" = { certCfg.postRun = mkSNI; }; "mailin.bouncy.email" = { certCfg.postRun = mkSNI; }; "mailsub.bouncy.email" = { certCfg.postRun = mkSNI; }; "surtr.yggdrasil.li" = {}; }; systemd.services.postfix = { preStart = concatStringsSep "\n" (mapAttrsToList (to: from: let cont = {type, path, text}: assert !(isNull path && isNull text); let path' = if isNull path then pkgs.writeText to text else path; in '' ln -sf ${path'} /run/postfix/maps/${to} postmap ${type}:/run/postfix/maps/${to} ''; in if builtins.isPath from then cont { path = from; } else cont from ) config.services.postfix.mapFilesRun); serviceConfig = { RuntimeDirectory = ["postfix/maps"]; LoadCredential = [ "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" "bouncy.email.sni.pem:${config.security.acme.certs."bouncy.email".directory}/sni.pem" "mailin.bouncy.email.sni.pem:${config.security.acme.certs."mailin.bouncy.email".directory}/sni.pem" "mailsub.bouncy.email.sni.pem:${config.security.acme.certs."mailsub.bouncy.email".directory}/sni.pem" ]; }; }; }; }