Stalwart: Difference between revisions

Onny (talk | contribs)
Tips and tricks: Auto update TLSA
Onny (talk | contribs)
Use stalwart-mail.credentials for secrets handling
 
(10 intermediate revisions by 3 users not shown)
Line 14: Line 14:
   enable = true;
   enable = true;
   openFirewall = true;
   openFirewall = true;
  credentials = {
    mail-pw1 = /etc/stalwart/mail-pw1;
    mail-pw2 = /etc/stalwart/mail-pw2;
    acme-secret = /etc/stalwart/acme-secret;
  };
   settings = {
   settings = {
     server = {
     server = {
Line 29: Line 34:
           bind = "[::]:465";
           bind = "[::]:465";
           protocol = "smtp";
           protocol = "smtp";
           tls.implicit = true
           tls.implicit = true;
         };
         };
         imaps = {
         imaps = {
           bind = "[::]:993";
           bind = "[::]:993";
           protocol = "imap";
           protocol = "imap";
           tls.implicit = true
           tls.implicit = true;
         };
         };
         jmap = {
         jmap = {
Line 57: Line 62:
       domains = [ "example.org" "mx1.example.org" ];
       domains = [ "example.org" "mx1.example.org" ];
       provider = "cloudflare";
       provider = "cloudflare";
       secret = "%{file:/etc/stalwart/acme-secret}%";
       secret = "%{file:/run/credentials/stalwart-mail.service/acme-secret}%";
     };
     };
     session.auth = {
     session.auth = {
Line 72: Line 77:
           class = "individual";
           class = "individual";
           name = "User 1";
           name = "User 1";
           secret = "%{file:/etc/stalwart/mail-pw1}%";
           secret = "%{file:/run/credentials/stalwart-mail.service/mail-pw1}%";
           email = [ "user1@example.org" ];
           email = [ "user1@example.org" ];
         }
         }
Line 78: Line 83:
           class = "individual";
           class = "individual";
           name = "postmaster";
           name = "postmaster";
           secret = "%{file:/etc/stalwart/mail-pw1}%";
           secret = "%{file:/run/credentials/stalwart-mail.service/mail-pw1}%";
           email = [ "postmaster@example.org" ];
           email = [ "postmaster@example.org" ];
         }
         }
Line 85: Line 90:
     authentication.fallback-admin = {
     authentication.fallback-admin = {
       user = "admin";
       user = "admin";
       secret = "%{file:/etc/stalwart/admin-pw}%";
       secret = "%{file:/run/credentials/stalwart-mail.service/admin-pw}%";
     };
     };
   };
   };
Line 222: Line 227:
  _25._tcp.mx1.example.org. 10800 IN RRSIG TLSA 13 5 10800 20230601000000 20230511000000 39688 example.org. He9VYZ35xTC3fNo8GJa6swPrZodSnjjIWPG6Th2YbsOEKTV1E8eGtJ2A +eyBd9jgG+B3cA/jw8EJHmpvy/buCw==
  _25._tcp.mx1.example.org. 10800 IN RRSIG TLSA 13 5 10800 20230601000000 20230511000000 39688 example.org. He9VYZ35xTC3fNo8GJa6swPrZodSnjjIWPG6Th2YbsOEKTV1E8eGtJ2A +eyBd9jgG+B3cA/jw8EJHmpvy/buCw==


=== Running behind reverse proxy ===
When running behind a load balancer or reverse proxy, Stalwart will not be able to see the "real" sender IP-addresses of incoming mails in case of simple port forwarding. [[HAProxy]] or Proxy Protocol solves this problem and should be used on the reverse proxy server to forward SMTP traffic. Stalwart will start parsing the Proxy Protocol packages if correctly configured on the listener.{{file|||3=services.stalwart-mail = {
  settings = {
    server = {
      listener = {
        smtp = {
          protocol = "smtp";
          bind = "[::]:25";
          proxy.trusted-networks = [
            "10.250.0.1/32"
            "fdc9:281f:4d7:9ee9::1/128"
          ];
        };
        [...]
      };
    };
  };
};|name=/etc/nixos/configuration.nix|lang=nix}}In this example we set <code>proxy.trusted-networks</code> with an array of the gateway IP-addresses in the <code>smtp</code> listener section.


== Configuration ==
== Configuration ==
Line 238: Line 261:
           class = "individual";
           class = "individual";
           name = "User 1";
           name = "User 1";
           secret = "%{file:/etc/stalwart/mail-pw1}%";
           secret = "%{file:/run/credentials/stalwart-mail.service/mail-pw1}%";
           email = [ "user1@example.org" "user1real@example.org ];
           email = [ "user1@example.org" "user1real@example.org ];
         }
         }
Line 245: Line 268:
   };
   };
};}}
};}}
=== Blocking mail sender address ===
If you don't want to receive any mails from a specific address, even not into your spam folder, you can add it to the spam-trap array.{{file|||3=services.stalwart-mail = {
  settings = {
    lookup = {
      spam-trap = {
        "malicious_sender1@spamhost.com" = "";
        "malicious_sender2@spamhost.com" = "";
      };
  };
};|name=/etc/nixos/configuration.nix|lang=nix}}


== Tips and tricks ==
== Tips and tricks ==
Line 254: Line 287:
Following script is a possible workaounrd. It extracts the ACME cert every five minute, calculates the TLSA hash and compares it with the upstream record. If it doesn't match, it uses [https://github.com/Stenstromen/gotlsaflare gotlsaflare] to update the TLSA record on Cloudflare.
Following script is a possible workaounrd. It extracts the ACME cert every five minute, calculates the TLSA hash and compares it with the upstream record. If it doesn't match, it uses [https://github.com/Stenstromen/gotlsaflare gotlsaflare] to update the TLSA record on Cloudflare.


{{file|/etc/nixos/configuration.nix|nix|3=
<syntaxhighlight lang="nixos">
systemd.services.tlsa-cloudflare-update = {
systemd.services.tlsa-cloudflare-update = {
   description = "Check and update TLSA/DANE record for mx1 from Stalwart ACME Cert";
   description = "Check and update TLSA/DANE record for mx1 from Stalwart ACME Cert";
Line 271: Line 304:
     User = "stalwart-mail";
     User = "stalwart-mail";
     Group = "stalwart-mail";
     Group = "stalwart-mail";
     EnvironmentFile = config.age.secrets.stalwart-mail-cloudflare-secret.path;
     EnvironmentFile = config.age.secrets.gotlsaflare-cloudflare-token.path;
     RuntimeDirectory = "stalwart-tlsa";
     RuntimeDirectory = "stalwart-tlsa";
   };
   };
 
  environment = {
    DOMAIN = "example.org";
    SUBDOMAIN = "mail";
    PORT = "25";
    ACME_PROVIDER_ID = "cloudflare";
  };
   path = with pkgs; [
   path = with pkgs; [
     bash
     bash
Line 288: Line 326:
     set -eu
     set -eu


    DOMAIN="example.org"
    SUBDOMAIN="mail"
    PORT="25"
    ACME_PROVIDER_ID="cloudflare"
     TLSA_RECORD="_$PORT._tcp.$SUBDOMAIN.$DOMAIN"
     TLSA_RECORD="_$PORT._tcp.$SUBDOMAIN.$DOMAIN"
     DB_PATH="/var/lib/stalwart-mail/db"
     DB_PATH="/var/lib/stalwart-mail/db"
Line 339: Line 373:
   };
   };
};
};
}}
</syntaxhighlight>
 
Adapt the variables <code>DOMAIN</code>, <code>SUBDOMAIN</code>, and <code>PORT</code> according to your needs. The variable <code>ACME_PROVIDER_ID</code> corresponds to the ACME profile name you've setup in the Stalwart webadmin interface. <code>EnvironmentFile</code> points to a file containing the secret Cloudflare api token in the format: TOKEN=12345678[...].
 
==== deSEC.io ====
 
In case you want to update your TLSA records at deSEC you can use [https://codeberg.org/Cameo007/dyndns-tlsa-desec dyndns-tlsa-desec] ('''install via flake''') which checks your existing records and updates them if necessary. The certificate and key are taken from the specified directory (like your [[ACME]] directory)
 
It defaults to <code>3 1 1</code> but you can choose other values as described [[wikipedia:DNS-based_Authentication_of_Named_Entities#RR_data_fields|here]].<syntaxhighlight lang="nixos">
services.dyndns-tlsa-desec = {
  enable = true;
  api_token_file = config.age.secrets.dyndns-tlsa-desec-api-key.path;
 
  tlsa_zones."example.com" = {
    cert_path = "/var/lib/acme/example.com/";
    records."_25._tcp.mail" = { };
  };
};
 
</syntaxhighlight>The program is executed hourly per default but you can set the <code>interval</code> option to any [https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html#Calendar%20Events systemd calendar event].<syntaxhighlight lang="nixos">
services.dyndns-tlsa-desec.interval = "5m"; # Every 5 minutes
</syntaxhighlight>
 
=== Sending from subaddresses ===
 
Receiving mails to subaddresses like <code>john+secondary@example.org</code> is enabled by default. Sending from subaddresses will fail with "You are not allowed to send from this address" as long as they are not an configured alias address. You can disable this check but it will allow any authenticated user to send from any other address.


{{file|/etc/nixos/configuration.nix|nix|3=services.stalwart-mail = {
  settings = {
    [...]
    session.auth.must-match-sender = false;
  };
};}}


A configuration option to customize the pattern of authorized sender addresses is a [https://github.com/stalwartlabs/stalwart/issues/394#issuecomment-3705990056 planned feature].
=== Test mail server ===
=== Test mail server ===
You can use several online tools to test your mail server configuration:
You can use several online tools to test your mail server configuration:


* [https://en.internet.nl/test-mail en.internet.nl/test-mail]: Test your mail server configuration for validity and security.
* [https://en.internet.nl/test-mail en.internet.nl/test-mail]: Test your mail server configuration for validity and security.
* [https://www.hardenize.com/ hardenize.com]: Test your mail server configuration for validity and security. Checks DANE validity even when not all MX servers support DANE.
* [https://www.mail-tester.com mail-tester.com]: Send a mail to this service and get a rating about the "spaminess" of your mail server.
* [https://www.mail-tester.com mail-tester.com]: Send a mail to this service and get a rating about the "spaminess" of your mail server.
* Send a mail to the echo server <code>echo@univie.ac.at</code>. You should receive a response containing your message in several seconds.
* Send a mail to the echo server <code>echo@univie.ac.at</code>. You should receive a response containing your message in several seconds.