{ config, pkgs, lib, ... }: let inherit (lib.modules) mkIf; inherit (lib.attrsets) mapAttrsToList; inherit (lib.strings) concatStringsSep optionalString; mailConfig = config.services.mailserver; vmailConfig = mailConfig.vmail; postfixCfg = config.services.postfix; dovecotRuntimeDir = "/run/dovecot2"; passwdFile = "${dovecotRuntimeDir}/passwd"; genPasswdScript = pkgs.writeScript "generate-password-file" '' #!${pkgs.stdenv.shell} set -euo pipefail ${concatStringsSep "\n" (map (userPasswdFile: '' if [ ! -f "${userPasswdFile}" ]; then echo "Expected password hash file ${userPasswdFile} does not exist!" exit 1 fi '') (mapAttrsToList (_email: config: config.passwordHashFile) mailConfig.accounts))} cat < ${passwdFile} ${concatStringsSep "\n" (mapAttrsToList ( email: config: "${email}:$(head -n 1 ${config.passwordHashFile})" ) mailConfig.accounts)} EOF ''; in { config = mkIf (mailConfig.enable) { services.dovecot2 = { enable = true; enableImap = true; enableLmtp = true; enableQuota = true; enablePop3 = false; enablePAM = false; # Not using PAM for Auth mailUser = vmailConfig.user; mailGroup = vmailConfig.group; mailLocation = "maildir:${vmailConfig.directory}/%d/%n"; sslServerCert = mailConfig.sslConfig.cert; sslServerKey = mailConfig.sslConfig.key; # For Sieve modules = with pkgs; [dovecot_pigeonhole]; protocols = ["sieve"]; sieveScripts = { # BROKEN: after: line 1: error: require command: unknown Sieve capability `fileinto'. # after = builtins.toFile "spam.sieve" '' # require "fileinto"; # if header :is "X-Spam" "Yes" { # fileinto "Junk"; # stop; # } # ''; }; mailboxes = { Trash = { auto = "no"; specialUse = "Trash"; }; Junk = { auto = "subscribe"; specialUse = "Junk"; }; Drafts = { auto = "subscribe"; specialUse = "Drafts"; }; Sent = { auto = "subscribe"; specialUse = "Sent"; }; }; extraConfig = '' ${optionalString mailConfig.debugMode '' mail_debug = yes auth_debug = yes verbose_ssl = yes ''} service imap-login { inet_listener imap { port = 143 } inet_listener imaps { port = 993 ssl = yes } } protocol imap { mail_max_userip_connections = 100 mail_plugins = $mail_plugins imap_sieve } ssl = required ssl_min_protocol = TLSv1.2 ssl_prefer_server_ciphers = yes service lmtp { unix_listener dovecot-lmtp { group = ${postfixCfg.group} mode = 0600 user = ${postfixCfg.user} } } recipient_delimiter = "+" lmtp_save_to_detail_mailbox = "no" protocol lmtp { mail_plugins = $mail_plugins sieve } mail_access_groups = "${vmailConfig.group}" userdb { driver = static args = uid=${toString vmailConfig.userID} gid=${toString vmailConfig.groupID} } passdb { driver = passwd-file args = ${passwdFile} } service auth { unix_listener auth { mode = 0660 user = ${postfixCfg.user} group = ${postfixCfg.group} } } auth_mechanisms = plain login namespace inbox { separator = "." inbox = yes } plugin { sieve_plugins = sieve_imapsieve sieve_extprograms sieve = file:${mailConfig.sieveDirectory}/%u/scripts;active=${mailConfig.sieveDirectory}/%u/active.sieve sieve_default = file:${mailConfig.sieveDirectory}/%u/default.sieve sieve_default_name = default # From elsewhere to Spam folder imapsieve_mailbox1_name = Junk imapsieve_mailbox1_causes = COPY imapsieve_mailbox1_before = file:${./spam_sieve/report-spam.sieve} # From Spam folder to elsewhere imapsieve_mailbox2_name = * imapsieve_mailbox2_from = Junk imapsieve_mailbox2_causes = COPY imapsieve_mailbox2_before = file:${./spam_sieve/report-ham.sieve} ${optionalString mailConfig.rspamd.enable (let pipeBin = pkgs.stdenv.mkDerivation { name = "pipe_bin"; src = ./pipe_bin; buildInputs = with pkgs; [makeWrapper coreutils bash rspamd]; buildCommand = '' mkdir -p $out/pipe/bin cp $src/* $out/pipe/bin/ chmod a+x $out/pipe/bin/* patchShebangs $out/pipe/bin for file in $out/pipe/bin/*; do wrapProgram $file \ --set PATH "${pkgs.coreutils}/bin:${pkgs.rspamd}/bin" done ''; }; in '' sieve_pipe_bin_dir = ${pipeBin}/pipe/bin '')} sieve_global_extensions = ${optionalString mailConfig.rspamd.enable "+vnd.dovecot.pipe"} +vnd.dovecot.environment } lda_mailbox_autosubscribe = yes lda_mailbox_autocreate = yes ''; }; systemd = { tmpfiles.rules = [ "f ${passwdFile} 600 dovecot2 dovecot2" ]; services = { dovecot2.preStart = '' ${genPasswdScript} ''; postfix.restartTriggers = [genPasswdScript]; }; }; }; }