#!/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)"