{ 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"]; 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 sieve_global_extensions = +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]; }; }; }; }