Документация

Всё, что нужно для запуска и настройки брокера. Встроенная справка в панели повторяет эти разделы и переведена на семь языков.

Быстрый старт

После установки брокер слушает три порта: :1883 — MQTT поверх TCP, :8883 — MQTT поверх TLS, :8080 — веб-панель и MQTT over WebSocket по пути /mqtt.

Откройте http://localhost:8080 и войдите администратором. Логин по умолчанию — admin / admin; смените пароль в разделе «Профиль» сразу после первого входа.

Администратор панели — это не MQTT-логин. Устройства подключаются под отдельными учётками, которые заводятся в разделе «Пользователи».

Заведите первого пользователя: имя, пароль от восьми символов и правило ACL. Правило $u/# даёт устройству полный доступ к собственной ветке — в панели для этого есть кнопка «Своя ветка».

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

Установка на Linux

Рекомендуемый способ — APT-репозиторий: обновления приходят вместе с системными.

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

Пакет ставит бинарник в /opt/elxmqttbroker, заводит системного пользователя elxmqtt без права входа, кладёт данные и config.json с правами 0600 рядом и включает службу elxmqttbroker в автозапуск.

systemctl status elxmqttbroker      # состояние
sudo systemctl restart elxmqttbroker # перезапуск
journalctl -u elxmqttbroker -f      # логи

Юнит закрыт ProtectSystem=strict: писать разрешено только в каталог брокера. Панель по умолчанию слушает 127.0.0.1:8080 и публикуется наружу через nginx, а MQTT-порты 1883 и 8883 открыты напрямую — это отдельный протокол, проксировать его не нужно.

В конфиге nginx обязательны заголовки Upgrade и Connection и большой proxy_read_timeout: /ws/live и /mqtt — долгоживущие WebSocket-соединения.

Установка на Windows

Обычный путь — установщик elxmqttbroker-setup.exe: он кладёт программу в Program Files, данные в C:\ProgramData\ELX MQTT Broker, по желанию регистрирует службу ELXMQTTBroker с автозапуском и открывает порты 1883, 8883 и 8080 в брандмауэре. Установщик говорит по-русски и по-английски и требует прав администратора.

Настройки лежат в ProgramData, а не рядом с программой: брокер переписывает config.json сам из панели, а службе нечего писать в Program Files. При удалении эта папка остаётся — в ней пользователи, права и сообщения.

Переносимый вариант — один elxmqttbroker.exe: положите его в отдельную папку и запустите, рядом появятся config.json, каталог data и самоподписанный TLS-сертификат.

elxmqttbroker.exe                    # запуск с настройками по умолчанию
elxmqttbroker.exe -web :9000         # панель на другом порту
elxmqttbroker.exe -version           # версия и штамп сборки

Переносимой копией можно управлять как службой и вручную — теми же командами, что использует установщик:

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

Конфигурация

config.json — единственный файл, который читает брокер. Он создаётся при первом запуске и содержит только то, что нужно знать до старта: адреса, TLS и учётку администратора.

{
  "dataDir": "data",
  "auth": { "username": "admin", "password": "admin" },
  "mqtt": { "tcp": ":1883", "tls": ":8883" },
  "web": { "addr": ":8080", "tlsAddr": "" },
  "tls": { "cert": "", "key": "", "selfSigned": true,
            "hosts": ["localhost", "127.0.0.1", "::1"] },
  "logLevel": "info",
  "logFormat": "text",
  "persistence": true
}

Открытый password при первом старте превращается в passwordHash и вычищается из файла. Всё остальное — лимиты протокола, SMTP, мосты, правила, API-токены, MQTT-пользователи — настраивается в панели и живёт в data/users.json; перезапуск для этого не нужен.

Флаги командной строки перекрывают файл:

ФлагЧто задаёт
-configпуть к config.json
-dataкаталог данных
-mqttадрес MQTT поверх TCP
-mqtt-tlsадрес MQTT поверх TLS
-webадрес веб-панели
-logуровень логирования
-versionнапечатать версию и выйти
config.json содержит хеш пароля администратора, пароль SMTP и учётные данные внешнего источника аутентификации. Права на файл должны быть 0600.

Пользователи и права

Контуров два, и путать их не стоит. Администратор — один, задаётся в config.json и управляет только панелью: ролей нет, регистрации нет, вошли — значит полный доступ. MQTT-устройства — отдельный список учёток: имя от 1 до 64 символов, пароль, флаг «включён» и права по темам.

ACL-правило состоит из фильтра темы, вида доступа и решения:

