diff --git a/hosts/prefect/default.nix b/hosts/prefect/default.nix index 8e45d632..90134be1 100644 --- a/hosts/prefect/default.nix +++ b/hosts/prefect/default.nix @@ -27,8 +27,7 @@ # ./services/dn42-peerfinder.nix ./services/fail2ban.nix # ./services/headscale.nix - # ./services/mailserver - ./services/mailserver/stalwart + ./services/mailserver # ./services/netdata.nix # ./services/nginx ./services/prometheus.nix diff --git a/hosts/prefect/services/mailserver/stalwart/acme.nix b/hosts/prefect/services/mailserver/acme.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/acme.nix rename to hosts/prefect/services/mailserver/acme.nix diff --git a/hosts/prefect/services/mailserver/stalwart/auth.nix b/hosts/prefect/services/mailserver/auth.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/auth.nix rename to hosts/prefect/services/mailserver/auth.nix diff --git a/hosts/prefect/services/mailserver/stalwart/auto-ban.nix b/hosts/prefect/services/mailserver/auto-ban.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/auto-ban.nix rename to hosts/prefect/services/mailserver/auto-ban.nix diff --git a/hosts/prefect/services/mailserver/stalwart/calendar.nix b/hosts/prefect/services/mailserver/calendar.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/calendar.nix rename to hosts/prefect/services/mailserver/calendar.nix diff --git a/hosts/prefect/services/mailserver/default.nix b/hosts/prefect/services/mailserver/default.nix index 086d7b2d..2e04d9eb 100644 --- a/hosts/prefect/services/mailserver/default.nix +++ b/hosts/prefect/services/mailserver/default.nix @@ -1,118 +1,217 @@ -{ lib, pkgs, ... }: { - imports = [ - ./logins.nix - ./monitoring.nix - ./overrides.nix - ]; - mailserver = { - enable = false; - fqdn = "mail.pyrox.dev"; - systemName = "PyroNet Mail"; - systemDomain = "mail.pyrox.dev"; - openFirewall = true; - stateVersion = 3; - - # All domains this server runs email for - domains = [ "pyrox.dev" ]; - - # Enable STARTTLS - enableImap = true; - enableSubmission = true; - - # Disable POP3, I don't use it and neither should you - enablePop3 = false; - enablePop3Ssl = false; - - # Enable ManageSieve so that we don't need to change the config to update sieves - enableManageSieve = true; - - # Set directories for services - mailDirectory = "/srv/mail/vmail"; - sieveDirectory = "/srv/mail/sieve"; - indexDir = "/var/lib/dovecot/indices"; - dkimKeyDirectory = "/srv/mail/dkim"; - - # Set all no-reply addresses - rejectRecipients = [ - "no-reply@pyrox.dev" - "dmarc-noreply@pyrox.dev" - ]; - - # DKIM Settings - dkimKeyBits = 4096; - dkimSelector = "mail"; - dkimSigning = true; - - # DMARC Settings - dmarcReporting = { - enable = true; + config, + lib, + self, + ... +}: +let + d = self.lib.data.mail; + cfg = config.services.stalwart-mail; + sec = config.age.secrets; + creds = config.services.stalwart-mail.credentials; + 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"; }; - - # Mailboxes for all users - mailboxes = { - Drafts = { - auto = "subscribe"; - specialUse = "Drafts"; + enable = true; + dataDir = "/var/lib/stalwart"; + settings = { + tracer.stdout.level = "info"; + authentication.fallback-admin = { + user = "fallback"; + secret = "%{file:${sec.stalwart-fallback-admin-pw.path}}%"; }; - Junk = { - auto = "subscribe"; - specialUse = "Junk"; + 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.*" + ]; }; - Sent = { - auto = "subscribe"; - specialUse = "Sent"; + 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" + ]; + }; }; - Trash = { - auto = "subscribe"; - specialUse = "Trash"; + 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; }; }; - - # Full-Text-Search Settings - fullTextSearch = { - enable = true; - autoIndex = true; - enforced = "body"; - memoryLimit = 2048; - }; - - # Certificate Settings - certificateScheme = "manual"; - certificateFile = "/var/lib/mail/mail.crt"; - keyFile = "/var/lib/mail/mail.key"; }; - - services.opendkim = { - user = lib.mkForce "virtualMail"; - group = lib.mkForce "virtualMail"; + systemd.services.stalwart-mail.serviceConfig = { + Restart = lib.mkForce "always"; + RestartSec = lib.mkForce 1; }; - - # Copy mail certs every month so that they don't expire - systemd = { - timers."copy-mail-certs" = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnBootSec = "5m"; - OnCalendar = "daily"; - Unit = "copy-mail-certs.service"; - }; + age.secrets = { + stalwart-secret-rsa = smSecret // { + file = ../../secrets/stalwart-secret-rsa.age; }; - - services."copy-mail-certs" = { - script = '' - set -eu - cp -fvr /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.pyrox.dev/mail.pyrox.dev.crt /var/lib/mail/mail.crt - chmod a+r /var/lib/mail/mail.crt - cp -fvr /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.pyrox.dev/mail.pyrox.dev.key /var/lib/mail/mail.key - chmod a+r /var/lib/mail/mail.key - chown -hR virtualMail:virtualMail /var/lib/mail/ - ''; - serviceConfig = { - Type = "oneshot"; - User = "root"; - }; + 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; }; }; - } diff --git a/hosts/prefect/services/mailserver/stalwart/imap.nix b/hosts/prefect/services/mailserver/imap.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/imap.nix rename to hosts/prefect/services/mailserver/imap.nix diff --git a/hosts/prefect/services/mailserver/logins.nix b/hosts/prefect/services/mailserver/logins.nix deleted file mode 100644 index 093e8e7b..00000000 --- a/hosts/prefect/services/mailserver/logins.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ - mailserver.loginAccounts = { - "pyrox@pyrox.dev" = { - hashedPassword = "$2b$05$8k04quBe6adg8d1yznEp3uNYM54MOVJTwDGIWvzocQFoWbmcCvebC"; - aliases = [ - "pyrox" - "postmaster@pyrox.dev" - "abuse@pyrox.dev" - "domains@pyrox.dev" - ]; - }; - "social@pyrox.dev" = { - hashedPassword = "$2b$05$kFDeXvSKU9oXuQXlitA7v.kkbzgCDTrm4O3Nb1kifPe7yAR7.KimO"; - sendOnly = true; - }; - "auth@pyrox.dev" = { - hashedPassword = "$2b$05$O049hbSwRJ5VYeAA8lLR4e6.fqVWf4PotgIUAO356j5K.OoGH5PF."; - sendOnly = true; - }; - "vault@pyrox.dev" = { - hashedPassword = "$2b$05$MHo03BG3AVpBh4NE97zQ8.gTPx2sCoa6Jsw.DRxHBOBaKZ8DbfPrS"; - sendOnly = true; - }; - "library@pyrox.dev" = { - hashedPassword = "$2b$05$IHsSbEla8KL4gwExvFECFuuoP0ESk66K29R.vawTpbxEpuw1ahii."; - sendOnly = true; - }; - "cloud@pyrox.dev" = { - hashedPassword = "$2b$05$kmbsJ2X3Y2l0KYO8jjy1SOJP29coEeKFaMqU6qvRzz/dLJp78CAk6"; - sendOnly = true; - }; - "git@pyrox.dev" = { - hashedPassword = "$2b$05$uZoLVdCo48rLVBFdG0.UXua8a.84w1PzmLYOpJ1qTNo25KCdQlflm"; - sendOnly = true; - }; - "share@pyrox.dev" = { - hashedPassword = "$2b$05$LDvYYmxYcTgqPMDvvhA.uO8UFh8yLqPzVuOdeYBq0x/WJ/85X3DEC"; - sendOnly = true; - }; - }; -} diff --git a/hosts/prefect/services/mailserver/monitoring.nix b/hosts/prefect/services/mailserver/monitoring.nix deleted file mode 100644 index fd95866d..00000000 --- a/hosts/prefect/services/mailserver/monitoring.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ config, pkgs, ... }: -# let -# cfg = config.mailserver; -# in -{ - mailserver.monitoring = { - enable = true; - alertAddress = "pyrox@pyrox.dev"; - config = '' - set daemon 120 with start delay 60 - set mailserver - localhost - set alert ${config.mailserver.monitoring.alertAddress} - - set httpd port 2812 and use address localhost - allow localhost - allow admin:obwjoawijerfoijsiwfj29jf2f2jd - - check filesystem root with path / - if space usage > 80% then alert - if inode usage > 80% then alert - - check system $HOST - if cpu usage > 95% for 10 cycles then alert - if memory usage > 75% for 5 cycles then alert - if swap usage > 20% for 10 cycles then alert - if loadavg (1min) > 90 for 15 cycles then alert - if loadavg (5min) > 80 for 10 cycles then alert - if loadavg (15min) > 70 for 8 cycles then alert - - check process postfix with pidfile /var/lib/postfix/queue/pid/master.pid - start program = "${pkgs.systemd}/bin/systemctl start postfix" - stop program = "${pkgs.systemd}/bin/systemctl stop postfix" - if failed port 25 protocol smtp for 5 cycles then restart - - check process dovecot with pidfile /var/run/dovecot2/master.pid - start program = "${pkgs.systemd}/bin/systemctl start dovecot2" - stop program = "${pkgs.systemd}/bin/systemctl stop dovecot2" - if failed host ${config.mailserver.fqdn} port 993 type tcpssl sslauto protocol imap for 5 cycles then restart - - check process rspamd with matching "rspamd: main process" - start program = "${pkgs.systemd}/bin/systemctl start rspamd" - stop program = "${pkgs.systemd}/bin/systemctl stop rspamd" - ''; - }; -} diff --git a/hosts/prefect/services/mailserver/overrides.nix b/hosts/prefect/services/mailserver/overrides.nix deleted file mode 100644 index 34e2c1cf..00000000 --- a/hosts/prefect/services/mailserver/overrides.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ lib, ... }: -let - inherit (lib) mkForce; - tlsProtocols = ">=TLSv1.2"; - excludeCiphers = "MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL, AES128-SHA, AES256-SHA"; -in -{ - services.postfix.config = { - # only support TLS 1.3/1.2 - smtpd_tls_protocols = mkForce tlsProtocols; - smtp_tls_protocols = mkForce tlsProtocols; - smtpd_tls_mandatory_protocols = mkForce tlsProtocols; - smtp_tls_mandatory_protocols = mkForce tlsProtocols; - - # Exclude insecure ciphers - smtpd_tls_mandatory_exclude_ciphers = mkForce excludeCiphers; - smtpd_tls_exclude_ciphers = mkForce excludeCiphers; - smtp_tls_mandatory_exclude_ciphers = mkForce excludeCiphers; - smtp_tls_exclude_ciphers = mkForce excludeCiphers; - }; -} diff --git a/hosts/prefect/services/mailserver/stalwart/queue.nix b/hosts/prefect/services/mailserver/queue.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/queue.nix rename to hosts/prefect/services/mailserver/queue.nix diff --git a/hosts/prefect/services/mailserver/stalwart/report.nix b/hosts/prefect/services/mailserver/report.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/report.nix rename to hosts/prefect/services/mailserver/report.nix diff --git a/hosts/prefect/services/mailserver/stalwart/server.nix b/hosts/prefect/services/mailserver/server.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/server.nix rename to hosts/prefect/services/mailserver/server.nix diff --git a/hosts/prefect/services/mailserver/stalwart/session.nix b/hosts/prefect/services/mailserver/session.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/session.nix rename to hosts/prefect/services/mailserver/session.nix diff --git a/hosts/prefect/services/mailserver/stalwart/signature.nix b/hosts/prefect/services/mailserver/signature.nix similarity index 100% rename from hosts/prefect/services/mailserver/stalwart/signature.nix rename to hosts/prefect/services/mailserver/signature.nix diff --git a/hosts/prefect/services/mailserver/stalwart/default.nix b/hosts/prefect/services/mailserver/stalwart/default.nix deleted file mode 100644 index 775f4155..00000000 --- a/hosts/prefect/services/mailserver/stalwart/default.nix +++ /dev/null @@ -1,217 +0,0 @@ -{ - config, - lib, - self, - ... -}: -let - d = self.lib.data.mail; - cfg = config.services.stalwart-mail; - sec = config.age.secrets; - creds = config.services.stalwart-mail.credentials; - 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; - }; - }; -}