{ config, lib, pkgs, ... }: with lib; let cfg = config.services.postfix; user = cfg.user; group = cfg.group; setgidGroup = cfg.setgidGroup; mainCf = '' queue_directory = /var/postfix/queue command_directory = ${pkgs.postfix}/sbin daemon_directory = ${pkgs.postfix}/libexec/postfix mail_owner = ${user} default_privs = nobody '' + optionalString config.networking.enableIPv6 '' inet_protocols = all '' + (if cfg.networks != null then '' mynetworks = ${concatStringsSep ", " cfg.networks} '' else if cfg.networksStyle != "" then '' mynetworks_style = ${cfg.networksStyle} '' else # Postfix default is subnet, but let's play safe '' mynetworks_style = host '') + optionalString (cfg.hostname != "") '' myhostname = ${cfg.hostname} '' + optionalString (cfg.domain != "") '' mydomain = ${cfg.domain} '' + optionalString (cfg.origin != "") '' myorigin = ${cfg.origin} '' + optionalString (cfg.destination != null) '' mydestination = ${concatStringsSep ", " cfg.destination} '' + optionalString (cfg.relayDomains != null) '' relay_domains = ${concatStringsSep ", " cfg.relayDomains} '' + '' local_recipient_maps = relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then cfg.relayHost else "[" + cfg.relayHost + "]"} alias_maps = hash:/var/postfix/conf/aliases mail_spool_directory = /var/spool/mail/ setgid_group = ${setgidGroup} '' + optionalString (cfg.sslCert != "") '' smtp_tls_CAfile = ${cfg.sslCACert} smtp_tls_cert_file = ${cfg.sslCert} smtp_tls_key_file = ${cfg.sslKey} smtp_use_tls = yes smtpd_tls_CAfile = ${cfg.sslCACert} smtpd_tls_cert_file = ${cfg.sslCert} smtpd_tls_key_file = ${cfg.sslKey} smtpd_use_tls = yes recipientDelimiter = ${cfg.recipientDelimiter} '' + optionalString (cfg.virtual != "") '' virtual_alias_maps = hash:/etc/postfix/virtual '' + cfg.extraConfig; masterCf = '' # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== smtp inet n - n - - smtpd #submission inet n - n - - smtpd # -o smtpd_tls_security_level=encrypt # -o smtpd_sasl_auth_enable=yes # -o smtpd_client_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING pickup fifo n - n 60 1 pickup cleanup unix n - n - 0 cleanup qmgr fifo n - n 300 1 qmgr tlsmgr unix - - n 1000? 1 tlsmgr rewrite unix - - n - - trivial-rewrite bounce unix - - n - 0 bounce defer unix - - n - 0 bounce trace unix - - n - 0 bounce verify unix - - n - 1 verify flush unix n - n 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - n - - smtp relay unix - - n - - smtp -o smtp_fallback_relay= # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 showq unix n - n - - showq error unix - - n - - error retry unix - - n - - error discard unix - - n - - discard local unix - n n - - local virtual unix - n n - - virtual lmtp unix - - n - - lmtp anvil unix - - n - 1 anvil scache unix - - n - 1 scache ${cfg.extraMasterConf} ''; aliases = optionalString (cfg.postmasterAlias != "") '' postmaster: ${cfg.postmasterAlias} '' + optionalString (cfg.rootAlias != "") '' root: ${cfg.rootAlias} '' + cfg.extraAliases ; aliasesFile = pkgs.writeText "postfix-aliases" aliases; virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual; mainCfFile = pkgs.writeText "postfix-main.cf" mainCf; masterCfFile = pkgs.writeText "postfix-master.cf" masterCf; in { ###### interface options = { services.postfix = { enable = mkOption { default = false; description = "Whether to run the Postfix mail server."; }; setSendmail = mkOption { default = true; description = "Whether to set the system sendmail to postfix's."; }; user = mkOption { default = "postfix"; description = "What to call the Postfix user (must be used only for postfix)."; }; group = mkOption { default = "postfix"; description = "What to call the Postfix group (must be used only for postfix)."; }; setgidGroup = mkOption { default = "postdrop"; description = " How to call postfix setgid group (for postdrop). Should be uniquely used group. "; }; networks = mkOption { default = null; example = ["192.168.0.1/24"]; description = " Net masks for trusted - allowed to relay mail to third parties - hosts. Leave empty to use mynetworks_style configuration or use default (localhost-only). "; }; networksStyle = mkOption { default = ""; description = " Name of standard way of trusted network specification to use, leave blank if you specify it explicitly or if you want to use default (localhost-only). "; }; hostname = mkOption { default = ""; description =" Hostname to use. Leave blank to use just the hostname of machine. It should be FQDN. "; }; domain = mkOption { default = ""; description =" Domain to use. Leave blank to use hostname minus first component. "; }; origin = mkOption { default = ""; description =" Origin to use in outgoing e-mail. Leave blank to use hostname. "; }; destination = mkOption { default = null; example = ["localhost"]; description = " Full (!) list of domains we deliver locally. Leave blank for acceptable Postfix default. "; }; relayDomains = mkOption { default = null; example = ["localdomain"]; description = " List of domains we agree to relay to. Default is the same as destination. "; }; relayHost = mkOption { default = ""; description = " Mail relay for outbound mail. "; }; lookupMX = mkOption { default = false; description = " Whether relay specified is just domain whose MX must be used. "; }; postmasterAlias = mkOption { default = "root"; description = "Who should receive postmaster e-mail."; }; rootAlias = mkOption { default = ""; description = " Who should receive root e-mail. Blank for no redirection. "; }; extraAliases = mkOption { default = ""; description = " Additional entries to put verbatim into aliases file. "; }; extraConfig = mkOption { default = ""; description = " Extra lines to be added verbatim to the main.cf configuration file. "; }; sslCert = mkOption { default = ""; description = "SSL certificate to use."; }; sslCACert = mkOption { default = ""; description = "SSL certificate of CA."; }; sslKey = mkOption { default = ""; description = "SSL key to use."; }; recipientDelimiter = mkOption { default = ""; example = "+"; description = " Delimiter for address extension: so mail to user+test can be handled by ~user/.forward+test "; }; virtual = mkOption { default = ""; description = " Entries for the virtual alias map. "; }; extraMasterConf = mkOption { default = ""; example = "submission inet n - n - - smtpd"; description = "Extra lines to append to the generated master.cf file."; }; }; }; ###### implementation config = mkIf config.services.postfix.enable { environment = { etc = singleton { source = "/var/postfix/conf"; target = "postfix"; }; # This makes comfortable for root to run 'postqueue' for example. systemPackages = [ pkgs.postfix ]; }; services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail { program = "sendmail"; source = "${pkgs.postfix}/bin/sendmail"; owner = "nobody"; group = "postdrop"; setuid = false; setgid = true; }; users.extraUsers = singleton { name = user; description = "Postfix mail server user"; uid = config.ids.uids.postfix; group = group; }; users.extraGroups = [ { name = group; gid = config.ids.gids.postfix; } { name = setgidGroup; gid = config.ids.gids.postdrop; } ]; jobs.postfix = # I copy _lots_ of shipped configuration filed # that can be left as is. I am afraid the exact # will list slightly change in next Postfix # release, so listing them all one-by-one in an # accurate way is unlikely to be better. { description = "Postfix mail server"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; daemonType = "fork"; preStart = '' if ! [ -d /var/spool/postfix ]; then ${pkgs.coreutils}/bin/mkdir -p /var/spool/mail /var/postfix/conf /var/postfix/queue fi ${pkgs.coreutils}/bin/chown -R ${user}:${group} /var/postfix ${pkgs.coreutils}/bin/chown -R ${user}:${setgidGroup} /var/postfix/queue ${pkgs.coreutils}/bin/chmod -R ug+rwX /var/postfix/queue ${pkgs.coreutils}/bin/chown root:root /var/spool/mail ${pkgs.coreutils}/bin/chmod a+rwxt /var/spool/mail ln -sf "${pkgs.postfix}/share/postfix/conf/"* /var/postfix/conf ln -sf ${aliasesFile} /var/postfix/conf/aliases ln -sf ${virtualFile} /var/postfix/conf/virtual ln -sf ${mainCfFile} /var/postfix/conf/main.cf ln -sf ${masterCfFile} /var/postfix/conf/master.cf ${pkgs.postfix}/sbin/postalias -c /var/postfix/conf /var/postfix/conf/aliases ${pkgs.postfix}/sbin/postmap -c /var/postfix/conf /var/postfix/conf/virtual ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf start ''; preStop = '' ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf stop ''; }; }; }