summaryrefslogtreecommitdiff
path: root/source/blog/2022/email-server-revisited/index.md
blob: 4519bae0b69f28d79d00a35c63f3ae42abd99be8 (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
---
title: "Revisiting my email server in 2022"
date: 2022-09-12
layout: "blog"
toc: true
---

More than two years have passed since my first post about
[setting up an email server in 2020 with OpenSMTPD and Dovecot](/blog/2020/email-server/)
and [its sequel](/blog/2020/email-server-extras/).
Since then, my server been going strong, with a few minor hiccups along the way.
In this post, I'll explain some of the changes I made.

This assumes you've followed the preceding guides:
the configuration snippets given here
should be interpreted as modifications to those guides,
*not* as complete setups!

Last updated on 2022-09-12.



## More about DMARC

When I wrote my original guide,
I didn't properly understand how DMARC works:
I misinterpreted it as an optional wrapper around SPF and DKIM.
But oh God, I was wrong.
Simon Andrews' article "[I figured out how DMARC works, and it almost broke me](https://simonandrews.ca/articles/how-to-set-up-spf-dkim-dmarc)"
showed me the ugly truth,
and I highly recommend reading it.
Briefly, DMARC does two arguably unrelated things.

Firstly, DMARC provides a way to diagnose issues with your SPF and DKIM configurations,
in the form of reports that get sent to the `ruf=` and/or `rua=` email address(es)
you put in the DNS record.
Without this, there's no way of knowing why
your emails are getting marked as spam.

Secondly, it improves the trustworthiness of SPF and DKIM by enforcing *alignment*.
This means something slightly different for SPF and DKIM,
and boils down to fixing a glaring issue:

* For some reason, in vanilla SMTP, it turns out that the email's `From:` header
  doesn't need to agree with the address in the SMTP `MAIL FROM` command;
  in other words, the server can claim a different sender than what's written in the message's header.
  SPF only verifies the former (i.e. it takes the domain in `MAIL FROM`),
  so one SMTP server can impersonate another.
* An email's DKIM signature header states the domain of the signing server with the `d=` tag,
  but, once again, that doesn't need to agree with the `From:` header's domain.
  DKIM doesn't look at the latter, so an SMTP server can validly sign impersonated messages.

DMARC's alignment refers to checking whether the domains match up for SPF and DKIM,
thus ensuring that an SMTP server can't pretend to be someone else.
It sounds obvious, but nope, apparently it wasn't before DMARC was made.



## DKIMproxy

To add DKIM signatures to my messages,
I switched from Rspamd to [DKIMproxy](http://dkimproxy.sourceforge.net/).



### Motivation

To sign outgoing emails for DKIM, my original guide used Rspamd ---
an unusual choice, since it's a spam filter designed to act on *incoming* messages.
Later, in the [sequel](/blog/2020/email-server-extras/) to that guide,
I needed some ugly workarounds to compensate for Rspamd's "smartness"
when I tried to play around with authentication schemes in OpenSMTPD.
Clearly, this wasn't ideal.

However, the reason I cut my losses and switched to another DKIM signer
was actually a bug in
MXToolBox' [deliverability tool](https://mxtoolbox.com/deliverability).
It appears that no matter what you do,
this tool claims that your email's signature fails validation.
I'm not the first to notice this issue:
see e.g. [this question](https://serverfault.com/questions/1005818/dkim-validating-but-mxtoolbox-reports-as-dkim-signature-not-verified) on Server Fault.
Other tools like the [DKIM validator](https://dkimvalidator.com/)
say that my DKIM signatures are correct.

There aren't many open-source alternatives out there for DKIM signing:
the only ones I know of are [OpenDKIM](http://www.opendkim.org/)
and DKIMproxy.
The former is a so-called "milter",
meaning it can only interact with MTAs via the milter API,
which is only supported by [Sendmail](https://www.proofpoint.com/us/products/email-protection/open-source-email-solution)
and [Postfix](https://www.postfix.org/).
Since we're using OpenSMTPD,
our only option is DKIMproxy,
which consists of two daemons:
`dkimproxy.out` to sign outgoing mail,
and `dkimproxy.in` to verify incoming mail.
We just need the former;
Rspamd is still convenient for handling the latter's functionality.



### DKIM settings

Let's start by disabling Rspamd's DKIM signer
in `/etc/rspamd/local.d/dkim_signing.conf`:
```sh
enabled = false;
```
Then configure `dkimproxy.out` as follows
in `/etc/dkimproxy/dkimproxy_out.conf`.
If you placed your DKIM public key
in a TXT DNS record for `<selector>._domainkey.example.com.`,
and stored your private key in `/path/to/dkim/private.key`,
then:
```sh
# Receive emails on 10027, sign them, and forward them to 10028
listen    127.0.0.1:10027
relay     127.0.0.1:10028

# Settings for email signing
domain    example.com
signature dkim(c=relaxed/relaxed,a=rsa-sha256)
keyfile   /path/to/dkim/private.key
selector  <selector>
```
Here, `rsa-sha256` is the signature algorithm
(this is the best available, because DKIM is ancient),
and `relaxed/relaxed` is the so-called *canonicalization* method,
which is applied before signing and verification,
to prevents failures if e.g. the email's whitespace gets changed in transit.



### OpenSMTPD settings

OpenSMTPD needs to send all outbound mail through `dkimproxy.out`.
In `/etc/smtpd/smtpd.conf`, we tell it that all emails coming from the MUA
must be relayed through `localhost:10027`, and then, after DKIM signing,
picked up again on `localhost:10028`:
```sh
# Outbound
listen on eth0 port 465 smtps       pki "example.com" auth <passwds> tag "TRUSTED"
listen on eth0 port 587 tls-require pki "example.com" auth <passwds> tag "TRUSTED"
action "SIGN" relay host "localhost:10027"
match from any tag "TRUSTED" for any action "SIGN"

listen on lo port 10028 tag "SIGNED"
action "SEND" relay srs
match from any tag "SIGNED" for any action "SEND"
```
The tag name `TRUSTED` reflects that only messages from trusted
(i.e. authenticated) MUAs should be signed.
After signing, emails get the tag `SIGNED`,
and are sent to their destination as usual.



## SMTP relay

Instead of sending my emails directly to their destinations,
I now send them to an SMTP relay server,
which then passes them on to their actual destinations.



### Motivation

Large email providers such as Google, Microsoft and Yahoo
manage many user accounts, so for them it makes sense
to keep track of IP-based sender reputations.
For example, if a number of low-quality emails are sent from a single IP
to many of the accounts they manage,
it's cheaper to simply blacklist that IP entirely at the MTA level,
rather than passing each message through a computationally-intensive spam filter.

But, as usual, Microsoft has to ruin everything with their draconic policies.
In a stroke of genius,
someone there decided to blindly ban IPs,
seemingly in blocks belonging to VPS providers.
One day, I tried to send an email to an Outlook-based account,
and OpenSMTPD reported it had been unable to make the delivery,
because Microsoft had thrown an error:

<a href="microsoft-bounce.png">
<img src="microsoft-bounce.png" class="darkinv" style="width:100%">
</a>

To their credit, they seem to be offering a way out.
This approach is reasonable: preventively ban high-risk IP ranges,
and allow "trustworthy" servers at the owner's request.
I got error 5.7.511, asking me to send an email to a support address.
If you're lucky, you may have a different error,
and get the opportunity to use the slick [delist portal](https://sender.office.com/) instead.
The URL in the bounce message links to [this list](https://go.microsoft.com/fwlink/?LinkId=526653) of error codes.

I confess, I never actually bothered to forward the message to the provided address:
my initial email was time-sensitive,
so I couldn't afford to wait for Microsoft's response.
Also, their customer support's stellar reputation precedes them,
so I chose to use my time more wisely.
Even if they would've resolved it nicely,
there's nothing preventing Microsoft (or any other provider)
from breaking my deliverability again in the future.
Instead, I opted for a compromise.

As a result of providers' IP reputation systems,
a whole new business has appeared: SMTP relays.
They offer to take the issue out of your hands:
you send your emails through their servers,
and they do their best to deliver them to large providers.
SMTP relays are mostly used for sending marketing emails in bulk,
but are also useful to avoid small-scale problems as described above.
There are many SMTP relay services to choose from, at various prices.

Using an SMTP relay results in more reliable delivery of your messages to large providers,
but an obvious concern is privacy:
the relay server can read all your outgoing emails (but not incoming),
so you'll have to trust the service you choose.
But it's no worse than using a major provider,
and if you're sending sensitive material, why use email in the first place?
Personally, I use [SMTP2GO](https://www.smtp2go.com/),
but I can't say how good they are; do your own research.



### OpenSMTPD settings

Once you've chosen an SMTP relay service, let's say `relay.com`,
and set up your account,
they'll let you create credentials to use their SMTP servers.
Suppose these credentials are `<username>` and `<password>`,
create a file `/etc/smtpd/relaypw` with contents:

```sh
<label> <username>:<password>
```

Where `<label>` is a string of your choice.
Note that `<password>` must be plaintext,
because it needs to be provided to the relay server.
To tell OpenSMTPD to use the relay,
edit `/etc/smtpd/smtpd.conf` as follows,
i.e. register the `relaypw` table and modify the `SEND` action:

```sh
table relaypw "/etc/smtpd/relaypw"

#action "SEND" relay srs ### Replace this line with the following:
action "SEND" relay host "smtps://<label>@relay.com:465" auth <relaypw> pki "example.com" srs
```

With `<label>` replaced by the label you chose earlier,
and `relay.com:465` replaced by the host/port combination
given in the relay service's documentation.
Depending on what they support, you may also need to change the protocol `smtps://` (SMTP over TLS)
to `smtp://` (SMTP with optional STARTTLS) or `smtp+tls://` (SMTP with mandatory STARTTLS).
I recommend SMTPS.



### DNS records

If you've been paying attention so far, you have a burning question:
what about SPF and stuff?
Wasn't the point to prevent SMTP servers from sending emails on others' behalf?
Well, yes, so you'll need to add some DNS records for the relay to work.
The details depend on which service you choose,
so they'll tell you what to do when you're setting up your account.
As an example, based on my experience with SMTP2GO,
you may need to add two CNAME records like:

```sh
emXXXXXX.example.com. CNAME return.relay.com.
sXXXXXX._domainkey.example.com. CNAME dkim.relay.com.
```

Which, roughly speaking, respectively enable SPF and DKIM.
Here, `XXXXXX` is an ID that the service will provide to you,
since the DNS addresses must be unique.

Technically, those two DNS records should be enough for SPF and DKIM,
but in practice, it seems that different email providers/tools have slightly
different interpretations of these standards,
and can get confused when an email passes through multiple unaffiliated SMTP servers.
Therefore, I recommend explicitly adding the relay to your SPF policy:

```sh
example.com TXT "v=spf1 mx include:spf.relay.com -all"
```

Your relay service might not publicly document their version of `spf.relay.com`,
but you can find it by looking up your CNAME `emXXXXXX.example.com` (or equivalent)
in MXToolBox' [ SPF tool](https://mxtoolbox.com/spf.aspx).

I also recommend relaxing your DMARC domain policy for SPF and DKIM,
such that your CNAME subdomains still pass the alignment checks:
```sh
_dmarc.example.com. TXT "v=DMARC1; aspf=r; adkim=r; ..."
```

Be sure to check that it's set up correctly
using the website "[Learn and test DMARC](https://www.learndmarc.com/)".