{ config, pkgs, lib, flakeInputs, ... }:

with lib;

let
  dovecotSievePipeBin = pkgs.stdenv.mkDerivation {
    name = "dovecot-sieve-pipe-bin";
    src = ./dovecot-pipe-bin;
    buildInputs = with pkgs; [ makeWrapper coreutils bash rspamd ];
    buildCommand = ''
      mkdir -p $out/pipe/bin
      cp $src/* $out/pipe/bin/
      chmod a+x $out/pipe/bin/*
      patchShebangs $out/pipe/bin

      for file in $out/pipe/bin/*; do
        wrapProgram $file \
          --set PATH "${pkgs.coreutils}/bin:${pkgs.rspamd}/bin"
      done
    '';
  };

  ccert-policy-server = flakeInputs.mach-nix.lib.${config.nixpkgs.system}.buildPythonPackage {
    src = ./ccert-policy-server;
    pname = "ccert-policy-server";
    version = "0.0.0";

    python = "python39";
    ignoreDataOutdated = true;

    requirements = ''
      sdnotify
      systemd-socketserver
      psycopg >=3.0.0
      psycopg-pool >=3.0.0
      psycopg-binary >=3.0.0
    '';

    overridesPre = [
      (self: super: { systemd-python = super.systemd.overrideAttrs (oldAttrs: { pname = "systemd-python"; }); })
    ];
  };

  spmDomains = ["bouncy.email"];
  emailDomains = spmDomains ++ ["kleen.consulting"];
in {
  config = {
    nixpkgs.overlays = [
      (final: prev: {
        postfix = prev.postfix.override {
          withLDAP = false;
          withPgSQL = true;
        };
        dovecot = prev.dovecot.override {
          withSQLite = false;
          withPgSQL = true;
        };
      })
    ];

    services.postfix = {
      enable = true;
      enableSmtp = false;
      hostname = "surtr.yggdrasil.li";
      recipientDelimiter = "";
      setSendmail = true;
      postmasterAlias = ""; rootAlias = ""; extraAliases = "";
      destination = [];
      sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem";
      sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem";
      networks = [];
      config = let
        relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}";
      in {
        smtpd_tls_security_level = "may";

        #the dh params
        smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path;
        smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path;
        #enable ECDH
        smtpd_tls_eecdh_grade = "strong";
        #enabled SSL protocols, don't allow SSLv2 and SSLv3
        smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1"];
        smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1"];
        #allowed ciphers for smtpd_tls_security_level=encrypt
        smtpd_tls_mandatory_ciphers = "medium";
        #allowed ciphers for smtpd_tls_security_level=may
        #smtpd_tls_ciphers = high
        #enforce the server cipher preference
        tls_preempt_cipherlist = true;
        #disable following ciphers for smtpd_tls_security_level=encrypt
        smtpd_tls_mandatory_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"];
        #disable following ciphers for smtpd_tls_security_level=may
        smtpd_tls_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"];
        #enable TLS logging to see the ciphers for inbound connections
        smtpd_tls_loglevel = "1";
        #enable TLS logging to see the ciphers for outbound connections
        smtp_tls_loglevel = "1";
        tls_medium_cipherlist = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";

        smtpd_tls_received_header = true;

        smtpd_tls_ask_ccert = true;
        smtpd_tls_CAfile = toString ./ca/ca.crt;

        smtp_tls_security_level = "dane";
        smtp_dns_support_level = "dnssec";

        smtp_tls_connection_reuse = true;

        tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" (
          concatMapStringsSep "\n\n" (domain:
            concatMapStringsSep "\n" (subdomain: "${subdomain} /run/credentials/postfix.service/${removePrefix "." subdomain}.full.pem")
              [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"]
          ) emailDomains
        )}'';

        smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix";

        local_recipient_maps = "";

        # 10 GiB
        message_size_limit = "10737418240";
        # 10 GiB
        mailbox_size_limit = "10737418240";

        smtpd_delay_reject = true;
        smtpd_helo_required = true;
        smtpd_helo_restrictions = "permit";

        smtpd_recipient_restrictions = [
          "reject_unauth_pipelining"
          "reject_non_fqdn_recipient"
          "reject_unknown_recipient_domain"
          "check_recipient_access pgsql:${pkgs.writeText "check_recipient_access.cf" ''
            hosts = postgresql:///email
            dbname = email
            query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s'
          ''}"
          "check_ccert_access ${relay_ccert}"
          "reject_non_fqdn_helo_hostname"
          "reject_invalid_helo_hostname"
          "reject_unauth_destination"
          "reject_unknown_recipient_domain"
          "reject_unverified_recipient"
        ];
        unverified_recipient_reject_code = "550";
        unverified_recipient_reject_reason = "Recipient address lookup failed";
        address_verify_map = "internal:address_verify_map";
        address_verify_positive_expire_time = "1h";
        address_verify_positive_refresh_time = "15m";
        address_verify_negative_expire_time = "15s";
        address_verify_negative_refresh_time = "5s";
        address_verify_cache_cleanup_interval = "5s";
        address_verify_poll_delay = "1s";

        smtpd_relay_restrictions = [
          "check_ccert_access ${relay_ccert}"
          "reject_unauth_destination"
        ];

        propagate_unmatched_extensions = ["canonical" "virtual" "alias"];
        smtpd_authorized_verp_clients = "";
        authorized_verp_clients = "";

        smtpd_client_event_limit_exceptions = "";

        milter_default_action = "accept";
        smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"];
        non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"];

        alias_maps = "";

        queue_run_delay = "10s";
        minimal_backoff_time = "1m";
        maximal_backoff_time = "10m";
        maximal_queue_lifetime = "100m";
        bounce_queue_lifetime = "20m";

        smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" ''
          # Allow DSN requests from local subnet only
          192.168.0.0/16      silent-discard
          172.16.0.0/12       silent-discard
          10.0.0.0/8          silent-discard
          0.0.0.0/0           silent-discard, dsn
          fd00::/8            silent-discard
          ::/0                silent-discard, dsn
        ''}";

        sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}";
        sender_canonical_classes = "envelope_sender";
        recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}";
        recipient_canonical_classes = ["envelope_recipient" "header_recipient"];

        virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" ''
          hosts = postgresql:///email
          dbname = email
          query = SELECT 1 FROM virtual_mailbox_domain WHERE domain = '%s'
        ''}'';
        virtual_mailbox_maps = ''pgsql:${pkgs.writeText "virtual_mailbox_maps.cf" ''
          hosts = postgresql:///email
          dbname = email
          query = SELECT 1 FROM virtual_mailbox_mapping WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_mapping WHERE lookup = '%s'))
        ''}'';
        dvlmtp_destination_recipient_limit = "1";
        virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp";
        smtputf8_enable = false;

        authorized_submit_users = "inline:{ root= postfwd= }";

        postscreen_access_list = "";
        postscreen_denylist_action = "drop";
        postscreen_greet_action = "enforce";
      };
      masterConfig = {
        smtps = {
          type = "inet";
          private = false;
          command = "smtpd";
          args = [
            "-o" "smtpd_tls_security_level=encrypt"
            "-o" "{smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}"
            "-o" "{smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}"
            "-o" "smtpd_tls_mandatory_ciphers=high"
            "-o" "smtpd_tls_dh1024_param_file=${toString config.security.dhparams.params."postfix-smtps-1024".path}"
            "-o" "smtpd_tls_dh512_param_file=${toString config.security.dhparams.params."postfix-smtps-512".path}"
            "-o" "{tls_eecdh_auto_curves = X25519 X448}"

            "-o" "smtpd_tls_wrappermode=yes"
            "-o" "smtpd_tls_ask_ccert=yes"
            "-o" "smtpd_tls_req_ccert=yes"
            "-o" "smtpd_tls_received_header=no"
            "-o" "cleanup_service_name=subcleanup"
            "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject"
            "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}"
            "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject"
            "-o" "{smtpd_sender_restrictions = reject_unknown_sender_domain,reject_unverified_sender,check_policy_service unix:/run/postfix-ccert-sender-policy.sock}"
            "-o" "unverified_sender_reject_code=550"
            "-o" "unverified_sender_reject_reason={Sender address rejected: undeliverable address}"
            "-o" ''{smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,check_recipient_access pgsql:${pkgs.writeText "check_recipient_access.cf" ''
            hosts = postgresql:///email
            dbname = email
            query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s'))
          ''},permit_tls_all_clientcerts,reject}''
            "-o" "milter_macro_daemon_name=surtr.yggdrasil.li"
            "-o" ''smtpd_milters=${config.services.opendkim.socket}''
          ];
        };
        subcleanup = {
          command = "cleanup";
          private = false;
          maxproc = 0;
          args = [
            "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" ''
              /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1
            ''}"
          ];
        };
        dvlmtp = {
          command = "lmtp";
          args = [
            "flags=DORX"
          ];
        };
        smtp_pass = {
          name = "smtpd";
          type = "pass";
          command = "smtpd";
        };
        postscreen = {
          name = "smtp";
          type = "inet";
          private = false;
          command = "postscreen";
          maxproc = 1;
        };
        smtp = {};
        relay = {
          command = "smtp";
          args = [ "-o" "smtp_fallback_relay=" ];
        };
        tlsproxy = {
          maxproc = 0;
        };
        dnsblog = {};
      };
    };

    services.postsrsd = {
      enable = true;
      domain = "surtr.yggdrasil.li";
      separator = "+";
      excludeDomains = [ "surtr.yggdrasil.li"
                       ] ++ concatMap (domain: [".${domain}" domain]) emailDomains;
    };

    services.opendkim = {
      enable = true;
      user = "postfix"; group = "postfix";
      socket = "local:/run/opendkim/opendkim.sock";
      domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li"] ++ emailDomains)}'';
      selector = "surtr";
      configFile = builtins.toFile "opendkim.conf" ''
        Syslog true
        MTA surtr.yggdrasil.li
        MTACommand ${config.security.wrapperDir}/sendmail
        LogResults true
      '';
    };

    services.rspamd = {
      enable = true;
      workers = {
        controller = {
          type = "controller";
          count = 1;
          bindSockets = [
            { mode = "0660";
              socket = "/run/rspamd/worker-controller.sock";
              owner = config.services.rspamd.user;
              group = config.services.rspamd.group;
            }
          ];
          includes = [];
          extraConfig = ''
            static_dir = "''${WWWDIR}"; # Serve the web UI static assets
          '';
        };
        external = {
          type = "rspamd_proxy";
          bindSockets = [
            { mode = "0660";
              socket = "/run/rspamd/rspamd-milter.sock";
              owner = config.services.rspamd.user;
              group = config.services.rspamd.group;
            }
          ];
          extraConfig = ''
            milter = yes;
            timeout = 120s;

            upstream "local" {
              default = yes;
              self_scan = yes;
            }
          '';
        };
      };
      locals = {
        "milter_headers.conf".text = ''
          use = ["authentication-results", "x-spamd-result", "x-rspamd-queue-id", "x-rspamd-server", "x-spam-level", "x-spam-status"];
          extended_headers_rcpt = [];
        '';
        "actions.conf".text = ''
          reject = 15;
          add_header = 10;
          greylist = 5;
        '';
        "groups.conf".text = ''
          symbols {
            "BAYES_SPAM" {
              weight = 2.0;
            }
          }
        '';
        "dmarc.conf".text = ''
          reporting = true;
          send_reports = true;
          report_settings {
            org_name = "Yggdrasil.li";
            domain = "yggdrasil.li";
            email = "postmaster@yggdrasil.li";
          }
        '';
        "redis.conf".text = ''
          servers = "${config.services.redis.servers.rspamd.unixSocket}";
        '';
        "dkim_signing.conf".text = "enabled = false;";
        "neural.conf".text = "enabled = false;";
        "classifier-bayes.conf".text = ''
          enable = true;
          expire = 8640000;
          new_schema = true;
          backend = "redis";
          per_user = true;
          min_learns = 0;

          autolearn = [0, 10];

          statfile {
              symbol = "BAYES_HAM";
              spam = false;
          }
          statfile {
              symbol = "BAYES_SPAM";
              spam = true;
          }
        '';
        # "redirectors.inc".text = ''
        #   visit.creeper.host
        # '';
      };
    };

    users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user "dovecot2" ];

    services.redis.servers.rspamd.enable = true;

    users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ];

    services.dovecot2 = {
      enable = true;
      enablePAM = false;
      sslServerCert = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.pem";
      sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem";
      sslCACert = toString ./ca/ca.crt;
      mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u";
      modules = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ];
      mailPlugins.globally.enable = [ "fts" "fts_xapian" ];
      protocols = [ "lmtp" "sieve" ];
      extraConfig = let
        dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" ''
          driver = pgsql
          connect = dbname=email
          password_query = SELECT NULL as password, 'Y' as nopassword, "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n'
          user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n'
          iterate_query = SELECT "user" FROM imap_user
        '';
      in ''
        mail_home = /var/lib/mail/%u

        mail_plugins = $mail_plugins quota

        first_valid_uid = ${toString config.users.users.dovecot2.uid}
        last_valid_uid = ${toString config.users.users.dovecot2.uid}
        first_valid_gid = ${toString config.users.groups.dovecot2.gid}
        last_valid_gid = ${toString config.users.groups.dovecot2.gid}

        ${concatMapStringsSep "\n\n" (domain:
          concatMapStringsSep "\n" (subdomain: ''
            local_name ${subdomain} {
              ssl_cert = </run/credentials/dovecot2.service/${subdomain}.pem
              ssl_key = </run/credentials/dovecot2.service/${subdomain}.key.pem
            }
          '') ["imap.${domain}" domain]
        ) emailDomains}

        ssl_require_crl = no
        ssl_verify_client_cert = yes

        ssl_min_protocol = TLSv1.2
        ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl_prefer_server_ciphers = no

        auth_ssl_username_from_cert = yes
        ssl_cert_username_field = commonName
        auth_mechanisms = external

        auth_verbose = yes
        verbose_ssl = yes
        auth_debug = yes

        service auth {
          user = dovecot2
        }
        service auth-worker {
          user = dovecot2
        }

        userdb {
          driver = prefetch
        }
        userdb {
          driver = sql
          args = ${dovecotSqlConf}
        }
        passdb {
          driver = sql
          args = ${dovecotSqlConf}
        }

        protocol lmtp {
          userdb {
            driver = sql
            args = ${pkgs.writeText "dovecot-sql.conf" ''
              driver = pgsql
              connect = dbname=email
              user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC
            ''}

            skip = never
            result_failure = return-fail
            result_internalfail = return-fail
          }

          mail_plugins = $mail_plugins sieve
        }

        mailbox_list_index = yes
        postmaster_address = postmaster@yggdrasil.li
        recipient_delimiter =
        auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-+_@

        service lmtp {
          vsz_limit = 1G

          unix_listener /run/dovecot-lmtp {
            mode = 0600
            user = postfix
            group = postfix
          }
        }

        namespace inbox {
          separator = /
          inbox = yes
          prefix =

          mailbox Trash {
            auto = no
            special_use = \Trash
          }
          mailbox Junk {
            auto = no
            special_use = \Junk
          }
          mailbox Drafts {
            auto = no
            special_use = \Drafts
          }
          mailbox Sent {
            auto = subscribe
            special_use = \Sent
          }
          mailbox "Sent Messages" {
            auto = no
            special_use = \Sent
          }
        }

        plugin {
          quota = count
          quota_rule = *:storage=1GB
          quota_rule2 = Trash:storage=+10%%
          quota_status_overquota = "552 5.2.2 Mailbox is full"
          quota_status_success = DUNNO
          quota_status_nouser = DUNNO
          quota_grace = 10%%
          quota_max_mail_size = ${config.services.postfix.config.message_size_limit}
          quota_vsizes = yes
        }

        protocol imap {
          mail_max_userip_connections = 50
          mail_plugins = $mail_plugins imap_quota imap_sieve
        }

        service imap-login {
          inet_listener imap {
            port = 0
          }
        }

        service managesieve-login {
          inet_listener sieve {
            port = 4190
          }
        }

        plugin {
          sieve_plugins = sieve_imapsieve sieve_extprograms
          sieve = file:~/sieve;active=~/dovecot.sieve
          sieve_redirect_envelope_from = orig_recipient
          sieve_before = /etc/dovecot/sieve_before.d

          sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
          sieve_pipe_bin_dir = ${dovecotSievePipeBin}/pipe/bin

          imapsieve_mailbox1_name = *
          imapsieve_mailbox1_causes = FLAG
          imapsieve_mailbox1_before = /etc/dovecot/sieve_flag.d/learn-junk.sieve
        }

        plugin {
          plugin = fts fts_xapian
          fts = xapian
          fts_xapian = partial=2 full=20 attachments=1 verbose=1

          fts_autoindex = yes

          fts_enforced = no
        }

        service indexer-worker {
          vsz_limit = ${toString (1024 * 1024 * 1024)}
        }
      '';
    };

    systemd.services.dovecot-fts-xapian-optimize = {
      description = "Optimize dovecot indices for fts_xapian";
      requisite = [ "dovecot2.service" ];
      after = [ "dovecot2.service" ];
      startAt = "*-*-* 22:00:00 Europe/Berlin";
      serviceConfig = {
        Type = "oneshot";
        ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A";
        PrivateDevices = true;
        PrivateNetwork = true;
        ProtectKernelTunables = true;
        ProtectKernelModules = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectSystem = true;
        PrivateTmp = true;
      };
    };
    systemd.timers.dovecot-fts-xapian-optimize = {
      timerConfig = {
        RandomizedDelaySec = 4 * 3600;
      };
    };

    environment.etc = {
      "dovecot/sieve_before.d/tag-junk.sieve".text = ''
        require ["imap4flags"];

        if header :contains "X-Spam-Flag" "YES" {
          addflag ["\\Junk"];
        }
      '';

      "dovecot/sieve_flag.d/learn-junk.sieve".text = ''
        require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables", "imap4flags"];

        if environment :matches "imap.user" "*" {
          set "username" "''${1}";
        }

        if environment :contains "imap.changedflags" "\\Junk" {
          if hasflag "\\Junk" {
            pipe :copy "learn_spam.sh" [ "''${username}" ];
          } else {
            if environment :matches "imap.mailbox" "*" {
              set "mailbox" "''${1}";
            }

            if not anyof(string "''${mailbox}" "Trash", string "''${mailbox}" "Junk") {
              pipe :copy "learn_ham.sh" [ "''${username}" ];
            }
          }
        }
      '';
    };

    security.dhparams = {
      params = {
        "postfix-512".bits = 512;
        "postfix-1024".bits = 2048;

        "postfix-smtps-512".bits = 512;
        "postfix-smtps-1024".bits = 2048;
      };
    };

    security.acme.rfc2136Domains = {
      "surtr.yggdrasil.li" = {
        restartUnits = [ "postfix.service" "dovecot2.service" ];
      };
    } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains)
    // listToAttrs (concatMap (domain: [
      (nameValuePair domain { restartUnits = ["postfix.service" "dovecot2.service"]; })
      (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; })
      (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; })
      (nameValuePair "imap.${domain}" { restartUnits = ["dovecot2.service"]; })
      (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; })
    ]) emailDomains);

    systemd.services.postfix = {
      serviceConfig.LoadCredential = [
        "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem"
        "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem"
      ] ++ concatMap (domain:
        map (subdomain: "${subdomain}.full.pem:${config.security.acme.certs.${subdomain}.directory}/full.pem")
          [domain "mailin.${domain}" "mailsub.${domain}"]
      ) emailDomains;
    };

    systemd.services.dovecot2 = {
      preStart = ''
        for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do
          ${pkgs.dovecot_pigeonhole}/bin/sievec $f
        done
      '';

      serviceConfig = {
        LoadCredential = [
          "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem"
          "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem"
        ] ++ concatMap (domain:
          concatMap (subdomain: [
            "${subdomain}.key.pem:${config.security.acme.certs.${subdomain}.directory}/key.pem"
            "${subdomain}.pem:${config.security.acme.certs.${subdomain}.directory}/fullchain.pem"
          ])
            [domain "imap.${domain}"]
        ) emailDomains;
      };
    };

    services.nginx = {
      upstreams.spm = {
        servers = {
          "unix:/run/spm/server.sock" = {};
        };
      };

      virtualHosts = listToAttrs (map (domain: nameValuePair "spm.${domain}" {
        forceSSL = true;
        kTLS = true;
        http3 = false;
        sslCertificate = "/run/credentials/nginx.service/spm.${domain}.pem";
        sslCertificateKey = "/run/credentials/nginx.service/spm.${domain}.key.pem";
        extraConfig = ''
          ssl_stapling off;
          ssl_verify_client on;
          ssl_client_certificate ${toString ./ca/ca.crt};
        '';
        locations."/" = {
          proxyPass = "http://spm";

          extraConfig = ''
            proxy_set_header SSL-CLIENT-VERIFY $ssl_client_verify;
            proxy_set_header SSL-CLIENT-S-DN $ssl_client_s_dn;
            proxy_set_header SPM-DOMAIN "${domain}";
          '';
        };
      }) spmDomains) // listToAttrs (map (domain: nameValuePair "mta-sts.${domain}" {
          forceSSL = true;
          kTLS = true;
          http3 = true;
          sslCertificate = "/run/credentials/nginx.service/mta-sts.${domain}.pem";
          sslCertificateKey = "/run/credentials/nginx.service/mta-sts.${domain}.key.pem";
          sslTrustedCertificate = "/run/credentials/nginx.service/mta-sts.${domain}.chain.pem";

          extraConfig = ''
            add_header Strict-Transport-Security "max-age=63072000" always;

            add_header Access-Control-Allow-Origin '*';
            add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
            add_header Access-Control-Allow-Headers 'X-Requested-With, Content-Type, Authorization';
            add_header Access-Control-Max-Age 7200;
          '';

          locations."/" = {
            extraConfig = ''
              charset utf-8;
              source_charset utf-8;
            '';
            root = pkgs.runCommand "mta-sts.${domain}" {} ''
              mkdir -p $out/.well-known
              cp ${pkgs.writeText "mta-sts.${domain}.txt" ''
                version: STSv1
                mode: enforce
                max_age: 2419200
                mx: mailin.${domain}
              ''} $out/.well-known/mta-sts.txt
            '';
          };
      }) emailDomains);
    };

    systemd.services.nginx.serviceConfig.LoadCredential = concatMap (domain: [
      "spm.${domain}.key.pem:${config.security.acme.certs."spm.${domain}".directory}/key.pem"
      "spm.${domain}.pem:${config.security.acme.certs."spm.${domain}".directory}/fullchain.pem"
    ]) spmDomains ++ concatMap (domain: [
      "mta-sts.${domain}.key.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/key.pem"
      "mta-sts.${domain}.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/fullchain.pem"
      "mta-sts.${domain}.chain.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/chain.pem"
    ]) emailDomains;

    systemd.services.spm = {
      serviceConfig = {
        Type = "notify";
        ExecStart = "${pkgs.spm}/bin/spm-server";
        User = "spm";
        Group = "spm";

        Environment = [
          "SPM_INSTANCE=ed1c0e1d-7be4-4dd5-b51a-291bad3ac9c9"
          "PGCONNSTR=dbname=email"
        ];

        LoadCredential = [
          "spm-keys.json:${config.sops.secrets."spm-keys.json".path}"
        ];
      };
    };
    systemd.sockets.spm = {
      wantedBy = [ "nginx.service" ];

      socketConfig = {
        ListenStream = "/run/spm/server.sock";
        SocketUser = "spm";
        SocketGroup = "spm";
        SocketMode = 0660;
      };
    };

    users.users.spm = {
      isSystemUser = true;
      group = "spm";
    };

    users.groups.spm = {
      members = [ config.services.nginx.user ];
    };

    sops.secrets."spm-keys.json" = {
      format = "binary";
      sopsFile = ./spm-keys.json;
    };

    services.postfix-mta-sts-resolver = {
      enable = true;
      loglevel = "debug";
    };

    systemd.sockets."postfix-ccert-sender-policy" = {
      requiredBy = ["postfix.service"];
      wants = ["postfix-ccert-sender-policy.service"];
      socketConfig = {
        ListenStream = "/run/postfix-ccert-sender-policy.sock";
      };
    };
    systemd.services."postfix-ccert-sender-policy" = {
      after = [ "postgresql.service" ];
      bindsTo = [ "postgresql.service" ];

      serviceConfig = {
        Type = "notify";

        ExecStart = "${ccert-policy-server}/bin/ccert-policy-server";

        Environment = [
          "PGDATABASE=email"
        ];

        DynamicUser = false;
        User = "postfix-ccert-sender-policy";
        Group = "postfix-ccert-sender-policy";
        ProtectSystem = "strict";
        SystemCallFilter = "@system-service";
        NoNewPrivileges = true;
        ProtectKernelTunables = true;
        ProtectKernelModules = true;
        ProtectKernelLogs = true;
        ProtectControlGroups = true;
        MemoryDenyWriteExecute = true;
        RestrictSUIDSGID = true;
        KeyringMode = "private";
        ProtectClock = true;
        RestrictRealtime = true;
        PrivateDevices = true;
        PrivateTmp = true;
        ProtectHostname = true;
        ReadWritePaths = ["/run/postgresql"];
      };
    };
    users.users."postfix-ccert-sender-policy" = {
      isSystemUser = true;
      group = "postfix-ccert-sender-policy";
    };
    users.groups."postfix-ccert-sender-policy" = {};

    services.postfwd = {
      enable = true;
      rules = ''
        id=RCPT01; protocol_state=DATA; protocol_state=END-OF-MESSAGE; action=rcpt(ccert_subject/100/3600/set(HIT_RATELIMIT=1,HIT_RATECOUNT=$$ratecount,HIT_RATELIMIT_LIMIT=100,HIT_RATELIMIT_INTERVAL=3600))
        id=RCPT02; protocol_state=DATA; protocol_state=END-OF-MESSAGE; action=rcpt(ccert_subject/1000/86400/set(HIT_RATELIMIT=1,HIT_RATECOUNT=$$ratecount,HIT_RATELIMIT_LIMIT=1000,HIT_RATELIMIT_INTERVAL=86400))

        id=JUMP_REJECT_RL; HIT_RATELIMIT=="1"; action=jump(REJECT_RL)

        id=EOF; action=DUNNO

        id=REJECT_RL; action=450 4.7.1 Exceeding maximum of $$HIT_RATELIMIT_LIMIT recipients per $$HIT_RATELIMIT_INTERVAL seconds [$$HIT_RATECOUNT]
      '';
    };
  };
}