54 lines
2.4 KiB
Bash
Executable File
54 lines
2.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Keep the UseSend SMTP proxy's TLS certificate in step with NPM's Let's Encrypt store.
|
|
#
|
|
# The proxy refuses AUTH until STARTTLS succeeds, so an expired or mismatched cert
|
|
# takes down ALL outbound mail for every app that sends through it -- and it fails
|
|
# with a bare TLS error that never mentions certificates. Hence this timer.
|
|
#
|
|
# Copies rather than bind-mounts because NPM mounts its letsencrypt tree with :Z
|
|
# (private SELinux category) and two consumers on one path fight over the label.
|
|
#
|
|
# Uses npm-43 because it carries *.usesend.gbrown.org, the SAN that validates
|
|
# smtp.usesend.gbrown.org. A wildcard matches exactly ONE label, so *.gbrown.org
|
|
# alone would not cover it.
|
|
set -euo pipefail
|
|
|
|
SRC=$HOME/Server/Nginx_Proxy_Manager/volumes/letsencrypt/live/npm-43
|
|
DST=$HOME/Server/UseSend/certs
|
|
CONTAINER=usesend-smtp-server
|
|
|
|
fp() { openssl x509 -in "$1" -noout -fingerprint -sha256 2>/dev/null | cut -d= -f2; }
|
|
certpub(){ openssl x509 -in "$1" -noout -pubkey 2>/dev/null | openssl sha256 | awk '{print $2}'; }
|
|
keypub() { openssl pkey -in "$1" -pubout 2>/dev/null | openssl sha256 | awk '{print $2}'; }
|
|
|
|
[ -r "$SRC/fullchain.pem" ] && [ -r "$SRC/privkey.pem" ] || {
|
|
echo "source cert unreadable under $SRC -- did NPM move it?" >&2; exit 1; }
|
|
|
|
src_fp=$(fp "$SRC/fullchain.pem")
|
|
dst_fp=$(fp "$DST/fullchain.pem" 2>/dev/null || true)
|
|
|
|
if [ -n "$dst_fp" ] && [ "$src_fp" = "$dst_fp" ]; then
|
|
echo "cert unchanged (${src_fp:0:17}...); nothing to do"
|
|
exit 0
|
|
fi
|
|
|
|
# Refuse to install a half-renewed pair: certbot writes the two files separately, so
|
|
# a run that lands between them would leave the proxy with a key that is not its cert.
|
|
if [ "$(certpub "$SRC/fullchain.pem")" != "$(keypub "$SRC/privkey.pem")" ]; then
|
|
echo "source cert and key do not match -- renewal probably mid-flight, retrying next run" >&2
|
|
exit 0
|
|
fi
|
|
|
|
echo "cert changed (${dst_fp:0:17}... -> ${src_fp:0:17}...), installing"
|
|
# -L dereferences: live/ entries are symlinks into ../../archive/, outside this copy.
|
|
cp -L "$SRC/fullchain.pem" "$DST/fullchain.pem.new"
|
|
cp -L "$SRC/privkey.pem" "$DST/privkey.pem.new"
|
|
chmod 644 "$DST/fullchain.pem.new"
|
|
chmod 640 "$DST/privkey.pem.new"
|
|
mv -f "$DST/fullchain.pem.new" "$DST/fullchain.pem"
|
|
mv -f "$DST/privkey.pem.new" "$DST/privkey.pem"
|
|
|
|
echo "restarting $CONTAINER"
|
|
podman restart "$CONTAINER" >/dev/null
|
|
echo "done; cert now valid until $(openssl x509 -in "$DST/fullchain.pem" -noout -enddate | cut -d= -f2)"
|