Stalwart: Difference between revisions

Onny (talk | contribs)
Add mail alias configuration
Onny (talk | contribs)
Tips and tricks: Auto update TLSA
Line 247: Line 247:


== Tips and tricks ==
== Tips and tricks ==
=== Auto update TLSA records ===
Stalwart [https://github.com/stalwartlabs/stalwart/issues/1664 does not yet] automatically update the TLSA record if your ACME certificate changes.
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=
systemd.services.tlsa-cloudflare-update = {
  description = "Check and update TLSA/DANE record for mx1 from Stalwart ACME Cert";
 
  after = [
    "network-online.target"
    "stalwart-mail.service"
  ];
  wants = [
    "network-online.target"
    "stalwart-mail.service"
  ];
 
  serviceConfig = {
    Type = "oneshot";
    User = "stalwart-mail";
    Group = "stalwart-mail";
    EnvironmentFile = config.age.secrets.stalwart-mail-cloudflare-secret.path;
    RuntimeDirectory = "stalwart-tlsa";
  };
  path = with pkgs; [
    bash
    coreutils
    openssl
    dnsutils
    gotlsaflare
    rocksdb.tools
    gawk
  ];
  script = ''
    set -eu
    DOMAIN="example.org"
    SUBDOMAIN="mail"
    PORT="25"
    ACME_PROVIDER_ID="cloudflare"
    TLSA_RECORD="_$PORT._tcp.$SUBDOMAIN.$DOMAIN"
    DB_PATH="/var/lib/stalwart-mail/db"
    TEMP_RAW="/run/stalwart-tlsa/cert.bundle"
    TEMP_CRT="/run/stalwart-tlsa/cert.crt"
    echo "Starting TLSA update process for $DOMAIN"
    ldb --db="$DB_PATH" --column_family=s get "acme.$ACME_PROVIDER_ID.cert" | base64 -d > "$TEMP_RAW"
    if [ ! -s "$TEMP_RAW" ]; then
      echo "ERROR: ACME certificate extraction failed"
      exit 1
    fi
    openssl x509 -in "$TEMP_RAW" -out "$TEMP_CRT"
    LOCAL_HASH=$(openssl x509 -in "$TEMP_CRT" -pubkey -noout | openssl pkey -pubin -outform DER | openssl sha256 | awk '{print tolower($2)}')
    echo "Local hash: $LOCAL_HASH"
    UPSTREAM_HASH=$(dig +nosplit +short TLSA "$TLSA_RECORD" | awk '{print tolower($4)}' | head -n1)
    echo "Upstream hash: $UPSTREAM_HASH"
    if [ "$LOCAL_HASH" = "$UPSTREAM_HASH" ]; then
      echo "Hashes match. DNS is up to date."
      exit 0
    fi
    echo "Hashes differ! Updating Cloudflare..."
    gotlsaflare update \
      --url "$DOMAIN" \
      --subdomain "$SUBDOMAIN" \
      --tcp"$PORT" \
      --cert "$TEMP_CRT"
    echo "TLSA update completed successfully."
  '';
};
systemd.timers.tlsa-cloudflare-update = {
  description = "Run TLSA check and update every 5 minutes";
  wantedBy = [ "timers.target" ];
  timerConfig = {
    OnBootSec = "2m";
    OnUnitActiveSec = "5m";
    Unit = "tlsa-cloudflare-update.service";
  };
};
}}


=== Test mail server ===
=== Test mail server ===