{ config, lib, pkgs, ... }: let cfg = config.services.stalwart-tlsa-cloudflare-update; in { options = { services.stalwart-tlsa-cloudflare-update = { enable = lib.mkOption { type = lib.types.bool; default = false; description = '' Enable stalwart-tlsa-cloudflare-update daemon. ''; }; settings = lib.mkOption { type = lib.types.submodule { freeformType = with lib.types; attrsOf anything; }; default = { }; description = '' Extra options which should be used by the stalwart-tlsa-cloudflare-update script. ''; }; secretFile = lib.mkOption { type = lib.types.nullOr (lib.types.pathWith { inStore = false; absolute = true; }); default = null; example = "/run/keys/cloudflare-api-key"; description = '' Secret file containing cloudflare api key ''; }; interval = lib.mkOption { type = lib.types.str; default = "*:00,30:00"; description = '' How often we run the sync. Default is half an hour. The format is described in {manpage}`systemd.time(7)`. ''; }; }; }; config = lib.mkIf cfg.enable { systemd.services.stalwart-tlsa-cloudflare-update = { description = "Check and update TLSA/DANE record for mx1 from Stalwart ACME Cert"; after = [ "network-online.target" "stalwart-mail.service" "agenix.service" ]; wants = [ "network-online.target" "stalwart-mail.service" "agenix.service" ]; serviceConfig = { Type = "oneshot"; User = "stalwart-mail"; Group = "stalwart-mail"; EnvironmentFile = cfg.secretFile; RuntimeDirectory = "stalwart-tlsa"; }; environment = lib.mapAttrs (_: v: toString v) cfg.settings; path = with pkgs; [ bash coreutils openssl dnsutils gotlsaflare rocksdb.tools gawk ]; script = '' set -eu 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.stalwart-tlsa-cloudflare-update = { description = "Run TLSA check and update"; wantedBy = [ "timers.target" ]; timerConfig = { OnCalendar = [ "" cfg.interval ]; }; }; }; meta = { maintainers = with lib.maintainers; [ onny ]; }; }