{ pkgs, vhackPackages, }: { scripts = { expectNoNewMail = pkgs.writeShellScript "assert-empty-emails" '' set -xe # fetchmail returns EXIT_CODE 1 when no new mail fetchmail --verbose >&2 || [ "$?" -eq 1 ] || { echo "Mail was not empty" >&2 exit 1 } ''; expectNewMail = pkgs.writeShellScript "assert-empty-emails" '' set -xe # fetchmail returns EXIT_CODE 1 when no new mail fetchmail --verbose >&2 || [ "$?" -ne 1 ] || { echo "No new mail" >&2 exit 1 } ''; expectNewSpamMail = pkgs.writeShellScript "assert-empty-emails" '' set -xe # fetchmail returns EXIT_CODE 1 when no new mail fetchmail --folder JUNK --verbose >&2 || [ "$?" -ne 1 ] || { echo "No new spam mail" >&2 exit 1 } ''; }; mkMailServer = serverName: userName: { config, lib, nodes, ... }: { age.identityPaths = ["${../../common/email/hostKey}"]; systemd.tmpfiles.rules = [ #- path mode user group - content "f /passwords/${userName} 777 root root - ${userName}-password" ]; age.secrets = { "mailDkim" = { file = ../../common/email/dkim + "/${serverName}/private.age"; mode = "600"; owner = "rspamd"; group = "rspamd"; }; }; # Disable rspamd. rspamd can't do the dns lookups in the sandbox for # determining if something is spam or not. Therefore it just rejects every mail with # 'Try again later.'. # services.rspamd.enable = lib.mkForce false; # users.users.rspamd.group = "rspamd"; # users.groups.rspamd = {}; # # services.postfix.settings.main = { # smtpd_milters = lib.mkForce []; # non_smtpd_milters = lib.mkForce []; # }; # mailserver.dkim.enable = false; services = { rspamd = { locals = { "asn.conf".text = lib.mkForce '' enabled = false; ''; "rbl.conf".text = lib.mkForce '' enabled = false; ''; "greylist.conf".text = lib.mkForce '' enabled = false; ''; }; }; }; # This DNS resolver is only there for rspamd (and apparently not even needed). But it # also removes our own name server changes, so the test node could no longer find # e.g. `acme.test` and so on. # See: https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/work_items/100 mailserver.localDnsResolver = false; vhack = { mail = { enable = true; fqdn = serverName; domains = [ serverName ]; security.dkimKeys."${serverName}" = { dkimPrivateKeyPath = config.age.secrets."mailDkim".path; dkimPublicKey = builtins.readFile (../../common/email/dkim + "/${serverName}/public"); keyAlgorithm = "rsa-sha-256"; }; accounts = { "${userName}@${serverName}" = { passwordFile = "/passwords/${userName}"; }; }; }; }; }; mkDnsZone = serverName: {config, ...}: let cfg = config.vhack.mail; keyAlgoToKeyType = keyAlgo: if keyAlgo == "ed25519-sha256" then "ed25519" else if keyAlgo == "rsa-sha-256" || keyAlgo == "rsa-sha-1" then "rsa" else builtins.throw "Impossible"; in { "${serverName}" = { MX = [ { preference = 10; exchange = "${serverName}."; } ]; # https://www.rfc-editor.org/rfc/rfc6376.html#section-3.6.1 # https://www.rfc-editor.org/rfc/rfc6376.html#section-7.5 DKIM = [ { selector = "mail"; k = keyAlgoToKeyType cfg.security.dkimKeys."${serverName}".keyAlgorithm; p = cfg.security.dkimKeys."${serverName}".dkimPublicKey; s = ["email"]; t = ["s"]; } ]; # https://www.rfc-editor.org/rfc/rfc7489.html#section-6.3 DMARC = [ { adkim = "strict"; aspf = "strict"; fo = ["0" "1" "d" "s"]; p = "quarantine"; rua = "admin@vhack.eu"; ruf = ["admin@vhack.eu"]; } ]; # https://www.rfc-editor.org/rfc/rfc7208.html # NOTE(@bpeetz): This server might not be directly sending mail, but it is still required for # the SMTP EHLO check. <2025-02-25> TXT = [ (builtins.concatStringsSep " " [ "v=spf1" # The version. "+mx" # Allow mail from this domain MX record. "-all" # Reject all other emails if the previous mechanism did not match. ]) ]; }; }; mkUser = user: mailServer: {nodes, ...}: { environment.systemPackages = [ vhackPackages.fetchmail-common-name pkgs.msmtp pkgs.procmail ]; users.users."${user}" = {isNormalUser = true;}; systemd.tmpfiles.rules = [ "d /home/${user}/mail 0700 ${user} users - -" "L /home/${user}/.fetchmailrc - - - - /etc/homeSetup/.fetchmailrc" "L /home/${user}/.procmailrc - - - - /etc/homeSetup/.procmailrc" "L /home/${user}/.msmtprc - - - - /etc/homeSetup/.msmtprc" ]; environment.etc = { "homeSetup/.fetchmailrc" = { text = '' poll "${mailServer}" protocol IMAP username "${user}@${mailServer}" password "${user}-password" ssl mda procmail; ''; mode = "0600"; inherit user; }; "homeSetup/.procmailrc" = { text = '' DEFAULT=$HOME/mail ''; mode = "0600"; inherit user; }; "homeSetup/.msmtprc" = { text = '' account ${user} host ${mailServer} domain ${mailServer} port 465 from ${user}@${mailServer} user ${user}@${mailServer} password ${user}-password auth on tls on tls_starttls off ''; mode = "0600"; inherit user; }; }; }; }