furtka-apps/apps/mosquitto/scripts/ensure-client.sh

48 lines
1.9 KiB
Bash
Raw Normal View History

#!/bin/sh
# on_start hook (provider: mosquitto).
#
# Runs INSIDE the mosquitto container on EVERY boot, before the consumer's
# container starts (reconcile visits providers first). Must be idempotent.
#
# Job: make sure the consumer's MQTT account still exists. The common path is
# a no-op — the passwd file lives on a persistent volume and survives reboots.
# This only does work if the account went missing (e.g. the data volume was
# wiped), in which case it restores the SAME password the consumer holds. It
# never rotates a live password, so the consumer's stored MQTT_PASS keeps
# working.
#
# Where the password comes from:
# - Core that injects consumer .env into on_start hooks hands it to us as
# $FURTKA_CONSUMER_ENV_MQTT_PASS — the clean path.
# - Older core gives on_start no consumer context, so we fall back to the
# copy provision-client.sh stashed on our own volume at install time.
# This hook's stdout is discarded by the reconciler (unlike on_install), so
# restoring the password is the only way to keep the consumer connectable.
# Errors go to stderr and fail the hook, which makes reconcile skip the
# consumer's `compose up` rather than start it with a broken account.
set -eu
user="${FURTKA_CONSUMER_APP:?ensure-client: FURTKA_CONSUMER_APP not set}"
passwd_file=/mosquitto/data/passwd
stash="/mosquitto/data/furtka-clients/${user}.pw"
# Account already present → nothing to do.
if [ -f "$passwd_file" ] && grep -q "^${user}:" "$passwd_file"; then
exit 0
fi
pass="${FURTKA_CONSUMER_ENV_MQTT_PASS:-}"
if [ -z "$pass" ] && [ -f "$stash" ]; then
pass="$(cat "$stash")"
fi
if [ -z "$pass" ]; then
echo "ensure-client: account '${user}' is missing and no password is" \
"available to restore it (no FURTKA_CONSUMER_ENV_MQTT_PASS, no stash);" \
"reinstall the app to re-provision." >&2
exit 1
fi
mosquitto_passwd -b "$passwd_file" "$user" "$pass"
kill -HUP 1 2>/dev/null || true