{ config, lib, pkgs, ... }: let domain = "mail.char.lt"; cert = config.security.acme.certs.${domain}; accounts = { som-codes = "charlotte@som.codes"; char-lt = "c@char.lt"; }; passwordFile = name: "/var/secrets/mail/${name}.password"; mbsyncConfig = name: username: pkgs.writeText "mbsync-${name}.conf" '' IMAPAccount migadu Host imap.migadu.com Port 993 User ${username} PassCmd "${pkgs.coreutils}/bin/cat $CREDENTIALS_DIRECTORY/password" TLSType IMAPS CertificateFile /etc/ssl/certs/ca-certificates.crt IMAPStore remote Account migadu IMAPAccount dovecot Host 127.0.0.1 Port 143 User ${username} PassCmd "${pkgs.coreutils}/bin/cat $CREDENTIALS_DIRECTORY/password" TLSType None AuthMechs PLAIN IMAPStore local Account dovecot Channel mail Far :remote: Near :local: Patterns * Sync All Create Both Remove Both Expunge Both CopyArrivalDate yes SyncState /var/lib/mail-sync/${name}/ ''; in { services.nginx.virtualHosts.${domain} = { enableACME = true; locations."/".return = "404"; }; security.acme.certs.${domain}.reloadServices = [ "dovecot.service" ]; networking.firewall.allowedTCPPorts = [ 993 ]; services.dovecot2 = { enable = true; package = pkgs.dovecot; settings = { dovecot_config_version = "2.4.4"; dovecot_storage_version = "2.4.4"; protocols.imap = true; hostname = domain; ssl = "required"; ssl_server_cert_file = "${cert.directory}/fullchain.pem"; ssl_server_key_file = "${cert.directory}/key.pem"; mail_uid = "dovemail"; mail_gid = "dovemail"; first_valid_uid = 1; mail_driver = "maildir"; mail_path = "~/Maildir"; "namespace inbox" = { inbox = true; separator = "/"; }; "passdb passwd-file".passwd_file_path = "/run/dovecot2/passwd"; "userdb passwd-file".passwd_file_path = "/run/dovecot2/passwd"; mail_plugins = { fts = true; fts_flatcurve = true; }; "fts flatcurve" = { }; fts_autoindex = true; fts_flatcurve_substring_search = true; "language en".default = true; language_tokenizers = "generic email-address"; language_filters = "normalizer-icu"; # Sync over IMAP so Dovecot alone manages Maildir UIDs and indexes. "service imap-login" = { "inet_listener imap" = { listen = "127.0.0.1"; port = 143; }; "inet_listener imaps" = { port = 993; ssl = true; }; }; }; }; systemd.tmpfiles.rules = [ "d /var/lib/mail 0700 dovemail dovemail -" ]; users.users.mail-sync = { isSystemUser = true; group = "mail-sync"; }; users.groups.mail-sync = { }; systemd.services = { dovecot = { wants = [ "acme-finished-${domain}.target" ]; after = [ "acme-finished-${domain}.target" ]; serviceConfig.LoadCredential = lib.mapAttrsToList ( name: _: "${name}:${passwordFile name}" ) accounts; preStart = lib.mkAfter '' umask 077 : > /run/dovecot2/passwd.new ${lib.concatStringsSep "\n" ( lib.mapAttrsToList (name: username: '' test -n "$(cat "$CREDENTIALS_DIRECTORY/${name}")" hash=$(${pkgs.openssl}/bin/openssl passwd -6 -stdin < "$CREDENTIALS_DIRECTORY/${name}") [[ "$hash" != *$'\n'* ]] printf '${username}:{CRYPT}%s:%s:%s::/var/lib/mail/${username}\n' \ "$hash" "$(id -u dovemail)" "$(id -g dovemail)" >> /run/dovecot2/passwd.new '') accounts )} chown root:dovecot2 /run/dovecot2/passwd.new chmod 640 /run/dovecot2/passwd.new mv /run/dovecot2/passwd.new /run/dovecot2/passwd ''; }; } // lib.mapAttrs' ( name: username: lib.nameValuePair "mail-sync-${name}" { description = "Synchronise ${username} with Migadu"; wants = [ "network-online.target" ]; requires = [ "dovecot.service" ]; after = [ "network-online.target" "dovecot.service" ]; serviceConfig = { Type = "oneshot"; User = "mail-sync"; Group = "mail-sync"; StateDirectory = "mail-sync/${name}"; StateDirectoryMode = "0700"; UMask = "0077"; LoadCredential = [ "password:${passwordFile name}" ]; ExecStart = "${pkgs.isync}/bin/mbsync --config ${mbsyncConfig name username} --all"; NoNewPrivileges = true; PrivateTmp = true; ProtectHome = true; ProtectSystem = "strict"; }; } ) accounts; systemd.timers = lib.mapAttrs' ( name: _: lib.nameValuePair "mail-sync-${name}" { wantedBy = [ "timers.target" ]; timerConfig = { OnBootSec = "1min"; OnUnitInactiveSec = "1min"; }; } ) accounts; }