Перейти до вмісту

B8. MQTT і IoT-шлюз

середнійспирається на модуль 15, модуль 17

Модуль 17 пояснює, чому пристрої Інтернету речей розмовляють не запитами HTTP, а через брокер повідомлень: датчик шле значення, хто хоче, той його читає, і жодна сторона не знає адреси іншої. Модуль 15 дає те, без чого брокер на відкритому порту небезпечний: шифрування й перевірку сертифіката. Ця робота збирає обидва модулі в малий, але повний IoT-стенд.

Після цієї роботи ви зможете:

  • підняти брокер Mosquitto з власним конфігом і під’єднати до нього публікаторів і споживача з різних просторів імен;
  • будувати топіки за схемою home/<кімната>/<датчик> і підписуватися на них шаблонами + і #;
  • пояснити й показати retained-повідомлення та last will, відрізнити обрив з’єднання від чесного відключення;
  • побачити в tshark, чим QoS 0, 1 і 2 відрізняються на дроті;
  • закрити брокер від анонімів, розділити права ACL між датчиками й шлюзом;
  • увімкнути TLS на порту 8883 із власним CA й довести, що клієнт без вашого CA не під’єднається.

Так влаштована «розумна оселя» чи телеметрія парку пристроїв: брокер у центрі, сотні датчиків по периметру, і кожен із них має право на власний топік і більше ні на що.

Результат роботи: чотири простори імен, брокер із паролями, ACL і TLS, два датчики й споживач, що працюють одночасно, а також файл ca.crt, який чекер приймає як довірений корінь.

Топологія. Назви просторів імен і адреси фіксовані, назви інтерфейсів ви обираєте самі. Хости лежать в одній підмережі 10.88.0.0/24 (рекомендовано: міст у п’ятому просторі b8-sw, як у B1).

Простір імен Адреса Що в ньому працює
broker 10.88.0.1/24 Mosquitto: порт 8883 (TLS), за бажанням 1883
sensor1 10.88.0.11/24 датчик кімнати kitchen
sensor2 10.88.0.12/24 датчик кімнати bedroom
gw 10.88.0.20/24 споживач, підписаний на home/#

Користувачі, паролі й права. Паролі навчальні й фіксовані, щоб чекер міг увійти від імені кожного.

Користувач Пароль Права
sensor1 s1pass запис у home/kitchen/#, читання нічого
sensor2 s2pass запис у home/bedroom/#, читання нічого
gw gwpass читання home/# і журналу брокера $SYS/broker/log/#, запису нікуди

Топіки.

Топік Хто публікує Зміст
home/kitchen/temp, home/bedroom/temp датчик щосекунди, без retain температура, наприклад 21.4
home/kitchen/status, home/bedroom/status датчик при старті (retained) і брокер за last will (retained) online або offline

Що зробити:

  1. Зібрати топологію й переконатися, що всі бачать 10.88.0.1.
  2. Створити CA і сертифікат брокера з subjectAltName IP:10.88.0.1 (клієнти під’єднуються за адресою, а не за іменем).
  3. Написати конфіг Mosquitto: allow_anonymous false, файл паролів (mosquitto_passwd), файл ACL із таблиці вище, слухач TLS на 10.88.0.1:8883. Запустити брокер зі своїм конфігом у просторі broker.
  4. Запустити в sensor1 і sensor2 датчики: кожен публікує температуру в home/<кімната>/temp і при старті ставить retained online у home/<кімната>/status; з’єднання датчика має last will offline (retained) на цей самий status. Датчики працюють по TLS.
  5. Запустити в gw споживача: підписка на home/# із виводом у файл.
  6. Зробити все, що в розділі «Етапи», і записати, що саме ви побачили.

Чого робити не треба. Міст MQTT між брокерами, MQTT 5 (властивості, спільні підписки), WebSocket-слухач, зберігання сесій на диск, керування багатьма пристроями. Датчики можуть бути скриптом на mosquitto_pub або програмою на Python, вибір за вами.

Обмеження. Кожен процес запускайте зі своїм конфігом, а не через системну службу. Паролі в конфіги зберігайте лише як хеші (mosquitto_passwd), у самих конфігах відкритих паролів не повинно бути.

