#!/bin/bash # Managed by Ansible — do not edit manually. # # Copies Caddy's managed TLS cert for mqtt.{{ domain }} out of Caddy's # storage (root:caddy, 0700 — nothing else can read it) into a location the # mosquitto container can read, then SIGHUPs the broker so it picks up the # new cert without a full restart. # # Triggered by mqtt-cert-sync.path.j2 (a systemd path unit) watching the # source cert file for changes — a path unit rather than cron so this fires # on the actual write instead of racing a polling interval. # # CONFIRMED 2026-08-16 against a real issuance on drb-server: this path is # correct, and the copied cert came out as CN=mqtt.drb.cusano.net issued by # Let's Encrypt. Was previously flagged unverified. # # UNVERIFIED: mosquitto 2.x reloading TLS certs on SIGHUP without dropping # connections is documented upstream but untested here. If listener 8883 # doesn't pick up the new cert (check `docker compose logs mosquitto` after # a sync), replace the `kill -s HUP` line below with a full # `docker compose ... restart mosquitto` instead. set -euo pipefail DOMAIN="mqtt.{{ domain }}" CADDY_CERT_DIR="/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/${DOMAIN}" DEST_DIR="/opt/drb/mosquitto-certs" APP_DIR="{{ app_dir }}" SRC_CERT="${CADDY_CERT_DIR}/${DOMAIN}.crt" SRC_KEY="${CADDY_CERT_DIR}/${DOMAIN}.key" if [ ! -f "$SRC_CERT" ] || [ ! -f "$SRC_KEY" ]; then echo "sync-mqtt-cert: source cert/key not found yet at $CADDY_CERT_DIR — Caddy may not have issued it yet." >&2 exit 0 fi mkdir -p "$DEST_DIR" # Copy, don't symlink — nothing outside the caddy user can read the # originals (0700-owned), so mosquitto (running as a different container/ # user) needs its own readable copy, not a pointer to an unreadable file. cp "$SRC_CERT" "$DEST_DIR/mqtt.crt" cp "$SRC_KEY" "$DEST_DIR/mqtt.key" # Ownership matters: the stock eclipse-mosquitto entrypoint drops privileges # to the in-image `mosquitto` user (uid/gid 1883) — the broker does NOT run # as root, despite what an earlier note in DEFERRED.md claimed. Proof from a # real deploy: "running mosquitto as user: mosquitto", immediately followed # by "Unable to load server certificate ... Permission denied" on a # 600 root:root cert. The host has no such user, so use the numeric gid. # The cert is public material (0644); the key is group-read only (0640). chown root:1883 "$DEST_DIR/mqtt.crt" "$DEST_DIR/mqtt.key" chmod 644 "$DEST_DIR/mqtt.crt" chmod 640 "$DEST_DIR/mqtt.key" cd "$APP_DIR" docker compose -f docker-compose.yml -f docker-compose.prod.yml kill -s HUP mosquitto echo "sync-mqtt-cert: copied cert for ${DOMAIN} and sent SIGHUP to mosquitto."