diff --git a/nixos/modules/mail.nix b/nixos/modules/mail.nix index 391778f..67e5877 100644 --- a/nixos/modules/mail.nix +++ b/nixos/modules/mail.nix @@ -101,7 +101,7 @@ in { # Reevaluate after DKIM and DMARC deployment spam.header.is-spam = "Dummyheader"; # disable moving to spam which would conflict with forwarding auth = { - # TODO check if HRZ conforms to these standards and we can validate them strictly + # TODO check if HRZ and our own VMs conform to these standards and we can validate them strictly dkim.verify = "relaxed"; arc.verify = "relaxed"; dmarc.verify = "relaxed"; @@ -140,52 +140,57 @@ in { starttls = "optional"; # e.g. Lobon does not offer starttls }; }; - remote."hrz" = { - address = "mailout.hrz.tu-darmstadt.de"; - port = 25; - protocol = "smtp"; - tls.implicit = false; # Don't assume TLS on this port but use STARTTLS - }; - remote."mailman" = { - address = "lobon.mathebau.de"; # must be created in DNS as a MX record because this field does not accept ip addresses. - port = 25; - protocol = "smtp"; - tls.implicit = false; # Don't assume TLS on this port but use STARTTLS + remote = { + "hrz" = { + address = "mailout.hrz.tu-darmstadt.de"; + port = 25; + protocol = "smtp"; + tls.implicit = false; # Don't assume TLS on this port but use STARTTLS + }; + "mailman" = { + address = "lobon.mathebau.de"; # must be created in DNS as a MX record because this field does not accept ip addresses. + port = 25; + protocol = "smtp"; + tls.implicit = false; # Don't assume TLS on this port but use STARTTLS + }; }; - session.rcpt = { - # In order to accept mail that we only forward - # without having to generate an account. - # Invalid addresses are filtered by DFN beforehand. - # See also https://stalw.art/docs/smtp/inbound/rcpt/#catch-all-addresses - catch-all = true; - relay = [ + session = { + ehlo.require = [ { - "if" = "!is_empty(authenticated_as) || rcpt_domain == 'lists.mathebau.de' || starts_with(remote_ip, '192.168.0.')"; #TODO restrict trust by IP - "then" = true; + "if" = "starts_with(remote_ip, '192.168.0.')"; #TODO setup vms properly + "then" = false; } - {"else" = false;} + {"else" = true;} + ]; + ehlo.reject-non-fqdn = [ + { + "if" = "starts_with(remote_ip, '192.168.0.')"; #TODO setup vms properly + "then" = false; + } + {"else" = true;} ]; - }; - session.ehlo.require = [ - { - "if" = "starts_with(remote_ip, '192.168.0.')"; #TODO setup vms properly - "then" = false; - } - {"else" = true;} - ]; - session.ehlo.reject-non-fqdn = [ - { - "if" = "starts_with(remote_ip, '192.168.0.')"; #TODO setup vms properly - "then" = false; - } - {"else" = true;} - ]; + rcpt = { + # In order to accept mail that we only forward + # without having to generate an account. + # Invalid addresses are filtered by DFN beforehand. + # See also https://stalw.art/docs/smtp/inbound/rcpt/#catch-all-addresses + catch-all = true; + relay = [ + { + "if" = "!is_empty(authenticated_as) || rcpt_domain == 'lists.mathebau.de' || starts_with(remote_ip, '192.168.0.')"; #TODO restrict trust by IP + "then" = true; + } + {"else" = false;} + ]; + }; + data.script = "'redirects'"; + }; # Stalwart gets its configuration from two places: A TOML configuration file that we control in this module # and from a database that can be configured from web management interface or via Rest API. - # We here define what comes from the TOML-file and especially add "sieve.trusted.scripts.*" to the default ones + # We here define what comes from the TOML-file and especially add "sieve.trusted.*" to the default ones # because only TOML-based keys may use macros to load files from disk. # We want this to be able to load our sieve-script for mail forwarding. # See https://stalw.art/docs/configuration/overview/#local-and-database-settings for more details. @@ -207,24 +212,24 @@ in { "certificate.*" ] # the default ones ++ ["sieve.trusted.*"]; #for macros to be able to include our redirection script - sieve.trusted.scripts.redirects.contents = "%{file:/tmp/virt_aliases}%"; # generated redirect script - sieve.trusted.from-addr = "sender"; # set the from-address to the original sender as specified in the MAIL FROM. - sieve.trusted.from-name = "sender"; - sieve.trusted.return-path = "sender"; - # If we are the sender, we sign the message with DKIM. Else we leave it alone. - sieve.trusted.sign = [ - { - "if" = "is_local_domain('', sender_domain) || sender_domain == 'lists.mathebau.de'"; - "then" = "['rsa-' + sender_domain, 'ed25519-' + sender_domain]"; - } - {"else" = false;} - ]; - sieve.trusted.limits = { - redirects = 50; - out-messages = 50; + sieve.trusted = { + scripts.redirects.contents = "%{file:/tmp/virt_aliases}%"; # generated redirect script + trusted.from-addr = "sender"; # set the from-address to the original sender as specified in the MAIL FROM. + from-name = "sender"; + return-path = "sender"; + # If we are the sender, we sign the message with DKIM. Else we leave it alone. + sign = [ + { + "if" = "is_local_domain('', sender_domain) || sender_domain == 'lists.mathebau.de'"; + "then" = "['rsa-' + sender_domain, 'ed25519-' + sender_domain]"; + } + {"else" = false;} + ]; + limits = { + redirects = 50; + out-messages = 50; + }; }; - session.data.script = "'redirects'"; - # See https://stalw.art/docs/smtp/authentication/dkim/sign # We need two blocks per domain because the domain setting in the blocks does not accept variables like `sender_domain`. signature = let @@ -249,15 +254,6 @@ in { in map signatureTemplate (["lists.mathebau.de"] ++ (map ({domain, ...}: domain) cfg.domains)); - # Sign *our* outgoing mails with the configured signatures. - auth.dkim.sign = [ - { - "if" = "is_local_domain('', sender_domain) || sender_domain == 'lists.mathebau.de'"; - "then" = "['rsa-' + sender_domain, 'ed25519-' + sender_domain]"; - } - {"else" = false;} - ]; - authentication.fallback-admin = { user = "admin"; # see passwd on azathoth for plaintext or machine secret in encoded format for HTTP Basic AUTH