Готово, коли sudo ./check.sh проходить усі перевірки, а датчики й споживач працюють на момент запуску чекера.

  • Прочитайте в модулі 17 розділ про MQTT (брокер, топіки, QoS, retained, last will), а в модулі 15 про ланцюжок довіри й subjectAltName.
  • Потрібні права root і пакети mosquitto, mosquitto-clients, openssl, tshark. Їх ставить setup/provision.sh; вручну: sudo apt-get install mosquitto mosquitto-clients openssl tshark. Службу mosquitto із пакета краще зупинити й не вмикати: ви запускаєте свій екземпляр у просторі broker.
  • Якщо ви робили B5, CA й сертифікат випускаються так само, але тут у subjectAltName пишуть IP-адресу: IP:10.88.0.1.
  • Робочий каталог на ваш вибір, наприклад ~/b8, із каталогом pki/, у якому лежить ca.crt. Чекер запускається з нього.
  • Файли самого брокера (конфіг, паролі, ACL, його сертифікат і ключ) кладіть у /etc/mosquitto/, а не в робочий каталог. Чому — у рамці нижче.
  1. Топологія й перший брокер без захисту.

    Збудуйте чотири простори імен і міст. Для початку напишіть мінімальний конфіг: слухач на 10.88.0.1:1883, allow_anonymous true. Запустіть:

    Terminal window
    sudo ip netns exec broker mosquitto -c /etc/mosquitto/b8.conf -d

    У Mosquitto 2.x без явного allow_anonymous true анонімів не буде, а без явного listener брокер слухає лише localhost: обидві пастки видно одразу.

    Перевірте: в gw запустіть mosquitto_sub -h 10.88.0.1 -t 'home/#' -v, у sensor1: mosquitto_pub -h 10.88.0.1 -t home/kitchen/temp -m 21.5. Споживач друкує home/kitchen/temp 21.5.

  2. Топіки й шаблони.

    Підпишіться на home/+/temp (+ замінює рівно один рівень) і на home/# (# замінює решту дерева, зокрема й нуль рівнів). Опублікуйте в home/kitchen/temp, home/kitchen/light/ceiling і home/bedroom/temp. Запишіть, який шаблон які повідомлення бачить і чому home/+/temp не бачить home/kitchen/light/ceiling.

    Підпишіться на $SYS/# і подивіться, що про себе розповідає брокер. Зверніть увагу: шаблон # службових топіків $SYS не охоплює.

  3. Retained.

    Опублікуйте з прапорцем -r значення в home/kitchen/status. Потім запустіть нового підписника: він отримує повідомлення одразу, хоча в момент публікації його не було, і бачить прапорець retain (-F '%r %t %p'). Очистіть retained порожнім повідомленням (mosquitto_pub -r -n -t ...).

    Поясніть, чому temp не слід робити retained, а status слід: що побачить споживач, який підключився об 11:00, і чи варто йому вірити в температуру, виміряну о 08:00.

  4. Last will.

    Запустіть довге з’єднання датчика з last will:

    Terminal window
    sleep 600 | mosquitto_pub -h 10.88.0.1 -t home/kitchen/temp -l \
    --will-topic home/kitchen/status --will-payload offline --will-retain --will-qos 1

    У gw підпишіться на home/kitchen/status. Вбийте датчик сигналом kill -9 на процесі mosquitto_pub: брокер публікує offline від імені датчика. Зробіть те саме через Ctrl+C (чесне відключення): повідомлення не з’являється. Поясніть, чому так і як брокер дізнається, що датчик помер (обрив TCP чи тайм-аут keepalive, а на практиці це різні ситуації).

  5. QoS 0, 1, 2 на дроті.

    Запустіть у broker tshark -i any -Y mqtt (порт 1883, без TLS), опублікуйте по одному повідомленню з -q 0, -q 1, -q 2 і випишіть послідовність пакетів для кожного: у QoS 1 PUBLISH і PUBACK, у QoS 2 чотири повідомлення (PUBLISH, PUBREC, PUBREL, PUBCOMP).

    Підпишіть споживача з -q 0, опублікуйте з -q 2 і подивіться на %q у виводі: споживач отримує менший із двох рівнів.

    Яка ціна гарантії (кількість пакетів, стан на брокері)? І від чого вона насправді захищає, якщо MQTT іде поверх TCP, а TCP сам перепосилає втрачені сегменти? Сформулюйте гіпотезу й запишіть її у звіт: її перевіряє експеримент у розділі «Далі, якщо цікаво».

  6. Автентифікація й ACL.

    Створіть файл паролів: sudo mosquitto_passwd -c /etc/mosquitto/b8.passwd sensor1, далі без -c для sensor2 і gw (паролі з таблиці) і sudo chmod 600 /etc/mosquitto/b8.passwd. Файл ACL (/etc/mosquitto/b8.acl) за таблицею прав; формат:

    user sensor1
    topic write home/kitchen/#

    У конфігу: allow_anonymous false, password_file, acl_file. Перезапустіть брокер.

    Перевірте: анонімна публікація відхиляється (Connection Refused: not authorised), неправильний пароль так само. Тепер від sensor1 опублікуйте у home/bedroom/temp: mosquitto_pub не повідомить про помилку (протокол 3.1.1 мовчки відкидає повідомлення), а споживач його не побачить. Повторіть із -V 5: клієнт попередить Publish 1 failed: Not authorized. Запишіть, чому відмову за ACL не видно публікатору й чому це небезпечно для налагодження.

  7. TLS з власним CA.

    Випустіть CA й сертифікат брокера (CA:TRUE для CA, CA:FALSE і subjectAltName=IP:10.88.0.1 для брокера). До конфігу додайте слухач:

    listener 8883 10.88.0.1
    cafile /etc/mosquitto/ca_certificates/b8/ca.crt
    certfile /etc/mosquitto/certs/b8/broker.crt
    keyfile /etc/mosquitto/certs/b8/broker.key

    Файли туди скопіюйте з pki/, ключ — з правами 600 (sudo install -m 600 pki/broker.key /etc/mosquitto/certs/b8/).

    Клієнти беруть --cafile pki/ca.crt. Перевірте: з CA працює; з --capath /etc/ssl/certs (тобто лише системні CA) не працює; порт 8883 без TLS-параметрів не працює. Подивіться в tshark, що на 8883 видно лише TLS, а на 1883 пароль у відкритому тексті.

  8. Датчики й споживач.

    Напишіть датчики за специфікацією з «Завдання» (по TLS, з паролем, retained online, last will на status, температура щосекунди) і запустіть їх у sensor1 та sensor2. Споживач у gw підписується на home/# і пише у файл. Зробіть kill -9 одному з датчиків і покажіть, що home/<кімната>/status став offline; потім запустіть датчик знову.