ПолеЗначения
Фильтробычный MQTT-фильтр: sensors/+/temp, dev/#, $u/#
Доступread, write или readwrite
Решениеразрешить или запретить

Правила проверяются сверху вниз, срабатывает первое подходящее, и всё deny-by-default: без явного разрешения доступа нет. Порядок — часть настройки, поэтому правила перетаскиваются мышью или двигаются стрелками с клавиатуры.

В фильтре разворачивается $u — имя подключившегося клиента. Одно правило $u/# замыкает каждое устройство в собственной ветке и избавляет от правила на каждое имя.

Пароли хранятся как PBKDF2-HMAC-SHA256, 210 000 итераций, со случайной солью. Токены сессий и API хранятся только хешами.

Аутентификация MQTT

Источник учётных записей выбирается в «Настройки → Аутентификация»; активен всегда ровно один. Смена происходит на лету: новый источник сначала проверяется кнопкой «Проверить», и только если он отвечает, брокер переключается и отключает клиентов, чтобы каждый прошёл проверку заново. Если источник недоступен, не меняется ничего.

ИсточникКогда подходит
internalучётки ведутся в панели — по умолчанию
mysql / mariadbустройства уже живут в базе вашей системы
sqliteто же самое, но база — файл
csvпростой список, файлы перечитываются при изменении
jwtустройство предъявляет подписанный токен вместо пароля
httpрешение принимает ваш сервис на каждое подключение

Схема для MySQL и MariaDB — в стиле 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} и ${clientid} подставляются параметрами запроса, а не склейкой строк — инъекция невозможна. passwordHash принимает plain, sha256, sha512, bcrypt и pbkdf2.

JWT кладётся в поле пароля или логина. Поддерживаются HS256/384/512 и RS256/384/512, проверяются подпись, exp, nbf, при желании iss, aud и любые свои claim'ы. Права берутся из claim acl в любом из двух форматов EMQX, а is_superuser снимает проверку целиком.

Алгоритм подписи берётся только из конфигурации, а не из заголовка токена: подделка с "alg": "none" не проходит.

HTTP-источник получает {username, password, clientid, peerhost} и отвечает {"result": "allow", "acl": […]}. Пустой 200 тоже считается разрешением; deny, 401, 403, таймаут и любая ошибка — отказ: недоступный сервис авторизации не должен превращаться в открытую дверь. Ответы кэшируются на cacheSeconds.

Правила: перезапись, автоподписка, лимиты

Перезапись тем подменяет имя темы на лету. Правило — это MQTT-фильтр для дешёвого отбора, регулярное выражение и шаблон новой темы с группами $1…$9:

КогдаФильтрВыражениеНовая тема
Всегдаraw/#^raw/(.+)$cooked/$1

Порядок как в EMQX: сначала перезапись, потом проверка ACL — права проверяются на той теме, под которой сообщение реально поедет. UNSUBSCRIBE проходит ту же перезапись, что и SUBSCRIBE, иначе клиент не смог бы отписаться.

Автоподписка оформляет подписки сразу после подключения — SUBSCRIBE от клиента не нужен. Правило %c/# или %u/# даёт каждому клиенту свою ветку без ручной настройки.

Ограничения по темам нужны, когда клиент ведёт себя нормально, а слишком часто идёт одна конкретная тема:

ПолеЧто задаёт
Шаблон темыобычный MQTT-фильтр: sensors/+/temp
Действиепрореживать до нормы или игнорировать полностью
Сообщений в секундунорма; дробные значения допустимы (0.2 — одно в пять секунд)
Считатьпо клиенту и теме или по теме целиком
Прореживание сохраняет последнее значение: пришедшее раньше срока придерживается, следующее его вытесняет, на ближайшем такте уходит самое свежее. Публикующий всегда получает обычное подтверждение — иначе он счёл бы доставку неудавшейся и прислал всё заново.

Флуд-контроль настраивается в «Настройки → Брокер» и действует на каждое подключение: сообщений в секунду, всплеск сообщений, байт в секунду, всплеск байт. Ноль — ограничения нет. Превышение не рвёт соединение: брокер делает паузу перед следующим чтением из сокета, TCP закрывает окно, и отправитель замедляется сам.

Отложенная публикация

Публикация в тему $delayed/{секунды}/{тема} придерживается брокером и уходит подписчикам, когда срок истечёт. Формат совместим с EMQX.

mosquitto_pub -t '$delayed/60/sensors/alarm' -m 'через минуту'

