Escape $ in the compose-interpolated .env so MQTT passwords survive
Build & Deploy / Build & push images (push) Successful in 4m2s
Build & Deploy / Deploy to VM (push) Failing after 2m12s

Compose interpolates the top-level .env, so a password containing "$fP" was
read as the variable $fP and replaced with an empty string — hence the
repeated "The \"fP\" variable is not set" warnings on every compose command.

The env_file templates are not interpolated, so c2-core kept the literal
password while mosquitto's entrypoint received the mangled one. The two sides
disagreed and c2-core could not authenticate to the broker. Escaping $ as $$
here (and only here) makes compose collapse it back to the real value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-08-09 21:28:38 -04:00
parent 6140dd7b9c
commit 971ab74d44
@@ -1,10 +1,19 @@
# Top-level docker-compose environment — MQTT credentials and registry prefix.
# Managed by Ansible. Do not edit manually.
#
# The passwords are $-escaped ($ -> $$). Compose INTERPOLATES this file, so a
# raw "$fP" in a password is read as the variable $fP, warned about, and
# replaced with an empty string. The env_file templates (c2-core.env.j2 etc.)
# are NOT interpolated, so they keep the literal value — which means an
# unescaped $ here silently gives mosquitto and c2-core two different
# passwords and MQTT auth fails. Compose collapses $$ back to a single $, so
# both sides end up with the real password.
# Do not add the same escaping to the env_file templates; it would be literal.
MQTT_C2_USER={{ vault_mqtt_c2_user }}
MQTT_C2_PASS={{ vault_mqtt_c2_pass }}
MQTT_C2_PASS={{ vault_mqtt_c2_pass | replace('$', '$$') }}
MQTT_NODE_USER={{ vault_mqtt_node_user }}
MQTT_NODE_PASS={{ vault_mqtt_node_pass }}
MQTT_NODE_PASS={{ vault_mqtt_node_pass | replace('$', '$$') }}
# Container registry prefix — docker compose uses this for image: ${REGISTRY}/name:latest
REGISTRY={{ vault_registry }}