{ config, lib, pkgs, ... }: with lib; let cfg = config.fudo.services.mail-server; hostname = config.instance.hostname; domain-name = config.instance.local-domain; domain = config.fudo.domains.${domain-name}; mailserver-host = domain.primary-mailserver; mailserver-domain-name = config.fudo.hosts.${mailserver-host}.domain; mailserver-domain = config.fudo.domains.${mailserver-domain-name}; mailserver-fqdn = "${mailserver-host}.${mailserver-domain-name}"; hasMailServer = mailserver-host != null; isMailServer = hostname == mailserver-host; isLocalMailserver = domain-name == mailserver-domain-name; metricsEnabled = mailserver-domain.prometheus-hosts != [ ]; host-certs = config.fudo.acme.host-domains.${hostname}; in { options.fudo.services.mail-server = with types; { debug = mkEnableOption "Enable debug options for mailserver."; state-directory = mkOption { type = str; description = "Directory at which to store mailserver state."; }; }; config = mkIf hasMailServer { services.nginx = mkIf (isMailServer && metricsEnabled) { enable = true; recommendedOptimisation = true; recommendedProxySettings = true; virtualHosts."mail-stats.${mailserver-domain-name}" = let trusted-networks = config.instance.local-networks; trustedNetworkString = optionalString (length trusted-networks > 0) (concatStringsSep "\n" (map (network: "allow ${network};") trusted-networks)) + '' deny all;''; in { enableACME = true; forceSSL = true; locations = let monitor-cfg = config.fudo.mail-server.monitoring; in { "/metrics/dovecot" = { proxyPass = "http://127.0.0.1:${ toString monitor-cfg.dovecot-listen-port }/metrics"; extraConfig = trustedNetworkString; }; "/metrics/postfix" = { proxyPass = "http://127.0.0.1:${ toString monitor-cfg.postfix-listen-port }/metrics"; extraConfig = trustedNetworkString; }; "/metrics/rspamd" = { proxyPass = "http://127.0.0.1:${ toString monitor-cfg.rspamd-listen-port }/metrics"; extraConfig = trustedNetworkString; }; }; }; }; fudo = { acme.host-domains = mkIf isMailServer { ${hostname} = { "imap.${mailserver-domain-name}" = { admin-email = "admin@${mailserver-domain-name}"; local-copies.dovecot = { user = config.services.dovecot2.user; dependent-services = [ "dovecot2.service" ]; }; }; "smtp.${mailserver-domain-name}" = { admin-email = "admin@${mailserver-domain-name}"; local-copies.postfix = { user = config.services.postfix.user; dependent-services = [ "postfix.service" ]; }; }; }; }; zones = { ${mailserver-domain.zone} = let server-ipv4 = pkgs.lib.network.host-ipv4 config mailserver-host; server-ipv6 = pkgs.lib.network.host-ipv6 config mailserver-host; srv-record = host: port: [{ inherit host port; }]; in { aliases = mkIf metricsEnabled { mail-stats = "${mailserver-fqdn}."; }; # srv-records.tcp = { # pop3 = srv-record mailserver-fqdn 110; # pop3s = srv-record mailserver-fqdn 995; # imap = srv-record mailserver-fqdn 143; # imaps = srv-record mailserver-fqdn 993; # smtp = srv-record mailserver-fqdn 25; # submission = srv-record mailserver-fqdn 587; # }; metric-records = mkIf metricsEnabled (genAttrs [ "dovecot" "postfix" "rspamd" ] (_: srv-record "mail-stats" 443)); }; }; metrics.prometheus.service-discovery-dns = mkIf metricsEnabled (genAttrs [ "dovecot" "postfix" "rspamd" ] (mtype: [ "${mtype}._metrics._tcp.${mailserver-domain-name}" ])); mail-server = mkIf isLocalMailserver { enable = isMailServer; domain = mailserver-domain-name; mail-hostname = "smtp.${mailserver-domain-name}"; monitoring.enable = metricsEnabled; debug = cfg.debug; clamav.enable = true; dkim.signing = true; dovecot = let cert-copy = host-certs."imap.${mailserver-domain-name}".local-copies.dovecot; in { ssl-certificate = cert-copy.full-certificate; ssl-private-key = cert-copy.private-key; }; postfix = let cert-copy = host-certs."smtp.${mailserver-domain-name}".local-copies.postfix; in { ssl-certificate = cert-copy.full-certificate; ssl-private-key = cert-copy.private-key; }; local-domains = [ mailserver-fqdn "smtp.${mailserver-domain-name}" ]; mail-directory = "${cfg.state-directory}/mail"; state-directory = "${cfg.state-directory}/state"; trusted-networks = let wrapIpv6 = net: let elems = builtins.split "/" net; in "[${elemAt elems 0}]/${elemAt elems 2}"; in map (network: if (builtins.match ".*::.*" network) != null then wrapIpv6 network else network) config.instance.local-networks; user-aliases = genAttrs (attrNames config.fudo.users) (username: config.fudo.users."${username}".email-aliases); alias-users = genAttrs [ "root" "postmaster" "hostmaster" "webmaster" "system" "admin" "dmarc-report" ] (alias: config.instance.local-admins); }; }; }; }