Avvio rapido
Dopo l'installazione il broker ascolta su tre porte: :1883 per MQTT su TCP, :8883 per MQTT su TLS e :8567 per il pannello web e MQTT su WebSocket al percorso /mqtt.
Apri http://localhost:8567 e accedi come amministratore. Le credenziali predefinite sono admin / admin; cambia la password nella sezione Profilo subito dopo il primo accesso.
Crea il tuo primo utente: un nome, una password di almeno otto caratteri e una regola ACL. La regola $u/# dà al dispositivo pieno accesso al proprio ramo: nel pannello c'è un pulsante «Ramo proprio» proprio per questo.
mosquitto_pub -h localhost -p 1883 -u sensor-42 -P s3nsorPass! \
-t 'sensor-42/temp' -m '21.5'
mosquitto_sub -h localhost -p 1883 -u sensor-42 -P s3nsorPass! \
-t 'sensor-42/#' -v
Installazione su Linux
La strada consigliata è il repository APT: gli aggiornamenti arrivano insieme a quelli di sistema.
sudo wget -qO /usr/share/keyrings/elx-repo.gpg https://repo.um-d.ru/elx-repo.gpg
echo "deb [signed-by=/usr/share/keyrings/elx-repo.gpg] https://repo.um-d.ru stable main" | sudo tee /etc/apt/sources.list.d/elx-repo.list
sudo apt-get update
sudo apt-get install elxmqttbroker
Il pacchetto installa il binario in /opt/elxmqttbroker, crea un utente di sistema elxmqtt senza possibilità di accesso, mette accanto i dati e config.json (modo 0600) e abilita il servizio elxmqttbroker all'avvio.
systemctl status elxmqttbroker # stato
sudo systemctl restart elxmqttbroker # riavvio
journalctl -u elxmqttbroker -f # log
L'unità è vincolata da ProtectSystem=strict: solo la cartella del broker è scrivibile. Per impostazione predefinita il pannello ascolta su 127.0.0.1:8567 e viene pubblicato tramite nginx, mentre le porte MQTT 1883 e 8883 restano esposte direttamente: è un altro protocollo e non ha bisogno di proxy.
Upgrade e Connection e prevedere un proxy_read_timeout generoso: /ws/live e /mqtt sono connessioni WebSocket di lunga durata.Installazione su Windows
La strada consueta è l'installatore elxmqttbroker-setup.exe: mette il programma in Program Files, i dati in C:\ProgramData\ELX MQTT Broker, registra a scelta il servizio ELXMQTTBroker perché parta con il sistema e apre le porte 1883, 8883 e 8567 nel firewall. Parla russo e inglese e richiede i diritti di amministratore.
ProgramData e non accanto al programma: il broker riscrive config.json dal pannello, e un servizio non deve scrivere in Program Files. Disinstallando, quella cartella resta: contiene gli utenti, i loro permessi e i messaggi.La strada portatile è un unico elxmqttbroker.exe: mettilo in una cartella sua ed eseguilo, e accanto compariranno config.json, una cartella data e un certificato TLS autofirmato.
elxmqttbroker.exe # avviare con i valori predefiniti
elxmqttbroker.exe -web :9000 # pannello su un'altra porta
elxmqttbroker.exe -version # versione e marchio di compilazione
Una copia portatile può essere gestita come servizio a mano, con gli stessi comandi usati dall'installatore:
elxmqttbroker.exe -service install -config "C:\ProgramData\ELX MQTT Broker\config.json" -data "C:\ProgramData\ELX MQTT Broker\data"
elxmqttbroker.exe -service start
elxmqttbroker.exe -service stop
elxmqttbroker.exe -service uninstall
Configurazione
config.json è l'unico file che il broker legge. Viene creato al primo avvio e contiene solo ciò che deve essere noto prima della partenza: indirizzi, TLS e l'account dell'amministratore.
{
"dataDir": "data",
"auth": { "username": "admin", "password": "admin" },
"mqtt": { "tcp": ":1883", "tls": ":8883" },
"web": { "addr": ":8567", "tlsAddr": "" },
"tls": { "cert": "", "key": "", "selfSigned": true,
"hosts": ["localhost", "127.0.0.1", "::1"] },
"logLevel": "info",
"logFormat": "text",
"persistence": true
}
Una password in chiaro diventa passwordHash al primo avvio e viene cancellata dal file. Tutto il resto — limiti del protocollo, SMTP, bridge, regole, token API, utenti MQTT — si configura dal pannello e sta in data/users.json; nulla di ciò richiede un riavvio.
Le opzioni da riga di comando prevalgono sul file:
| Opzione | Che cosa imposta |
|---|---|
| -config | percorso di config.json |
| -data | cartella dei dati |
| -mqtt | indirizzo di MQTT su TCP |
| -mqtt-tls | indirizzo di MQTT su TLS |
| -web | indirizzo del pannello web |
| -log | livello di log |
| -version | stampare la versione e uscire |
config.json contiene l'impronta della password dell'amministratore, la password SMTP e le credenziali della fonte di autenticazione esterna. Il file dovrebbe avere il modo 0600.Utenti e permessi
Ci sono due circuiti, e non vanno confusi. L'amministratore è un unico account definito in config.json che gestisce soltanto il pannello: nessun ruolo, nessuna registrazione, e chi entra ha pieno accesso. I dispositivi MQTT sono un elenco di account a parte: un nome da 1 a 64 caratteri, una password, un indicatore di attivazione e permessi per topic.
Una regola ACL si compone di un filtro di topic, una modalità di accesso e una decisione:
| Campo | Valori |
|---|---|
| Filtro | un normale filtro MQTT: sensors/+/temp, dev/#, $u/# |
| Accesso | read, write o readwrite |
| Decisione | consentire o negare |
Le regole si controllano dall'alto verso il basso, vince la prima corrispondenza e tutto è negato per impostazione predefinita: senza un permesso esplicito non c'è accesso. L'ordine fa parte della configurazione, quindi le regole si trascinano con il mouse o si spostano con le frecce.
Il filtro espande $u, il nome del client che si collega. Un'unica regola $u/# confina ogni dispositivo al proprio ramo e ti risparmia una regola per nome.
Le password sono conservate come PBKDF2-HMAC-SHA256 con 210 000 iterazioni e sale casuale. I token di sessione e di API sono conservati solo come impronta.
Autenticazione MQTT
La fonte di credenziali si sceglie in Impostazioni → Autenticazione, e ne è attiva una sola per volta. Il cambio avviene a caldo: la nuova fonte viene prima verificata con il pulsante Prova e solo se risponde il broker commuta e disconnette i client perché ciascuno si autentichi di nuovo. Se la fonte non è raggiungibile, non cambia nulla.
| Fonte | Quando serve |
|---|---|
internal | gli account si gestiscono dal pannello — l'impostazione predefinita |
mysql / mariadb | i dispositivi vivono già nel database del tuo sistema |
sqlite | lo stesso, ma il database è un file |
csv | un elenco semplice; i file vengono ricaricati quando cambiano |
jwt | il dispositivo presenta un token firmato invece di una password |
http | il tuo servizio decide a ogni connessione |
Lo schema di MySQL e MariaDB segue quello di EMQX:
CREATE TABLE mqtt_user (username VARCHAR(128) PRIMARY KEY, password VARCHAR(255));
CREATE TABLE mqtt_acl (username VARCHAR(128), action VARCHAR(16),
permission VARCHAR(16), topic VARCHAR(255));
-- action: publish | subscribe | pubsub; permission: allow | deny
INSERT INTO mqtt_user VALUES ('dev', SHA2('devpass',256));
INSERT INTO mqtt_acl VALUES ('dev','pubsub','allow','dev/#');
"mqttAuth": {
"backend": "mariadb",
"sql": {
"dsn": "mqtt:mqttpass@tcp(127.0.0.1:3306)/mqtt",
"passwordQuery": "SELECT password FROM mqtt_user WHERE username = ${username} LIMIT 1",
"aclQuery": "SELECT action, permission, topic FROM mqtt_acl WHERE username = ${username}",
"passwordHash": "sha256"
}
}
${username} e ${clientid} sono legati come parametri della query invece di essere concatenati, quindi l'iniezione è impossibile. passwordHash accetta plain, sha256, sha512, bcrypt e pbkdf2.
Un JWT va nel campo della password o del nome utente. Sono supportati HS256/384/512 e RS256/384/512; vengono verificati la firma, exp e nbf e, a scelta, iss, aud e qualsiasi claim tuo. I permessi arrivano dal claim acl in uno dei due formati EMQX, e is_superuser annulla del tutto la verifica.
"alg": "none" non passa.La fonte HTTP riceve {username, password, clientid, peerhost} e risponde con {"result": "allow", "acl": […]}. Un 200 vuoto vale come consenso; deny, 401, 403, un tempo scaduto e qualsiasi errore sono un rifiuto: un servizio di autorizzazione irraggiungibile non deve diventare una porta aperta. Le risposte restano in cache per cacheSeconds.
Regole: riscrittura, sottoscrizione automatica, limiti
La riscrittura dei topic sostituisce al volo il nome di un topic. Una regola è un filtro MQTT per una preselezione economica, un'espressione regolare e un modello di destinazione con i gruppi $1…$9:
| Quando | Filtro | Espressione | Nuovo topic |
|---|---|---|---|
| Sempre | raw/# | ^raw/(.+)$ | cooked/$1 |
L'ordine segue EMQX: prima la riscrittura, poi il controllo delle ACL — i permessi si verificano sul topic sotto cui il messaggio viaggerà davvero. UNSUBSCRIBE passa dalla stessa riscrittura di SUBSCRIBE, altrimenti un client non potrebbe mai annullare la sottoscrizione.
La sottoscrizione automatica crea sottoscrizioni subito dopo la connessione, senza SUBSCRIBE da parte del client. Una regola %c/# o %u/# dà a ogni client il proprio ramo senza configurazioni manuali.
I limiti per topic servono quando un client nel complesso si comporta bene, ma un topic preciso arriva troppo spesso:
| Campo | Che cosa imposta |
|---|---|
| Schema del topic | un normale filtro MQTT: sensors/+/temp |
| Azione | ridurre a una cadenza o scartare del tutto |
| Messaggi al secondo | la cadenza desiderata; sono ammesse le frazioni (0.2 è uno ogni cinque secondi) |
| Conteggio per | client e topic, oppure il topic nel complesso |
Il controllo delle inondazioni si configura in Impostazioni → Broker e vale per connessione: messaggi al secondo, raffica di messaggi, byte al secondo e raffica di byte. Zero significa nessun limite. Superarli non chiude la connessione: il broker si ferma prima della lettura successiva del socket, il TCP chiude la finestra e il mittente rallenta da solo.
Pubblicazione differita
Una pubblicazione su $delayed/{secondi}/{topic} viene trattenuta dal broker e rilasciata agli iscritti alla scadenza. Il formato è compatibile con EMQX.
mosquitto_pub -t '$delayed/60/sensors/alarm' -m 'tra un minuto'
L'intervallo va da un secondo a 4 294 967 (circa 49 giorni). La conferma arriva subito: il broker risponde della ricezione, non dell'esistenza di iscritti. La risoluzione è di un secondo.
La coda è visibile in Regole → Differiti: topic, payload, conto alla rovescia e annullamento uno per uno o di tutti insieme. I messaggi sopravvivono a un riavvio, e quelli scaduti mentre il broker era fermo escono subito dopo l'avvio.
Bridging tra broker
Il broker può collegarsi come client a un altro broker MQTT e inoltrare i topic nei due sensi. Si configura in Impostazioni → Bridge: l'indirizzo remoto (la porta è facoltativa — 1883 per impostazione predefinita, 8883 con TLS), le credenziali, TLS, la versione del protocollo e le regole.
| Campo della regola | Significato |
|---|---|
| Filtro | quali topic inoltrare |
| Direzione | out, in o both |
| QoS | qualità della consegna attraverso il bridge |
| Prefissi | che cosa anteporre da ciascun lato |
Ci sono protezione dai loop, riconnessione automatica, No Local nella versione 5 (niente eco né duplicati) e stato e contatori nel pannello. Il bridge lavora a livello di broker e non come client separato.
API REST
Il pannello chiama la propria API con un cookie di sessione; i sistemi esterni usano token bearer da Impostazioni → API. Un token viene mostrato una sola volta e ne è conservata solo l'impronta. Un token può essere emesso in sola lettura: allora passano soltanto GET, HEAD e OPTIONS.
curl -H "Authorization: Bearer TOKEN" http://localhost:8567/api/stats
Lo scenario tipico è il tuo sistema che crea un dispositivo e gli concede l'accesso al broker nello stesso passaggio:
# creare
curl -X POST http://localhost:8567/api/users \
-H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
-d '{"username":"sensor-42","password":"s3nsorPass!","enabled":true,
"acl":[{"filter":"$u/#","access":"readwrite","allow":true}]}'
# cambiare la password o i permessi (solo i campi inviati)
curl -X PATCH http://localhost:8567/api/users/sensor-42 \
-H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
-d '{"password":"newPass12345","enabled":false}'
# eliminare
curl -X DELETE http://localhost:8567/api/users/sensor-42 \
-H "Authorization: Bearer TOKEN"
La password non viene mai restituita. Gli errori tornano in una forma unica, {"error":{"code":"…","message":"…"}}: duplicate, reserved, invalid_input, not_found, read_only, unauthorized.
Le modifiche hanno effetto subito: al cambio di una password, dei permessi o a un'eliminazione il broker chiude da sé le connessioni attive di quell'utente, così le nuove regole valgono immediatamente e non alla riconnessione successiva.
È disponibile l'intera API del pannello: statistiche e cronologia, client, sottoscrizioni, messaggi ritenuti, pubblicazione, eventi, utenti, impostazioni, bridge, regole, messaggi differiti, importazione ed esportazione.
Archiviazione e riavvii
Con persistence: true lo stato vive in data/broker.db — SQLite in modalità WAL, Go puro, senza CGO. Che cosa sopravvive a un riavvio:
- I messaggi ritenuti — scritti appena cambiano, non a intervalli. Tutto ciò che ha superato il proprio Message Expiry viene scartato all'avvio.
- Le sessioni persistenti — sottoscrizioni, code offline QoS 1/2 e handshake rimasti a metà. Salvate ogni dieci secondi e allo spegnimento. Le sessioni pulite non vengono salvate: per specifica muoiono con la connessione.
- I messaggi differiti — quelli scaduti mentre il broker era fermo escono subito dopo l'avvio.
All'avvio finisce nel log una riga come state restored retained=128 sessions=7 subscriptions=34 delayed=2. Accanto, in data/, stanno users.json (account, regole e impostazioni del pannello, scritto in modo atomico) e il certificato autofirmato.
Limitazioni note
- Il clustering non è supportato: il broker è progettato come processo unico.
- Il bridge parla QoS 0 e 1; il QoS 2 viene abbassato a 1.
- L'autenticazione avanzata di tipo SCRAM esiste nel protocollo, ma la fonte interna non la offre e risponde
0x8C Bad authentication method. - La limitazione di cadenza vale per connessione e non per utente: un client che apre dieci connessioni ottiene dieci limiti.
- La salute della macchina viene calcolata su Linux e Windows; su macOS e BSD la scheda dice onestamente «nessun dato».