З каталогу, де лежить pki/ca.crt, за запущених датчиків:

Terminal window
sudo /шлях/до/labs/b8-mqtt-iot/check.sh
# або з явним шляхом до CA:
sudo ./check.sh ~/b8/pki/ca.crt

Чекер нічого не будує й не зупиняє. Він під’єднується до брокера від імені sensor1, sensor2 і gw, користуючись унікальними топіками з випадковим суфіксом, і нічого за собою не лишає. Перевіряється:

  1. Топологія: адреси на місці, усі бачать 10.88.0.1.
  2. Порти: брокер слухає 8883; якщо відкритий і 1883, перевірки нижче виконуються й для нього.
  3. Автентифікація: з правильним паролем працює; публікація й підписка без імені й без пароля не працюють; неправильний пароль відхиляється.
  4. ACL: кожен датчик пише у свою кімнату й не пише в чужу; gw не пише; sensor1 не читає чужої кімнати.
  5. Retained, last will, QoS: retained-повідомлення приходить новому підписнику з прапорцем retain і стирається порожнім; з’єднання, що обірвалося без DISCONNECT, спричиняє публікацію will; QoS 0, 1 і 2 доходять із своїм рівнем; home/<кімната>/status має retained online, а home/<кімната>/temp отримує живі (не retained) вимірювання.
  6. TLS: сертифікат брокера перевіряється проти вашого CA за 10.88.0.1; клієнт лише з системним сховищем довіри й клієнт без TLS не з’єднуються; ca.crt справді CA і чинний, сертифікат брокера чинний і має IP у SAN.

