{ config, lib, self, ... }: let d = self.lib.data.mail; cfg = config.services.stalwart-mail; sec = config.age.secrets; credsDir = "/run/credentials/stalwart-mail.service"; certDir = config.security.acme.certs."pyroxdev-mail".directory; isAuthenticated = d: { "if" = "!is_empty(authenticated_as)"; "then" = d; }; otherwise = d: { "else" = d; }; ifThen = f: d: { "if" = f; "then" = d; }; smSecret = { owner = "stalwart-mail"; group = "stalwart-mail"; }; in { services.stalwart-mail = { credentials = { cert = "${certDir}/cert.pem"; key = "${certDir}/key.pem"; }; enable = true; dataDir = "/var/lib/stalwart"; settings = { tracer.stdout.level = "info"; authentication.fallback-admin = { user = "fallback"; secret = "%{file:${sec.stalwart-fallback-admin-pw.path}}%"; }; config = { local-keys = [ "asn.*" "auth.*" "authentication.*" "auto-ban.*" "calendar.*" "certificate.*" "changes.*" "cluster.*" "config.*" "contacts.*" "directory.*" "http.*" "imap.*" "jmap.*" "queue.*" "report.*" "resolver.*" "server.*" "session.*" "signature.*" "storage.*" "store.*" "tracer.*" "webadmin.*" "form.*" "email.*" "spam-filter.*" ]; }; certificate = { default = { default = true; cert = "%{file:${credsDir}/cert}%"; private-key = "%{file:${credsDir}/key}%"; subjects = [ "dav.pyrox.dev" "mail.pyrox.dev" "mta-sts.pyrox.dev" "autoconfig.pyrox.dev" "autodiscover.pyrox.dev" ]; }; }; server = import ./server.nix { inherit d; }; # Use NixOS-generated certs now, since stalwart can't do it on its own # (DeSec API Errors abound) # acme = import ./acme.nix { inherit cfg sec; }; # HTTP Configuration # https://stalw.art/docs/http/overview http = { url = "'https://${d.extUrl}'"; hsts = true; rate-limit = { account = "10000/1m"; }; }; # Disable HTTP Forms submission # https://stalw.art/docs/http/form-submission form.enable = false; # DKIM Signatures signature = import ./signature.nix { inherit sec; }; # Storage Settings # https://stalw.art/docs/storage/overview store = { data = { type = "rocksdb"; path = "${cfg.dataDir}/db"; purge.frequency = "0 3 *"; }; blob = { type = "fs"; path = "${cfg.dataDir}/blobs"; depth = 2; compression = "lz4"; purge.frequency = "0 4 *"; }; db.path = "${cfg.dataDir}/db2"; }; storage = { data = "data"; blob = "blob"; fts = "data"; lookup = "data"; directory = "default"; }; directory = { default = { type = "internal"; store = "data"; }; }; # ASN/GeoIP Lookups # https://stalw.art/docs/server/asn asn = { type = "dns"; separator = "|"; zone.ipv4 = "origin.asn.cymru.com"; zone.ipv6 = "origin6.asn.cymru.com"; index.asn = 0; index.asn-name = 1; index.country = 2; }; auto-ban = import ./auto-ban.nix; # JMAP Settings # https://stalw.art/docs/email/jmap jmap = { mailbox.max-depth = 10; mailbox.max-name-length = 255; # 50 MB email.max-attachment-size = 50 * 1000 * 1000; # 75 MB email.max-size = 75 * 1000 * 1000; email.parse.max-items = 10; }; imap = import ./imap.nix; # Maintainance # https://stalw.art/docs/email/maintenance email.auto-expunge = "180d"; changes.max-history = 10000; session = import ./session.nix { inherit isAuthenticated otherwise ifThen; }; queue = import ./queue.nix { inherit d ifThen otherwise; }; # DNS Settings # https://stalw.art/docs/mta/outbound/dns resolver = { custom = [ "tls://dns11.quad9.net" "tcp://1.1.1.1" ]; concurrency = 2; preserve-intermediates = true; timeout = "5s"; attempts = 3; edns = true; }; report = import ./report.nix { inherit d; }; calendar = import ./calendar.nix; # Authentication auth = import ./auth.nix { inherit ifThen otherwise; }; # Contacts # https://stalw.art/docs/collaboration/contact contacts = { # 512 KiB max-size = 524288; default.href-name = "default"; default.display-name = "Contacts"; }; # Spam Filtering # https://stalw.art/docs/spamfilter/overview spam-filter = { card-is-ham = true; }; }; }; systemd.services.stalwart-mail.serviceConfig = { Restart = lib.mkForce "always"; RestartSec = lib.mkForce 1; }; age.secrets = { stalwart-secret-rsa = smSecret // { file = ../../secrets/stalwart-secret-rsa.age; }; stalwart-secret-ed25519 = smSecret // { file = ../../secrets/stalwart-secret-ed25519.age; }; stalwart-desec-token = smSecret // { file = ../../secrets/stalwart-desec-token.age; }; stalwart-fallback-admin-pw = smSecret // { file = ../../secrets/stalwart-fallback-admin-pw.age; }; }; }