Интервал — от одной секунды до 4 294 967 (около 49 суток). Подтверждение приходит сразу: брокер отвечает за приём, а не за наличие подписчиков. Точность — одна секунда.

Очередь видна в «Правила → Отложенные»: тема, payload, обратный отсчёт, отмена поштучно или целиком. Сообщения переживают перезапуск, а те, чей срок истёк, пока брокер лежал, уходят сразу при старте.

Мост между брокерами

Брокер умеет подключаться как клиент к другому MQTT-брокеру и пересылать темы в обе стороны. Настраивается в «Настройки → Мосты»: адрес удалённого брокера (порт необязателен — по умолчанию 1883, а по TLS 8883), логин, TLS, версия протокола и правила.

Поле правилаЗначение
Фильтркакие темы пересылать
Направлениеout, in или both
QoSкачество доставки через мост
Префиксычто добавить к теме на той и на этой стороне

Есть защита от петель, авто-переподключение, No Local на пятой версии (нет эха и дублей), статус и счётчики в панели. Мост работает на уровне брокера, а не отдельного клиента.

Мост говорит QoS 0 и 1; QoS 2 понижается до 1, доставка QoS 1 — best-effort.

REST API

Панель ходит в собственный API по cookie сессии; для внешних систем есть bearer-токены — «Настройки → API». Токен показывается один раз, в базе лежит только его хеш. Токен можно выдать только на чтение — тогда проходят лишь GET, HEAD и OPTIONS.

curl -H "Authorization: Bearer ТОКЕН" http://localhost:8080/api/stats

Типовой сценарий — ваша система заводит устройство и сразу выдаёт ему доступ к брокеру:

# создать
curl -X POST http://localhost:8080/api/users \
  -H "Authorization: Bearer ТОКЕН" -H "Content-Type: application/json" \
  -d '{"username":"sensor-42","password":"s3nsorPass!","enabled":true,
       "acl":[{"filter":"$u/#","access":"readwrite","allow":true}]}'

# изменить пароль или права (только переданные поля)
curl -X PATCH http://localhost:8080/api/users/sensor-42 \
  -H "Authorization: Bearer ТОКЕН" -H "Content-Type: application/json" \
  -d '{"password":"newPass12345","enabled":false}'

# удалить
curl -X DELETE http://localhost:8080/api/users/sensor-42 \
  -H "Authorization: Bearer ТОКЕН"

Пароль обратно не возвращается никогда. Ошибки приходят в едином виде {"error":{"code":"…","message":"…"}}: duplicate, reserved, invalid_input, not_found, read_only, unauthorized.

Изменения применяются мгновенно: при смене пароля, прав или удалении брокер сам разрывает живые соединения этого пользователя, чтобы новые правила действовали сразу, а не с очередного переподключения.

Этот API управляет встроенным списком пользователей. Если выбран внешний источник, учётки живут там и заводить их нужно в самом источнике.

Доступен весь API панели: статистика и история, клиенты, подписки, retain, публикация, события, пользователи, настройки, мосты, правила, отложенные сообщения, импорт и экспорт.

Хранилище и перезапуск

При persistence: true состояние лежит в data/broker.db — SQLite в режиме WAL на чистом Go, без CGO. Перезапуск переживают:

  • Retain-сообщения — пишутся сразу при каждом изменении, не по таймеру. При старте просроченные по Message Expiry отбрасываются.
  • Постоянные сессии — подписки, оффлайн-очереди QoS 1/2, незавершённые рукопожатия. Снимок раз в десять секунд и при остановке. Чистые сессии не сохраняются: по спецификации они умирают вместе с соединением.
  • Отложенные сообщения — те, чей срок наступил, пока брокер был выключен, уходят сразу после старта.

При старте в лог идёт строка вида state restored retained=128 sessions=7 subscriptions=34 delayed=2. Рядом в data/ лежат users.json (учётки, правила и настройки панели, запись атомарная) и самоподписанный сертификат.

Известные ограничения

  • Кластеризация не поддерживается: брокер рассчитан на один процесс.
  • Мост говорит QoS 0 и 1, QoS 2 понижается до 1.
  • Расширенная аутентификация вроде SCRAM в протоколе есть, но встроенный источник её не предлагает и отвечает 0x8C Bad authentication method.
  • Ограничение частоты действует на подключение, а не на пользователя: клиент, открывший десять соединений, получает десять лимитов.
  • Состояние машины считается на Linux и Windows; на macOS и BSD карточка честно пишет «нет данных».