summaryrefslogtreecommitdiff
path: root/os/kay/modules/mail.nix
blob: 685461fc7a62bf4984246b3b15a3526b43563225 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
{ config, pkgs, ... }:
let
  ipv6 = "2001:470:ee65::1337";
  domain = config.global.userdata.domain;
  username = config.global.userdata.name;
  email = [
    "${username}@${domain}"

    # used by github automation
    # https://github.com/nocodb/nocodb/blob/32826d4b24e9285b898bb3547fdf550f81c930bb/nix/bumper/bumper.sh#L5
    "auto@${domain}"
    # used by mail.sinanmohd.com
    "postmaster@${domain}"
    # used by ns1.sinanmohd.com
    "hostmaster@${domain}"
  ];

  credentials_directory = "/run/credentials/stalwart-mail.service";
in
{
  security.acme.certs.${domain}.postRun = "systemctl restart stalwart-mail.service";
  sops.secrets = {
    "mail.${domain}/dkim_rsa" = { };
    "mail.${domain}/dkim_ed25519" = { };
    "mail.${domain}/password" = { };
  };

  systemd.services.stalwart-mail.serviceConfig.LoadCredential = [
    "password:${config.sops.secrets."mail.${domain}/password".path}"

    "dkim_rsa:${config.sops.secrets."mail.${domain}/dkim_rsa".path}"
    "dkim_ed25519:${config.sops.secrets."mail.${domain}/dkim_ed25519".path}"

    "cert:${config.security.acme.certs.${domain}.directory}/fullchain.pem"
    "key:${config.security.acme.certs.${domain}.directory}/key.pem"
  ];

  services.postgresql = {
    ensureDatabases = [ "stalwart" ];
    ensureUsers = [
      {
        name = "stalwart";
        ensureDBOwnership = true;
      }
    ];
  };

  services.stalwart-mail = {
    enable = true;
    openFirewall = true;

    settings = {
      queue.outbound = {
        ip-strategy = "ipv6_then_ipv4";
        source-ip.v6 = "['${ipv6}']";
        tls.starttls = "optional";
      };
      http.url = "'https://stalwart.${domain}'";

      server = {
        hostname = "mail.${domain}";
        listener = {
          smtp = {
            bind = [
              "[${ipv6}]:25"
              "0.0.0.0:25"
            ];
            protocol = "smtp";
          };
          submission = {
            bind = "[::]:587";
            protocol = "smtp";
          };
          submissions = {
            bind = "[::]:465";
            protocol = "smtp";
            tls.implicit = true;
          };
          imaptls = {
            bind = "[::]:993";
            protocol = "imap";
            tls.implicit = true;
          };
          http = {
            bind = "[::]:8085";
            protocol = "http";
          };
        };
      };

      signature = {
        rsa = {
          private-key = "%{file:${credentials_directory}/dkim_rsa}%";
          inherit domain;
          selector = "rsa";
          headers = [
            "From"
            "To"
            "Date"
            "Subject"
            "Message-ID"
          ];
          algorithm = "rsa-sha-256";
          canonicalization = "simple/simple";

          set-body-length = true;
          expire = "2d";
          report = true;
        };
        ed25519 = {
          private-key = "%{file:${credentials_directory}/dkim_ed25519}%";
          inherit domain;
          selector = "ed25519";
          headers = [
            "From"
            "To"
            "Date"
            "Subject"
            "Message-ID"
          ];
          algorithm = "ed25519-sha256";
          canonicalization = "simple/simple";

          set-body-length = true;
          expire = "2d";
          report = true;
        };
      };

      certificate."default" = {
        cert = "%{file:${credentials_directory}/cert}%";
        private-key = "%{file:${credentials_directory}/key}%";
      };

      storage = {
        data = "postgresql";
        fts = "postgresql";
        blob = "postgresql";
        lookup = "postgresql";
        directory = "memory";
      };
      store.postgresql = {
        type = "postgresql";
        host = "localhost";
        database = "stalwart";
        user = "stalwart";
        timeout = "15s";
        tls.enable = false;
        pool.max-connections = 10;
      };

      directory."memory" = {
        type = "memory";

        principals = [
          {
            class = "admin";
            name = "${username}@${domain}";
            secret = "%{file:${credentials_directory}/password}%";
            inherit email;
          }
          {
            # for mta-sts & dmarc reports
            class = "individual";
            name = "reports@${domain}";
            secret = "%{file:${credentials_directory}/password}%";
            email = [ "reports@${domain}" ];
          }
        ];
      };
    };
  };
}