# DMARC report analyzer, imported by control only. parsedmarc fetches the # aggregate/forensic reports that land in the dmarc@cnx.email mailbox on mx1, # parses the XML, and stores results in a local Elasticsearch; the official # parsedmarc dashboard + an Elasticsearch datasource are auto-provisioned into # the Grafana instance that server.nix already runs on this host. # # IMAP runs over the ZeroTier mesh, not the public net: we pin mx1.cnx.email to # its mesh address in /etc/hosts so TLS still validates against the public # Let's Encrypt cert (primary domain mx1.cnx.email) while the bytes stay on the # overlay. The mailbox passphrase is the shared mail-dmarc-cred secret; parsedmarc # reads it as root in its ExecStartPre, so root-owned (clan default) is fine. { config, lib, ... }: let mesh = import ../mesh-hosts.nix { inherit config lib; }; in { imports = [ ../mail-dmarc-cred.nix ]; # Elasticsearch 7.x is under the (unfree) Elastic License; allow just this one # package rather than opening allowUnfree globally. nixpkgs.config.allowUnfreePredicate = pkg: lib.getName pkg == "elasticsearch"; # Keep mx1's IMAP traffic on the mesh while presenting the public cert name. networking.hosts.${mesh.hosts.mx1} = [ "mx1.cnx.email" ]; services.parsedmarc = { enable = true; provision = { # Local Elasticsearch on 127.0.0.1:9200 (loopback; no firewall change). # datasource + dashboard default to true once ES and Grafana are both on. elasticsearch = true; # GeoIP needs a MaxMind account/license key; skip it (reports still parse, # just without source-IP geolocation). geoIp = false; grafana = { datasource = true; dashboard = true; }; }; settings = { imap = { host = "mx1.cnx.email"; port = 993; ssl = true; user = "dmarc@cnx.email"; password = { _secret = config.clan.core.vars.generators.mail-dmarc-cred.files."passphrase".path; }; }; mailbox = { watch = true; # IMAP IDLE: process reports as they arrive delete = false; # archive processed reports, don't delete }; general = { save_aggregate = true; save_forensic = true; }; }; }; }