At some point, my Nextcloud quietly stopped being able to send email. This is currently Nextcloud 27.1.3, running in docker on Debian 11.8, but it might have been broken for some time.
When setting up exim on Debian, debconf asks you a few questions and generates a default config. My setup has no other deviations from the default, and the only notable config option is to set it to listen only on some specified local addresses.
Nextcloud is set to connect to one of those local addresses to send email, and this used to work. Now however, the email setting test fails with a generic error message, and causes exim to log
TLS error on connection from (<hostname>) [172.21.0.2] (gnutls_handshake): Decryption has failed.
Long story short, this happens because exim uses a self-signed certificate out of the box, and Nextcloud is unhappy about it but won’t tell you that.
I don’t care about TLS at all. This traffic never leaves the local machine, and has no need to be encrypted, but that doesn’t appear to be an option. Nextcloud allows either “TLS” or “None/STARTTLS” as its encryption options; there is no “None”. In theory it should be possible to get exim to stop advertising STARTTLS support in response to
EHLO; in practice after an hour or so trying every config option that looked relevant, I’m forced to conclude that it’s not actually possible – I found some references online to compiling it without TLS support, but I’m not willing to go that far.
Okay, so I need an actual certificate that will propitiate Nextcloud. Letsencrypt to the rescue? Well yes, but also erghh. First, a TLS certificate can obviously only be generated for a hostname, not an IP address, so I created a new DNS entry pointing to the
10.x.x.x local address in question, and reconfigured Nextcloud to use that hostname. The I generated the certificate using acme-dns for verification – this might not be the best option, but I already have it set up so it’s easy for me. One way or another, trying to create a certificate for a private service is almost certainly going to require a DNS-01 challenge.
Debian’s exim is set up so that most configuration is done by editing
/etc/exim4/update-exim4.conf.conf, then running
update-exim4.conf, then restarting the
exim4 service. The config lines needed are:
So far so good, but it still doesn’t work. Quite reasonably,
certbot will only allow creating certificate files accessible by
root. The idea is that a service needing a certificate should start up as
root, read whatever privileged data it needs, then drop privileges. Sadly, exim does not do this. It runs as
Debian-exim, and only attempts to read the certificate on demand. This leads to errors like
TLS error on connection from (<hostname>) [172.21.0.2] (cert/key setup: cert=/path/to/fullchain.pem key=/path/to/privkey.pem): Error while reading file.
The only solution I’ve found so far is to copy the files to another location and change their owner to
Debian-exim. Finally, this works, and Nextcloud can now send emails!
One remaining problem: I don’t want to have to repeat this every few months when the certificate is updated. I see that there is a promising-looking
/etc/letsencrypt/renewal-hooks/deploy/, and I’m really hoping that I can just drop a shell script in there and call it a day…
Update: The hook worked first time.