{ domain, email, ssl, }: let subdomain = "mx.${domain}"; tlsModule = import ./mta-sts.nix { inherit domain ssl; }; autoconfigModule = import ./autoconfig.nix { inherit domain; hosts = [ { name = "autoconfig.${domain}"; inherit ssl; } ]; }; in { imports = [ ../databases/postgresql.nix ./rspamd.nix tlsModule autoconfigModule ]; services.maddy = { enable = true; primaryDomain = subdomain; localDomains = [ "$(hostname)" ]; tls = { loader = "acme"; extraConfig = '' email ${email} agreed hostname ${subdomain} challenge dns-01 dns cloudflare { api_token "{env:CF_API_TOKEN}" } ''; }; config = '' auth.pass_table local_authdb { table sql_table { driver postgres dsn "host=/run/postgresql dbname=maddy user=maddy" table_name passwords } } storage.imapsql local_mailboxes { driver postgres dsn "host=/run/postgresql dbname=maddy user=maddy" } table.chain local_rewrites { optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3" optional_step static { entry postmaster postmaster@$(primary_domain) } optional_step file /etc/maddy/aliases } msgpipeline local_routing { check { rspamd { api_path unix:/run/rspamd/rspamd.sock } } destination postmaster $(local_domains) { modify { replace_rcpt &local_rewrites } deliver_to &local_mailboxes } default_destination { reject 550 5.1.1 "User doesn't exist" } } smtp tcp://0.0.0.0:25 { limits { all rate 25 1s all concurrency 10 } dmarc yes max_message_size 25M check { require_mx_record dkim spf } source $(local_domains) { reject 501 5.1.8 "Use Submission for outgoing SMTP" } default_source { destination postmaster $(local_domains) { deliver_to &local_routing } default_destination { reject 550 5.1.1 "User doesn't exist" } } } submission tls://0.0.0.0:465 tcp://0.0.0.0:587 { limits { all rate 25 1s } auth &local_authdb source $(local_domains) { check { authorize_sender { prepare_email &local_rewrites user_to_email identity } } destination postmaster $(local_domains) { deliver_to &local_routing } default_destination { modify { dkim $(primary_domain) $(local_domains) default } deliver_to &remote_queue } } default_source { reject 501 5.1.8 "Non-local sender domain" } } imap tls://0.0.0.0:993 tcp://0.0.0.0:143 { auth &local_authdb storage &local_mailboxes } target.remote outbound_delivery { limits { destination rate 25 1s destination concurrency 10 } mx_auth { dane mtasts { cache ram } local_policy { min_tls_level encrypted min_mx_level none } } } target.queue remote_queue { target &outbound_delivery autogenerated_msg_domain $(primary_domain) bounce { destination postmaster $(local_domains) { deliver_to &local_routing } default_destination { reject 550 5.0.0 "Refusing to send DSNs to non-local addresses" } } } ''; }; networking.firewall.allowedTCPPorts = [ 25 587 465 143 993 ]; # Ensure creation of PostgreSQL database services.postgresql = { ensureUsers = [ { name = "maddy"; ensureDBOwnership = true; } ]; ensureDatabases = [ "maddy" ]; }; # Configure rspamd services.rspamd = { locals."dkim_signing.conf".text = '' selector = "default"; domain = "${subdomain}"; path = "/var/lib/maddy/dkim_keys/$domain_$selector.key"; ''; }; systemd.services.rspamd.serviceConfig.SupplementaryGroups = [ "maddy" ]; }