{ config, pkgs, lib, ... }: let mail_config = (import ./config.nix { }); passwdDir = "/run/dovecot2"; passwdFile = "${passwdDir}/passwd"; bool2int = x: if x then "1" else "0"; # maildir in format "/${domain}/${user}" dovecotMaildir = "maildir:${mail_config.vmail_config.directory}/%d/%n"; postfixCfg = config.services.postfix; dovecot2Cfg = config.services.dovecot2; stateDir = "/var/lib/dovecot"; passwordFiles = lib.mapAttrs (name: value: value.passwordFile) mail_config.accounts; genPasswdScript = pkgs.writeScript "generate-password-file" '' #!${pkgs.stdenv.shell} set -euo pipefail if (! test -d "${passwdDir}"); then mkdir "${passwdDir}" chmod 755 "${passwdDir}" fi for f in ${ builtins.toString (lib.mapAttrsToList (name: value: passwordFiles."${name}") mail_config.accounts) }; do if [ ! -f "$f" ]; then echo "Expected password hash file $f does not exist!" exit 1 fi done cat < ${passwdFile} ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}:${ builtins.toString mail_config.vmail_config.user_group_id }:${ builtins.toString mail_config.vmail_config.user_group_id }::${mail_config.vmail_config.directory}:/run/current-system/sw/bin/nologin:") mail_config.accounts)} EOF chmod 600 ${passwdFile} ''; 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 { services.dovecot2 = { enable = true; enableImap = true; enablePop3 = false; enablePAM = false; enableQuota = true; mailGroup = mail_config.vmail_config.user_group_name; mailUser = mail_config.vmail_config.user_group_name; mailLocation = dovecotMaildir; sslServerCert = mail_config.ssl_config.cert; sslServerKey = mail_config.ssl_config.key; enableLmtp = true; modules = [ pkgs.dovecot_pigeonhole ]; protocols = [ "sieve" ]; sieveScripts = { 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 = '' ${lib.optionalString mail_config.debug_mode '' 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 } mail_access_groups = "${mail_config.vmail_config.user_group_name}" 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 } passdb { driver = passwd-file args = ${passwdFile} } userdb { 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:${mail_config.sieve_directory}/%u/scripts;active=${mail_config.sieve_directory}/%u/active.sieve sieve_default = file:${mail_config.sieve_directory}/%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} sieve_pipe_bin_dir = ${pipeBin}/pipe/bin sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment } lda_mailbox_autosubscribe = yes lda_mailbox_autocreate = yes ''; }; systemd.services.dovecot2 = { preStart = '' ${genPasswdScript} ''; }; systemd.services.postfix.restartTriggers = [ genPasswdScript ]; }