m-chrzan.xyz
aboutsummaryrefslogtreecommitdiff
path: root/src/blog/dovecot-ssl-certificate-renewal.html.erb
blob: 2cafcde0ad118e8216ea98e581168cdb25c5c45c (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
title: Dovecot SSL Certificate Renewal
date: May 12, 2021 11:06
---
<p>
Just a quick technical note.
</p>

<p>
As mentioned before, I
<a href='<%= path_to "blog_hosting-on-vultr-vps" %>'>
  started self hosting email
</a> a while ago. Exactly three months ago, in fact, and I know this because
that's how long Let's Encrypt SSL certificates are valid
for. Yesterday, my email clients started complaining about an invalid
certificate coming from my mail server.
</p>

<p>
After a few minutes of worrying if I'm being man-in-the-middled, I ssh'd into my
VPS to debug.
</p>

<h3>TL;DR: Root cause and solution</h3>
<p>
Turns out when configuring Dovecot (the IMAP server I use), the SSL certificate
you set is just a static string, not a filename pointing to a file that Dovecot
will read every time it needs to serve it. As such, even after Certbot correctly
got a new certificate, Dovecot was still using the old one.
</p>

<p>
This can be fixed by restarting Dovecot, allowing it to read the new
certificate, assuming its available at the same path.
</p>

<h4>Long term solution</h4>
<p>
I already have a cronjob that runs <code>certbot renew</code> once a month to
renew any SSL certificates going stale. I'm going to change it to instead point
to a script that does

<pre>
certbot renew
systemctl restart dovecot
</pre>

to automate reloading the certificate.
</p>

<h3>Learnings</h3>
<p>
A few things I learned while debugging this issue:
</p>

<h4>Viewing certificate info on the command line</h4>
<p>
My first step was to check the certificate I thought should have been served by
Dovecot. In particular, I wanted to see its expiry date as well as compare its
fingerprint to that reported by my mail client.
</p>

<p>
Certificates are stored in ASCII armor format, which is not human readable.
Turns out you can get a human readable interpretation of your certificate with
the <code>openssl</code> CLI tool:

<pre>
openssl x509 -text -noout -in &lt;certificate file&gt;
</pre>

will get you a detailed, human readable output. For example, you can find a
"Validity" section, specifying the time period during which the certificate is
valid.
</p>

<p>
Adding the <code>-fingerprint</code> flag will also output the fingerprint hash.
The <code>-noout</code> flag is just there to suppress the output of the
non-readable ASCII armor text.
</p>

<h4>Let's Encrypt certificate locations</h4>
<p>
Currently valid certificates from Let's Encrypt are stored under
<code>/etc/letsencrypt/live/&lt;domain name&gt;/</code>. This is where my
Dovecot server was configured to get its certificate from, so before I realized
that the certificate is only read once, I was surprised that the fingerprint of
the certificate stored here did not match the one my mail client was receiving.
</p>

<p>
Turns out Let's Encrypt also stores historic certificates under
<code>/etc/letsencrypt/archive/</code>. Here I was able to find the certificate
with a matching fingerprint to the faulty one received by my client, and confirm
that it did indeed expire yesterday. Finding this is what led me to realizing
how Dovecot handles its certificate configuration.
</p>