Skip to content

Instantly share code, notes, and snippets.

@greenmoss
Last active May 23, 2024 18:13
Show Gist options
  • Save greenmoss/8ee9d4acd3a21df699cde2225a78399e to your computer and use it in GitHub Desktop.
Save greenmoss/8ee9d4acd3a21df699cde2225a78399e to your computer and use it in GitHub Desktop.
This script renews letsecnrypt SSL certificates using Cloudflare dns-1 renewal. It assumes you are using Mailcow.
#!/usr/bin/env bash
# This script renews letsecnrypt SSL certificates using Cloudflare dns-1 renewal
# It assumes you are using Mailcow
set -euo pipefail
# REQUIRED set these:
your_email=letsencrypt@your.domain
your_domain=mail.your.domain # only tested with single domain
cloudflare_ini_path=/root/.cloudflare # add your Cloudflare file here, called cloudflare.ini
# OPTIONAL also set these:
log_file=/var/log/certbot-cloudflare.log # if you don't want any logs, change it to /dev/null
# send all output and errors to log file
exec 1>$log_file
exec 2>&1
# log what we are doing
set -x
date # overwrite, no log rotate!
echo "starting renewal"
docker pull certbot/dns-cloudflare
docker run --rm \
-v $cloudflare_ini_path/cloudflare.ini:/cloudflare.ini \
-v /opt/mailcow-dockerized/data/assets/ssl:/etc/letsencrypt \
certbot/dns-cloudflare \
certonly -n --agree-tos -m $your_email \
--dns-cloudflare --dns-cloudflare-credentials /cloudflare.ini \
-d $your_domain
cd /opt/mailcow-dockerized/data/assets/ssl
newcerts=$(find live/$your_domain/ -mmin -5)
if [ -z "$newcerts" ]; then
echo "no renewals found, not restarting services"
exit
fi
ln -sfv live/$your_domain/privkey.pem key.pem
ln -sfv live/$your_domain/cert.pem cert.pem
cd ../../..
function reload_ssl_service () {
service=$1
port=$2
echo "restarting SSL service $1 on port $2"
docker-compose restart $service
timeout 30 sh -c '\
while ! \
openssl s_client -showcerts -connect $0:$1 2>/dev/null </dev/null | openssl x509 -noout 2>/dev/null; do
# $0 and $1 are inside single quotes, which means they expand to the arguments provided to sh -c
sleep 1
done' $your_domain $port
echo "$service SSL cert expiration"
openssl s_client -showcerts -connect $your_domain:$port 2>/dev/null </dev/null | openssl x509 -noout -text | grep 'Not After'
}
reload_ssl_service nginx-mailcow 443
reload_ssl_service dovecot-mailcow 993
reload_ssl_service postfix-mailcow 465
date
echo "completed"
@gpz1100
Copy link

gpz1100 commented Jul 28, 2023

Logs indicate all 3 have successfully restarted. Restarting manually didn't make any difference.

I think the issue is path related. Mailcow docs say not to use simlinks for certs.

Built in acme client places certs in to /data/assets/ssl/{domain.com}. It appears /data/assets/ssl is then mapped to the containers as /etc/ssl/mail. Your script symlinks them to /data/assets/ssl directly. From there it gets confusing.

Perhaps there's been some changes to cert placement in the current version. (2023-05a).

Edit. Rereading the mailcow docs - specifically https://docs.mailcow.email/post_installation/firststeps-ssl/#how-to-use-your-own-certificate

Does indeed indicate


To use your own certificates, just save the combined certificate (containing the certificate and intermediate CA/CA if any) to data/assets/ssl/cert.pem and the corresponding key to data/assets/ssl/key.pem.

IMPORTANT: Do not use symbolic links! Make sure you copy the certificates and do not link them to data/assets/ssl.


I'm not sure how /data/assets/ssl/{domain.com} got created in my instance to begin with. It does have a date of few weeks ago, so perhaps it was made by the built in acme client. So the only change then is copying files rather than symlinking?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment