summaryrefslogtreecommitdiffstats
path: root/krebs/3modules/exim-retiolum.nix
blob: f78f1746c05f79402808c8c5d8e3485b89e498a9 (plain)
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
{ config, pkgs, lib, ... }:
with import ../../lib/pure.nix { inherit lib; }; let
  cfg = config.krebs.exim-retiolum;

  # Due to improvements to the JSON notation, braces around top-level objects
  # are not necessary^Wsupported by rspamd's parser when including files:
  # https://github.com/rspamd/rspamd/issues/2674
  toMostlyJSON = value:
    assert typeOf value == "set";
    (s: substring 1 (stringLength s - 2) s)
    (toJSON value);

  to-lsearch = concatMapStrings ({ from, to, ... }: "${from}: ${to}\n");
  lsearch = mapAttrs (name: set: toFile name (to-lsearch set)) ({
    inherit (cfg) system-aliases;
  });

in {
  options.krebs.exim-retiolum = {
    enable = mkEnableOption "krebs.exim-retiolum";
    local_domains = mkOption {
      type = with types; listOf hostname;
      default = ["localhost"] ++ config.krebs.build.host.nets.retiolum.aliases;
    };
    primary_hostname = mkOption {
      type = types.str;
      default = let x = "${config.krebs.build.host.name}.r"; in
        assert elem x config.krebs.build.host.nets.retiolum.aliases;
        x;
    };
    relay_to_domains = mkOption {
      # TODO hostname with wildcards
      type = with types; listOf str;
      default = [
        "*.r"
      ];
    };
    rspamd = {
      enable = mkEnableOption "krebs.exim-retiolum.rspamd" // {
        default = false;
      };
      locals = {
        logging = {
          level = mkOption {
            type = types.enum [
              "error"
              "warning"
              "notice"
              "info"
              "debug"
              "silent"
            ];
            default = "notice";
          };
        };
        options = {
          local_networks = mkOption {
            type = types.listOf types.cidr;
            default = [
              config.krebs.build.host.nets.retiolum.ip4.prefix
              config.krebs.build.host.nets.retiolum.ip6.prefix
            ];
          };
        };
      };
    };
    system-aliases = mkOption {
      type = types.listOf (types.submodule ({
        options = {
          from = mkOption {
            type = types.str; # TODO e-mail address
          };
          to = mkOption {
            type = types.str; # TODO e-mail address / TODO listOf
          };
        };
      }));
      default = [];
    };
  };
  imports = [
    {
      config = lib.mkIf cfg.rspamd.enable {
        services.rspamd.enable = true;
        services.rspamd.locals =
          mapAttrs'
            (name: value: nameValuePair "${name}.inc" {
              text = toMostlyJSON value;
            })
            cfg.rspamd.locals;
        users.users.${config.krebs.exim.user.name}.extraGroups = [
          config.services.rspamd.group
        ];
      };
    }
  ];
  config = lib.mkIf cfg.enable {
    krebs.exim = {
      enable = true;
      config =
        # This configuration makes only sense for retiolum-enabled hosts.
        # TODO modular configuration
        assert config.krebs.tinc.retiolum.enable;
        /* exim */ ''
          keep_environment =

          primary_hostname = ${cfg.primary_hostname}
          domainlist local_domains = ${concatStringsSep ":" cfg.local_domains}
          domainlist relay_to_domains = ${concatStringsSep ":" cfg.relay_to_domains}

          ${optionalString cfg.rspamd.enable /* exim */ ''
            spamd_address = /run/rspamd/rspamd.sock variant=rspamd
          ''}

          acl_smtp_rcpt = acl_check_rcpt
          acl_smtp_data = acl_check_data

          host_lookup = *
          rfc1413_hosts = *
          rfc1413_query_timeout = 5s

          log_file_path = syslog
          syslog_timestamp = false
          syslog_duplication = false

          tls_advertise_hosts =

          begin acl

          acl_check_rcpt:
            deny
              local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
              message = restricted characters in address

            accept
              domains = +local_domains : +relay_to_domains

            deny
              message = relay not permitted


          acl_check_data:
            ${optionalString cfg.rspamd.enable /* exim */ ''
              accept condition = ''${if eq{$interface_port}{587}}

              warn remove_header = ${concatStringsSep " : " [
                "x-spam"
                "x-spam-report"
                "x-spam-score"
              ]}

              warn
                spam = nobody:true

              warn
                condition = ''${if !eq{$spam_action}{no action}}
                add_header = X-Spam: Yes
                add_header = X-Spam-Report: $spam_report
                add_header = X-Spam-Score: $spam_score
            ''}
            accept


          begin routers

          system_aliases:
            debug_print = "R: system_aliases for $local_part@$domain"
            driver = redirect
            data = ''${lookup{$local_part}lsearch{${lsearch.system-aliases}}}

          local:
            driver = accept
            domains = +local_domains
            check_local_user
          # local_part_suffix = +*
          # local_part_suffix_optional
            transport = home_maildir

          remote:
            driver = manualroute
            domains = +relay_to_domains
            transport = remote_smtp
            route_list = ^.* $0 byname


          begin transports

          remote_smtp:
            driver = smtp

          home_maildir:
            driver = appendfile
            maildir_format
            directory = $home/Maildir
            directory_mode = 0700
            delivery_date_add
            envelope_to_add
            return_path_add
          # group = mail
          # mode = 0660

          begin retry
          ${concatMapStringsSep "\n" (k: "${k} * F,42d,1m") cfg.relay_to_domains}
          * * F,2h,15m; G,16h,1h,1.5; F,4d,6h

          begin rewrite

          begin authenticators
        '';
    };
  };
}