Чекер не вимагає закрити порт 1883: якщо він відкритий, анонімний доступ на ньому перевіряється теж.

Що треба вміти підтвердити

Section titled “Що треба вміти підтвердити”
Твердження Чим доводиться
+ і # вибирають різні піддерева mosquitto_sub -v з двома шаблонами
Retained видно підписнику, якого не було -F '%r' дає 1
Will спрацьовує лише при обриві kill -9 проти Ctrl+C
QoS 2 коштує чотирьох пакетів tshark -Y mqtt
Доставлений рівень — мінімум із двох %q у підписника з меншим QoS
Відмову за ACL не видно публікатору (3.1.1) повідомлення зникає без помилки
Клієнт без вашого CA не з’єднується --capath /etc/ssl/certs дає відмову
Пароль на 1883 видно, на 8883 ні tshark на обох портах

Брокер не стартує з TLS: Unable to load server key file. Mosquitto з пакета після запуску знижує привілеї до користувача mosquitto, який не читає ваш ключ. Зробіть ключ доступним цьому користувачу або задайте user root у конфігу (для стенду припустимо).

Датчики під’єднуються, а нічого не публікують. ACL не має правила topic write для користувача або шаблон не збігається з топіком. Відмови мовчки відкидаються: дивіться в журнал брокера з log_type all (mosquitto_sub від gw на '$SYS/broker/log/#') і в mosquitto_pub -V 5.

Connection Refused: not authorised при правильному паролі. Файл паролів створено без -c на порожньому шляху, або брокер не перечитав його після зміни. Перезапустіть брокер.

Protocol error чи certificate verify failed при TLS. Клієнт не довіряє сертифікату: у subjectAltName немає адреси, за якою ви під’єднуєтеся (потрібне саме IP:10.88.0.1, а не DNS), або ви передали не той ca.crt. Перевірте openssl s_client -connect 10.88.0.1:8883 -CAfile pki/ca.crt -verify_ip 10.88.0.1.

Споживач бачить старі значення температури. Датчик публікує temp з прапорцем retain: споживач, що підключився пізніше, отримує значення вчорашнього дня. Retained призначений для стану, а не для потоку вимірювань.

Will не приходить. Датчик відключався чесно (DISCONNECT), а не обривом; або will заданий на топік, у який користувач не має права писати.

Датчик вмирає тихо після перезапуску брокера. mosquitto_pub -l не перепідключається; для стенду перезапускайте датчики вручну або напишіть цикл із повторною спробою. Справжній клієнт (бібліотека paho чи власна програма) мусить повторно підключатися сам.

Напишіть датчики й шлюз на Python із бібліотекою paho-mqtt: виконайте перепідключення та on_connect, що заново оформлює підписки й retained online. Додайте шлюзу правило «якщо temp не оновлювався 5 секунд або status став offline, вивести тривогу». Замініть паролі клієнтськими сертифікатами (require_certificate true, use_identity_as_username true).

QoS під втратами. Поставте на порти моста tc netem loss 30% (на ядрі з sch_netem, див. B6) і для кожного QoS опублікуйте 50 повідомлень, поки gw підписаний із тим самим рівнем. Порахуйте, скільки дійшло, скільки дублікатів і скільки часу це зайняло. Підписку оформіть до ввімкнення втрат: інакше ви поміряєте те, як довго підписник під’єднувався. Потім другий дослід: gw підписується зі сталою сесією (-c -i gw1) і виходить, датчик публікує 20 повідомлень, gw повертається з тим самим ідентифікатором.

Очікуйте, що перший дослід розчарує: втрати пакетів на каналі перекриває TCP, і всі три рівні доставлять усе, змінюється лише час (і дуже сильно між прогонами, бо тайм-аут TCP подвоюється з кожною невдачею). Різниця між рівнями з’являється тоді, коли рветься з’єднання чи підписника немає: для офлайн-сесії брокер зберігає повідомлення QoS 1 і 2, а QoS 0 відкидає. Поясніть у звіті, чому саме так і в якому з дослідів знадобилася б QoS 2, а не 1.