diff options
Diffstat (limited to 'hosts/surtr/email/default.nix')
-rw-r--r-- | hosts/surtr/email/default.nix | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix new file mode 100644 index 00000000..49f156eb --- /dev/null +++ b/hosts/surtr/email/default.nix | |||
@@ -0,0 +1,230 @@ | |||
1 | { config, pkgs, lib, ... }: | ||
2 | |||
3 | with lib; | ||
4 | |||
5 | let | ||
6 | postfix_map = tableType: tableName: "${tableType}:/run/postfix/maps/${tableName}"; | ||
7 | postfix_hash = postfix_map "hash"; | ||
8 | in { | ||
9 | options = { | ||
10 | services.postfix.mapFilesRun = mkOption { | ||
11 | type = types.attrsOf (types.either types.path (types.submodule { | ||
12 | options = { | ||
13 | type = mkOption { | ||
14 | type = types.str; | ||
15 | default = "hash"; | ||
16 | }; | ||
17 | |||
18 | path = mkOption { | ||
19 | type = types.nullOr types.path; | ||
20 | default = null; | ||
21 | }; | ||
22 | |||
23 | text = mkOption { | ||
24 | type = types.nullOr types.lines; | ||
25 | default = null; | ||
26 | }; | ||
27 | }; | ||
28 | })); | ||
29 | default = {}; | ||
30 | }; | ||
31 | }; | ||
32 | |||
33 | config = { | ||
34 | services.postfix = { | ||
35 | enable = true; | ||
36 | hostname = "surtr.yggdrasil.li"; | ||
37 | recipientDelimiter = "+"; | ||
38 | setSendmail = true; | ||
39 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; | ||
40 | destination = []; | ||
41 | sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; | ||
42 | sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; | ||
43 | networks = ["127.0.0.0/8" "[::ffff:127.0.0.0]/104" "[::1]/128" "10.141.0.0/16"]; | ||
44 | mapFilesRun = { | ||
45 | "relay_ccert" = { text = ""; }; | ||
46 | "sni" = { text = '' | ||
47 | bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem | ||
48 | mailin.bouncy.email /run/credentials/postfix.service/mailin.bouncy.email.sni.pem | ||
49 | mailsub.bouncy.email /run/credentials/postfix.service/mailsub.bouncy.email.sni.pem | ||
50 | .bouncy.email /run/credentials/postfix.service/bouncy.email.sni.pem | ||
51 | '';}; | ||
52 | "esmtp_access" = { type = "cidr"; text = '' | ||
53 | # Allow DSN requests from local subnet only | ||
54 | 192.168.0.0/16 silent-discard | ||
55 | 172.16.0.0/12 silent-discard | ||
56 | 10.0.0.0/8 silent-discard | ||
57 | 0.0.0.0/0 silent-discard, dsn | ||
58 | fd00::/8 silent-discard | ||
59 | ::/0 silent-discard, dsn | ||
60 | '';}; | ||
61 | }; | ||
62 | config = { | ||
63 | #the dh params | ||
64 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; | ||
65 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; | ||
66 | #enable ECDH | ||
67 | smtpd_tls_eecdh_grade = "strong"; | ||
68 | #enabled SSL protocols, don't allow SSLv2 and SSLv3 | ||
69 | smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; | ||
70 | smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; | ||
71 | #allowed ciphers for smtpd_tls_security_level=encrypt | ||
72 | smtpd_tls_mandatory_ciphers = "high"; | ||
73 | #allowed ciphers for smtpd_tls_security_level=may | ||
74 | #smtpd_tls_ciphers = high | ||
75 | #enforce the server cipher preference | ||
76 | tls_preempt_cipherlist = true; | ||
77 | #disable following ciphers for smtpd_tls_security_level=encrypt | ||
78 | smtpd_tls_mandatory_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; | ||
79 | #disable following ciphers for smtpd_tls_security_level=may | ||
80 | smtpd_tls_exclude_ciphers = ["aNULL" "MD5" "DES" "ADH" "RC4" "PSD" "SRP" "3DES" "eNULL"]; | ||
81 | #enable TLS logging to see the ciphers for inbound connections | ||
82 | smtpd_tls_loglevel = "1"; | ||
83 | #enable TLS logging to see the ciphers for outbound connections | ||
84 | smtp_tls_loglevel = "1"; | ||
85 | |||
86 | smtpd_tls_ask_ccert = true; | ||
87 | smtpd_tls_CAfile = toString ./ca/ca.crt; | ||
88 | |||
89 | smtp_tls_security_level = "dane"; | ||
90 | smtp_dns_support_level = "dnssec"; | ||
91 | |||
92 | tls_server_sni_maps = postfix_hash "sni"; | ||
93 | |||
94 | local_recipient_maps = ""; | ||
95 | |||
96 | # 10 GiB | ||
97 | message_size_limit = "10737418240"; | ||
98 | # 10 GiB | ||
99 | mailbox_size_limit = "10737418240"; | ||
100 | |||
101 | smtpd_delay_reject = true; | ||
102 | smtpd_helo_required = true; | ||
103 | smtpd_helo_restrictions = "permit"; | ||
104 | |||
105 | smtpd_recipient_restrictions = [ | ||
106 | "reject_unauth_pipelining" | ||
107 | "reject_non_fqdn_recipient" | ||
108 | "reject_unknown_recipient_domain" | ||
109 | "permit_mynetworks" | ||
110 | "check_ccert_access ${postfix_hash "relay_ccert"}" | ||
111 | "reject_non_fqdn_helo_hostname" | ||
112 | "reject_invalid_helo_hostname" | ||
113 | "reject_unauth_destination" | ||
114 | "reject_unknown_recipient_domain" | ||
115 | "reject_unverified_recipient" | ||
116 | ]; | ||
117 | |||
118 | smtpd_relay_restrictions = [ | ||
119 | "permit_mynetworks" | ||
120 | "check_ccert_access ${postfix_hash "relay_ccert"}" | ||
121 | "reject_unauth_destination" | ||
122 | ]; | ||
123 | |||
124 | propagate_unmatched_extensions = ["canonical" "virtual" "alias"]; | ||
125 | smtpd_authorized_verp_clients = "$authorized_verp_clients"; | ||
126 | authorized_verp_clients = "$mynetworks"; | ||
127 | |||
128 | milter_default_action = "accept"; | ||
129 | smtpd_milters = [config.services.opendkim.socket]; | ||
130 | non_smtpd_milters = [config.services.opendkim.socket]; | ||
131 | |||
132 | alias_maps = ""; | ||
133 | |||
134 | queue_run_delay = "10s"; | ||
135 | minimal_backoff_time = "1m"; | ||
136 | maximal_backoff_time = "10m"; | ||
137 | maximal_queue_lifetime = "100m"; | ||
138 | bounce_queue_lifetime = "20m"; | ||
139 | |||
140 | smtpd_discard_ehlo_keyword_address_maps = postfix_map "cidr" "esmtp_access"; | ||
141 | |||
142 | sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; | ||
143 | sender_canonical_classes = "envelope_sender"; | ||
144 | recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; | ||
145 | recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; | ||
146 | }; | ||
147 | masterConfig = { | ||
148 | smtps = { | ||
149 | type = "inet"; | ||
150 | command = "smtpd"; | ||
151 | args = [ | ||
152 | "-o" "smtpd_tls_wrappermode=yes" | ||
153 | "-o" "smtpd_tls_req_ccert=yes" | ||
154 | "-o" "smtpd_client_restrictions=permit_tls_all_clientcerts,reject" | ||
155 | "-o" "smtpd_recipient_restrictions=reject_unauth_pipelining,reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_tls_all_clientcerts,reject" | ||
156 | ]; | ||
157 | }; | ||
158 | }; | ||
159 | }; | ||
160 | |||
161 | services.postsrsd = { | ||
162 | enable = true; | ||
163 | domain = "srs.surtr.yggdrasil.li"; | ||
164 | separator = "+"; | ||
165 | excludeDomains = [ "surtr.yggdrasil.li" | ||
166 | ".bouncy.email" "bouncy.email" | ||
167 | ]; | ||
168 | }; | ||
169 | |||
170 | services.opendkim = { | ||
171 | enable = true; | ||
172 | # user = "postfix"; group = "postfix"; | ||
173 | # socket = "local:/run/opendkim/opendkim.sock"; | ||
174 | domains = ''csl:${concatStringsSep "," ["surtr.yggdrasil.li" "bouncy.email"]}''; | ||
175 | selector = "surtr"; | ||
176 | configFile = builtins.toFile "opendkim.conf" '' | ||
177 | Syslog true | ||
178 | MTACommand ${config.security.wrapperDir}/sendmail | ||
179 | LogResults true | ||
180 | ''; | ||
181 | }; | ||
182 | |||
183 | security.dhparams = { | ||
184 | params = { | ||
185 | "postfix-512".bits = 512; | ||
186 | "postfix-1024".bits = 2048; | ||
187 | }; | ||
188 | }; | ||
189 | |||
190 | security.acme.domains = let | ||
191 | mkSNI = '' | ||
192 | cat key.pem full.pem > sni.pem | ||
193 | ''; | ||
194 | in { | ||
195 | "bouncy.email" = { | ||
196 | certCfg.postRun = mkSNI; | ||
197 | }; | ||
198 | "mailin.bouncy.email" = { | ||
199 | certCfg.postRun = mkSNI; | ||
200 | }; | ||
201 | "mailsub.bouncy.email" = { | ||
202 | certCfg.postRun = mkSNI; | ||
203 | }; | ||
204 | "surtr.yggdrasil.li" = {}; | ||
205 | }; | ||
206 | |||
207 | systemd.services.postfix = { | ||
208 | preStart = concatStringsSep "\n" (mapAttrsToList (to: from: let | ||
209 | cont = {type, path, text}: assert !(isNull path && isNull text); let | ||
210 | path' = if isNull path then pkgs.writeText to text else path; | ||
211 | in '' | ||
212 | ln -sf ${path'} /run/postfix/maps/${to} | ||
213 | postmap ${type}:/run/postfix/maps/${to} | ||
214 | ''; | ||
215 | in if builtins.isPath from then cont { path = from; } else cont from | ||
216 | ) config.services.postfix.mapFilesRun); | ||
217 | |||
218 | serviceConfig = { | ||
219 | RuntimeDirectory = ["postfix/maps"]; | ||
220 | LoadCredential = [ | ||
221 | "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" | ||
222 | "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" | ||
223 | "bouncy.email.sni.pem:${config.security.acme.certs."bouncy.email".directory}/sni.pem" | ||
224 | "mailin.bouncy.email.sni.pem:${config.security.acme.certs."mailin.bouncy.email".directory}/sni.pem" | ||
225 | "mailsub.bouncy.email.sni.pem:${config.security.acme.certs."mailsub.bouncy.email".directory}/sni.pem" | ||
226 | ]; | ||
227 | }; | ||
228 | }; | ||
229 | }; | ||
230 | } | ||