{ inputs, config, lib, ... }: with lib; let cfg = config.modules.services.nixos-mailserver; in { imports = [ inputs.nixos-mailserver.nixosModules.mailserver ]; options.modules.services.nixos-mailserver = { enable = mkEnableOption "nixos-mailserver"; webmail = { enable = mkOption { type = types.bool; default = false; description = lib.mdDoc "Whether to enable roundcube webmail"; }; domain = mkOption { type = types.str; }; realHost = mkOption { type = types.str; }; }; }; config = mkIf cfg.enable { sops.secrets.sefidel-imap-pass = { mode = "0440"; owner = "dovecot2"; group = "dovecot2"; }; sops.secrets.system-imap-pass = { mode = "0440"; owner = "dovecot2"; group = "dovecot2"; }; sops.secrets.internal-imap-pass = { mode = "0440"; owner = "dovecot2"; group = "dovecot2"; }; # TODO: https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/merge_requests/316 # WTF: nobody knows why this is needed, but removing it breaks dovecot2. services.dovecot2.sieve.extensions = [ "fileinto" ]; systemd.services.dovecot2 = { serviceConfig.SupplementaryGroups = [ "acme" ]; }; services.postfix = { dnsBlacklists = [ # TODO: add sources "b.barracudacentral.org" "bl.spamcop.net" ]; dnsBlacklistOverrides = '' exotic.sh OK sefidel.net OK sefidel.com OK 192.168.0.0/16 OK ''; }; # HACK: nixos-mailserver sets up reload hook on 'fqdn', which is 'mail.exotic.sh'. # Since our cert is a wildcard cert with domain 'exotic.sh', it is excluded from the hook. security.acme.certs."exotic.sh".reloadServices = [ "postfix.service" "dovecot2.service" ]; mailserver = { enable = true; fqdn = "mail.exotic.sh"; domains = [ "exotic.sh" "nand.moe" "sefidel.com" "sefidel.net" ]; mailboxes = { Trash = { auto = "no"; specialUse = "Trash"; }; Junk = { auto = "subscribe"; specialUse = "Junk"; }; Drafts = { auto = "subscribe"; specialUse = "Drafts"; }; Sent = { auto = "subscribe"; specialUse = "Sent"; }; }; loginAccounts = { "contact@sefidel.com" = { catchAll = [ "sefidel.com" ]; hashedPasswordFile = config.sops.secrets.sefidel-imap-pass.path; }; "contact@sefidel.net" = { aliases = [ "sefidel" "dev@sefidel.net" "social@sefidel.net" "media@sefidel.net" "public@sefidel.net" "admin" "admin@sefidel.net" "postmaster" "postmaster@sefidel.net" ]; hashedPasswordFile = config.sops.secrets.sefidel-imap-pass.path; }; "sef@exotic.sh" = { aliases = [ "sef" "sefidel" "sefidel@exotic.sh" "admin" "admin@exotic.sh" "postmaster" "postmaster@exotic.sh" "admin@nand.moe" "postmaster@nand.moe" ]; hashedPasswordFile = config.sops.secrets.sefidel-imap-pass.path; }; "system@exotic.sh" = { aliases = [ "system" "system@nand.moe" ]; hashedPasswordFile = config.sops.secrets.system-imap-pass.path; }; "internal@exotic.sh" = { aliases = [ "internal" ]; hashedPasswordFile = config.sops.secrets.internal-imap-pass.path; }; }; localDnsResolver = false; certificateScheme = "manual"; certificateFile = "${config.security.acme.certs."exotic.sh".directory}/cert.pem"; keyFile = "${config.security.acme.certs."exotic.sh".directory}/key.pem"; enableImap = true; enableImapSsl = true; enableSubmission = true; enableSubmissionSsl = true; virusScanning = false; }; services.roundcube = mkIf cfg.webmail.enable { enable = true; hostName = cfg.webmail.realHost; database.host = "localhost"; # use localDB, pgsql db/user creation is done automatically. plugins = [ "archive" "enigma" "help" "markasjunk" "vcard_attachments" "zipdownload" ]; extraConfig = '' # STARTTLS required for authn, therefore the domain must match the SSL cert. $config['smtp_server'] = 'tls://${config.mailserver.fqdn}'; ''; }; services.nginx.virtualHosts.${cfg.webmail.realHost} = { enableACME = mkForce false; # conflicts with useACMEHost forceSSL = true; useACMEHost = cfg.webmail.domain; }; modules.persistence.directories = [ "/var/lib/dovecot" "/var/lib/rspamd" "/var/lib/redis-rspamd" "/var/vmail" "/var/dkim" "/var/sieve" "/var/spool/mail" ]; networking.firewall.allowedTCPPorts = [ 143 993 465 587 ]; }; }