summaryrefslogtreecommitdiff
path: root/source/blog/2020/email-server-extras/index.md
blob: b19819bdc0268e63c90e420672b028a455cecedc (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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
---
title: "Setting up an email server in 2020 with OpenSMTPD and Dovecot: extras"
date: 2020-04-28
layout: "blog"
toc: true
---

This sequel to my post
"[Setting up an email server in 2020 with OpenSMTPD and Dovecot](/blog/2020/email-server/)"
gives extra tips and tricks to extend your email setup.
See also the sequel's sequel,
"[Revisiting my email server in 2022](/blog/2022/email-server-revisited/)".

Last updated on 2022-09-12.


## General

### Multiple domains

You can generalize your setup to handle multiple domains
with very little effort. In the following,
I'll assume that your two domains are called `foo.com` and `bar.com`.


#### DNS records

There should be MX, SPF, DKIM and DMARC records for both domains,
as explained in the previous guide. Fortunately, these records
can have identical contents for both domains!

However, it remains essential that the mail server's mailname
and reverse DNS domain name match up exactly,
so you should create MX records with that in mind.
Therefore, if the email server for both domains has `mx1.foo.com`
as reverse DNS name, the MX records should look like this:
```sh
foo.com. MX 42 mx1.foo.com.
bar.com. MX 42 mx1.foo.com.
```
This is perfectly valid: the only thing that matters is that
what your SMTP server calls itself agrees with what reverse DNS
says that the server is actually called.


#### Dovecot

To make Dovecot aware of multiple domains,
you only need to update the `/etc/dovecot/users` file
to add accounts for both domains.
However, in the original guide, I said to only write `user`
in the file, without the `@foo.com`, for an address `user@foo.com`.
Unsurprisingly, that isn't an option for multiple domains,
so you must put the full address in `/etc/dovecot/users`.

Then update `/etc/dovecot/dovecot.conf` to reflect that,
by changing `%n` to `%u` in `username_format`:
```sh
userdb {
	driver = passwd-file
	args = username_format=%u /etc/dovecot/users
	override_fields = uid=vmail gid=vmail home=/home/vmail/%d/%n
}
```
Also note the change in the `home` setting:
the inbox of a user `user@foo.com` will now be stored
in `/home/vmail/foo.com/user`.
That's all you need to change.


#### OpenSMTPD

To inform OpenSMTPD of all the domains,
create a new file `/etc/smtpd/domains`,
and in there put all desired names on their own line:
```sh
foo.com
bar.com
```
And as I mentioned when discussing the DNS records,
you should check that `/etc/smtpd/mailname` agrees
with your server's reverse DNS.

Then, in the main configuration file, tell OpenSMTPD to
use the new domains file when deciding whether to accept an message,
by declaring a new table and changing the `match` line for inbound mail:
```sh
table domains "/etc/smtpd/domains"
# ...
match from any for domain <domains> action "RECV"
```

#### Rspamd

The last thing to do is to inform Rspamd of the multiple domains.
It's really easy: simply add multiple domain blocks:
```c
domain {
	foo.com {
		path = "/path/to/dkim/private.key";
		selector = "hello";
	}
}
domain {
	bar.com {
		path = "/path/to/dkim/private.key";
		selector = "world";
	}
}
```

### Advanced security

SPF, DKIM and DMARC are email's traditional DNS-based security systems,
but in 2018 the IETF released [RFC 8460](https://tools.ietf.org/html/rfc8460) and [RFC 8461](https://www.rfc-editor.org/rfc/rfc8461.txt),
which respectively define TLSRPT and MTA-STS,
two fancy new systems focused on TLS-encrypted email transport.

These security mechanisms are pretty new,
so you won't get a huge benefit from enabling them,
but big email providers' draconian spam filters might like it.


#### TLSRPT

TLS reporting, or TLSRPT for short, is very simple:
all it does is provide a contact email address in case
somebody has trouble with the TLS configuration of your SMTP server.

To enable it for your custom email domain `example.com`,
simply create a DNS TXT record for the `_smtp._tls` subdomain:
```sh
_smtp._tls.example.com. TXT "v=TLSRPTv1; rua=mailto:<contact>"
```
Where `<contact>` is an email address of your choosing.
That's all!


#### MTA-STS

MTA Strict Transport Security (MTA-STS) tells other servers
that you take TLS encryption of messages very seriously,
so they should avoid sending you unencrypted email,
and should only accept certain certificates from your side.

Compared to the previously discussed DNS-based security extensions,
MTA-STS is a bit more work to set up,
because you'll also need an HTTP web server.

The DNS part is still pretty simple:
create yet another DNS TXT record,
this time for the subdomain `_mta-sts`:
```sh
_mta-sts.example.com. TXT "v=STSv1; id=<id>"
```
The `<id>` should identify the version of your policy,
so other servers can quickly see if something changed.
I recommend using today's date.

For the next part, I'll assume that you already have
a web server running on a server with the IP address `1.2.3.4`.
I use [nginx](https://nginx.org/) for this, running
on the same server as OpenSMTPD and Dovecot,
but you don't have to do the same.

Create an A record which binds your server
to the subdomain `mta-sts` (without underscore):
```sh
mta-sts.example.com. A 1.2.3.4
```
Set your web server to serve the file
`https://mta-sts.example.com/.well-known/mta-sts.txt`
(we'll discuss that file in a moment).
Note that this policy file **must** be served over HTTPS,
so you need a valid TLS certificate for that domain.

The contents of the `mta-sts.txt` policy file are as follows,
where `mx1.example.com` and `mx2.example.com` are the hosts
mentioned in `example.com`'s DNS MX records:
```sh
version: STSv1
mode: enforce
mx: mx1.example.com
mx: mx2.example.com
max_age: <age>
```
All MX servers must be mentioned this way.
If you're feeling cautious, you may want to set
`mode` to `testing` in the beginning.
This policy is valid for `<age>` seconds,
which is recommended to be several weeks,
but to start with, I suggest using 86400 seconds (one day).
Finally, ensure that this file has CRLF Windows-style line endings.

To correctly pass an MTA-STS test, the TLS certificate
presented by e.g. `mx1.example.com` should be valid for `mx1.exaple.com`.
To achieve this without needing to manage too many certificates,
you can specify multiple domains when requesting a certificate,
or you can use a wildcard domain (`*.example.com`).
Note, however, that MTA-STS testing tools don't like
the latter option, so I recommend the former.

Once you're done, check your work by using either
[ESMTP](https://esmtp.email/tools/mta-sts/)'s or [Ayke](https://aykevl.nl/apps/mta-sts/)'s
online MTA-STS validation tools,
ignoring any warnings about DNSSEC or DANE.
If all is good, great!

Even if you did everything correctly,
these tools will warn you that you're not using DNSSEC/DANE.
It might then be tempting to set that up for even more security,
but I recommend against that for private servers: take a look at [this](https://dane.sys4.de/common_mistakes).



## OpenSMTPD

### Client certificates (in addition to passwords)

You can configure OpenSMTPD to request a client certificate
for sending emails, as a second factor for authentication.

UPDATE: When I wrote this two years ago, it worked,
but now it doesn't anymore, and I can't figure out why.
It seems OpenSMTPD always rejects the client certificates for being self-signed,
even if they can manually be verified for our CA using the `openssl` tool.
I'm leaving this tutorial here for anyone who's interested,
but it's unlikely I'll fix it anytime soon.


#### Certificates

We need to start with some cryptography to create and verify certificates.
I recommend that you do all of this on your trusted  *client* device,
and only copy the necessary files to the server later.

DISCLAIMER:
All the keys and certificates that we'll generate in this section are
for **private use** only, to handle a small number of trusted clients.
I'm not a cryptography expert, so you should **not** listen to me
for large-scale systems that may involve untrusted devices.

The first step is to set up a private Certificate Authority (CA),
which issues the client certificates and can be used to verify them.
Start by generating an RSA private key,
which you should store in a safe place and not share with anyone:
```sh
$ openssl genrsa -out mailca.key 2048
```
Extract a public certificate from this key as follows.
Because we're lazy, we give it a lifetime of 36500 days:
```sh
$ openssl req -new -x509 -days 36500 -key mailca.key -out mailca.crt
```
When running this command, OpenSSL will ask you some questions
about who this certificate is intended for.
Since this is for personal use, your answers don't matter,
so just use the defaults.
Some fields (I think only *Country Name* and *Organization Name*)
cannot be empty, but the others can.

Moving on to the client, once again generate an RSA private key:
```sh
$ openssl genrsa -out mailclient.key 2048
```
From this private key, create a Certificate Signing Request (CSR)
as follows, where you'll be asked the same questions as before:
```sh
$ openssl req -new -key mailclient.key -out mailclient.csr
```
By feeding this CSR to the CA, we can create a signed client certificate
that can be verified using the CA's public certificate.
```sh
$ openssl x509 -req -in mailclient.csr -out mailclient.crt \
        -days 36499 -CA mailca.crt -CAkey mailca.key
```
If you want to multiple client certificates,
just repeat the last few steps for each one.


#### Server

OpenSMTPD needs to verify the validity of client certificates
using the CA's public certificate, so you should copy that
to somewhere on the server, e.g. `/etc/smtpd/mailca.crt`,
and declare it to OpenSMTPD by adding this near
the top of `/etc/smtpd/smtpd.conf`:
```sh
ca "mailca" cert "/etc/smtpd/mailca.crt"
```
Then replace the entire configuration for outbound mail as follows.
Note that this removes SMTPS support, leaving only STARTTLS:
```sh
# Outbound
listen on eth0 port 587 tls-require verify pki "example.com" ca "mailca" auth <passwds> filter "rspamd"
action "SEND" relay srs
match from any auth for any action "SEND"
```
The magic word here is "`verify`", which tells OpenSMTPD
to ask for a client certificate and to verify it using the given CA.


#### Client

Now you won't be able to send emails if your client doesn't
present its certificate to the server!
Unfortunately, not all mail clients support this; personally
I use [Thunderbird](https://www.thunderbird.net/) with success.
I won't include any client-specific configuration here,
but I will say this:

For some clients (like Thunderbird), you'll have an easier time
importing your client certificate if you encode it in the
[PKCS #12](https://en.wikipedia.org/wiki/PKCS_12) storage format:
```sh
$ openssl pkcs12 -export -in mailclient.crt -inkey mailclient.key \
        -certfile mailca.crt -out mailclient.pfx
```
OpenSSL will ask you to set a password, which you'll need to
enter again when importing the certificate into the client.



### ~~Client certificates (instead of passwords)~~

UPDATE: Don't do this.
As said above, OpenSMTPD's certificate verification is a mystery,
so for all I know, if you follow the instructions in this subsection,
you might find yourself running an *open* SMTP relay!
That would be bad, because anyone on the Internet
could send emails through your server with zero authentication.
In theory, the client certificates act as authentication,
but, again, the verification process is mysterious,
so I'm just not confident enough to say.

If you really want to, you can use the client certificates
as a substitute for passwords. This is especially useful
if you set up a catchall inbox in Dovecot,
because this will allow you to send emails
from arbitrary addresses from your domain.

To do this, follow the same procedure as in the previous section,
but with a slightly different OpenSMTPD configuration:
```sh
listen on eth0 port 587 tls-require verify pki "example.com" ca "mailca" filter "rspamd" tag "VALID"
action "SEND" relay srs
match from any tag "VALID" for any action "SEND"
```
All incoming connections that present a good certificate
will be tagged as being `VALID`, and their mail will be relayed.

Unfortunately, we're not quite done yet here,
because Rspamd is now very confused...


#### Rspamd

When OpenSMTPD passes a message through Rspamd, it also includes
some metadata, most notably whether the sender has authenticated
successfully with OpenSMTPD... which is now no longer the case
for submissions, because we've removed the `auth` directive!

Rspamd therefore starts regarding these outgoing emails
as *incoming* emails, because they don't seem
to come from a trusted user. So instead of signing them with DKIM
and handing them back to OpenSMTPD, it will do a full spam scan.
If they get a high spam score (which is likely for short test emails),
*your* spam filter, running on *your* server,
will be flagging *your* messages as spam!

The solution is to whitelist your domain(s) in Rspamd,
so it won't scan them. To do this, create a new file
`/etc/rspamd/local.d/settings.conf` with these contents,
where `foo.com` and `bar.com` are the domains to whitelist:
```c
outbound {
	priority = high;
	from = "@foo.com";
	from = "@bar.com";
	apply {
		actions {
			add_header = 1000;
		}
	}
}
```
Setting `priority` to `high` ensures that Rspamd checks
this rule before doing anything else.
You can add any number of `from` directives;
this rule will be applied if any of them match.
It only sets the threshold for the action `add_header` to `1000`.
That is, if the email doesn't get a spam score of at least 1000
(the default is 6) Rspamd will not add any spam tags.

Because Rspamd is still regarding your emails as inbound,
you also need to change the global settings of
the DKIM signer in `/etc/rspamd/local.d/dkim_signing.conf`,
such that they include the following:
```c
sign_inbound = true;
allow_hdrfrom_mismatch  = true;
allow_username_mismatch = true;
```
This tells Rspamd to add DKIM signatures to incoming emails,
which in this case includes yours.
Allowing these mismatches ensures that the messages still get signed,
even if you're sending from an arbitrary address.



## Dovecot

### Catchall inbox

In Dovecot, you can create a catch-all inbox that will accept all
emails sent to your domain that don't match anyone in `/etc/dovecot/users`.
Just add another `userdb` block *after* the first:
```sh
userdb {
	driver = static
	args = uid=vmail gid=vmail home=/var/vmail/catchall allow_all_users=yes
}
```
The `static` driver means there is no table file:
all configuration is directly within this `userdb` block.
If we don't specify `allow_all_users=yes`, then Dovecot
will check whether users exist using the `passdb` table,
and will conclude that the recipient is invalid.