The Dynamic Security plugin refuses to start the broker unless dynamic-security.json already holds an admin client, and no reconcile loop seeds it (novox/hq ADR 0052). Add a run-once init container, declared before the server container, that runs mosquitto's own bootstrap entrypoint in the runtime image: it seeds the store offline via mosquitto_ctrl and exits, and the host gates the broker on its completion. The bootstrap hands the seeded file to the broker's user (uid 1883, chown + 0600): the broker must read the seed at startup AND persist to it as clients come and go, but the init container runs as root and would otherwise leave a file the broker can neither read nor rewrite. This is the ownership question ADR 0052 left for the lab to settle. It seeds only when the file is absent, so what the running plugin grows is never clobbered (issue 035). Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
144 lines
4.3 KiB
JSON
144 lines
4.3 KiB
JSON
{
|
|
"module": "mosquitto",
|
|
"version": "1",
|
|
"provides": [
|
|
{
|
|
"name": "mqtt-topic",
|
|
"scope": "mesh"
|
|
}
|
|
],
|
|
"capabilities": [
|
|
"container-runtime"
|
|
],
|
|
"emits": [
|
|
"module.mosquitto.topic.provisioned",
|
|
"module.mosquitto.topic.deprovisioned"
|
|
],
|
|
"consumes": [
|
|
"module.mosquitto.topic.provisioned",
|
|
"module.mosquitto.topic.deprovisioned"
|
|
],
|
|
"serves": {
|
|
"mqtt-topic": {}
|
|
},
|
|
"receives": {
|
|
"mqtt-topic": "/var/lib/mosquitto-module/grants/mesh.json"
|
|
},
|
|
"grants": {
|
|
"mqtt-topic": "/var/lib/mosquitto-module/grants"
|
|
},
|
|
"own-secrets": {
|
|
"admin": "/var/lib/mosquitto-module/admin.secret",
|
|
"broker": "/var/lib/mesh/mosquitto/broker"
|
|
},
|
|
"listens": [
|
|
{
|
|
"port": 1883,
|
|
"protocol": "tcp",
|
|
"from": "mesh",
|
|
"why": "modules on any machine that were granted a topic namespace"
|
|
},
|
|
{
|
|
"port": 8081,
|
|
"protocol": "tcp",
|
|
"from": "mesh",
|
|
"why": "the same broker over MQTT-on-WebSockets, for browser clients"
|
|
}
|
|
],
|
|
"resources": [
|
|
{
|
|
"id": "mesh-state",
|
|
"type": "directory",
|
|
"path": "/var/lib/mesh/mosquitto",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "state",
|
|
"type": "directory",
|
|
"path": "/var/lib/mosquitto-module",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "grants-dir",
|
|
"type": "directory",
|
|
"path": "/var/lib/mosquitto-module/grants",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data",
|
|
"type": "directory",
|
|
"path": "/services/mosquitto/data",
|
|
"mode": "0700",
|
|
"owner": "1883:1883"
|
|
},
|
|
{
|
|
"id": "server-conf",
|
|
"type": "file",
|
|
"path": "/var/lib/mosquitto-module/mosquitto.conf",
|
|
"mode": "0600",
|
|
"owner": "1883:1883",
|
|
"content": "persistence true\npersistence_location /mosquitto/data\n\nlog_dest stdout\nlog_type warning\nlog_type error\nlog_type notice\n\n# Every client authenticates; identities and their per-topic ACLs are managed\n# at runtime by the dynamic security plugin, whose store the plugin itself owns.\nallow_anonymous false\nplugin /usr/lib/mosquitto_dynamic_security.so\nplugin_opt_config_file /mosquitto/data/dynamic-security.json\n\n# MQTT listener\nlistener 1883\n\n# MQTT-over-WebSockets listener\nlistener 8081\nprotocol websockets\n"
|
|
},
|
|
{
|
|
"id": "net",
|
|
"type": "network",
|
|
"name": "mosquitto"
|
|
},
|
|
{
|
|
"id": "bootstrap",
|
|
"type": "container",
|
|
"name": "mosquitto-bootstrap",
|
|
"image": "mesh-runtime-mosquitto@sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
|
"run-once": true,
|
|
"volumes": [
|
|
"/services/mosquitto/data:/mosquitto/data",
|
|
"/var/lib/mosquitto-module/admin.secret:/run/secrets/admin:ro"
|
|
],
|
|
"env": {
|
|
"MESH_PROVISION_MQTT": "mosquitto:1883",
|
|
"MESH_PROVISION_ADMIN_USER": "mesh-admin",
|
|
"MESH_PROVISION_PASSWORD_FILE": "/run/secrets/admin",
|
|
"MESH_DYNSEC_FILE": "/mosquitto/data/dynamic-security.json"
|
|
},
|
|
"args": [
|
|
"run",
|
|
"/app/modules/mosquitto/dist/bootstrap/index.js"
|
|
]
|
|
},
|
|
{
|
|
"id": "server",
|
|
"type": "container",
|
|
"name": "mosquitto",
|
|
"image": "eclipse-mosquitto@sha256:6f8d8a947c506f8a2290ec65cd4bd2bc7cb4d43fb5f6271f861cb013e2ef9797",
|
|
"network": "mosquitto",
|
|
"ports": [
|
|
"1883",
|
|
"8081"
|
|
],
|
|
"volumes": [
|
|
"/services/mosquitto/data:/mosquitto/data",
|
|
"/var/lib/mosquitto-module/mosquitto.conf:/mosquitto/config/mosquitto.conf:ro"
|
|
]
|
|
},
|
|
{
|
|
"id": "runtime",
|
|
"type": "container",
|
|
"name": "mesh-mosquitto",
|
|
"image": "mesh-runtime-mosquitto@sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
|
"network": "mosquitto",
|
|
"volumes": [
|
|
"/var/lib/mesh/mosquitto/broker:/run/secrets/broker:ro",
|
|
"/var/lib/mosquitto-module/grants:/var/lib/mosquitto-module/grants:ro",
|
|
"/var/lib/mosquitto-module/admin.secret:/run/secrets/admin:ro"
|
|
],
|
|
"env": {
|
|
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
"MESH_RECEIVES": "/var/lib/mosquitto-module/grants/mesh.json",
|
|
"MESH_PROVISION_MQTT": "mosquitto:1883",
|
|
"MESH_PROVISION_ADMIN_USER": "mesh-admin",
|
|
"MESH_PROVISION_PASSWORD_FILE": "/run/secrets/admin"
|
|
}
|
|
}
|
|
]
|
|
}
|