B8. MQTT і IoT-шлюз
Модуль 17 пояснює, чому пристрої Інтернету речей розмовляють не запитами HTTP, а через брокер повідомлень: датчик шле значення, хто хоче, той його читає, і жодна сторона не знає адреси іншої. Модуль 15 дає те, без чого брокер на відкритому порту небезпечний: шифрування й перевірку сертифіката. Ця робота збирає обидва модулі в малий, але повний IoT-стенд.
Після цієї роботи ви зможете:
- підняти брокер Mosquitto з власним конфігом і під’єднати до нього публікаторів і споживача з різних просторів імен;
- будувати топіки за схемою
home/<кімната>/<датчик>і підписуватися на них шаблонами+і#; - пояснити й показати retained-повідомлення та last will, відрізнити обрив з’єднання від чесного відключення;
- побачити в
tshark, чим QoS 0, 1 і 2 відрізняються на дроті; - закрити брокер від анонімів, розділити права ACL між датчиками й шлюзом;
- увімкнути TLS на порту 8883 із власним CA й довести, що клієнт без вашого CA не під’єднається.
Так влаштована «розумна оселя» чи телеметрія парку пристроїв: брокер у центрі, сотні датчиків по периметру, і кожен із них має право на власний топік і більше ні на що.
Завдання
Section titled “Завдання”Результат роботи: чотири простори імен, брокер із паролями, 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 |
Що зробити:
- Зібрати топологію й переконатися, що всі бачать
10.88.0.1. - Створити CA і сертифікат брокера з
subjectAltNameIP:10.88.0.1(клієнти під’єднуються за адресою, а не за іменем). - Написати конфіг Mosquitto:
allow_anonymous false, файл паролів (mosquitto_passwd), файл ACL із таблиці вище, слухач TLS на10.88.0.1:8883. Запустити брокер зі своїм конфігом у просторіbroker. - Запустити в
sensor1іsensor2датчики: кожен публікує температуру вhome/<кімната>/tempі при старті ставить retainedonlineуhome/<кімната>/status; з’єднання датчика має last willoffline(retained) на цей самийstatus. Датчики працюють по TLS. - Запустити в
gwспоживача: підписка наhome/#із виводом у файл. - Зробити все, що в розділі «Етапи», і записати, що саме ви побачили.
Чого робити не треба. Міст MQTT між брокерами, MQTT 5 (властивості,
спільні підписки), WebSocket-слухач, зберігання сесій на диск, керування
багатьма пристроями. Датчики можуть бути скриптом на mosquitto_pub
або програмою на Python, вибір за вами.
Обмеження. Кожен процес запускайте зі своїм конфігом,
а не через системну службу. Паролі в конфіги зберігайте лише як хеші
(mosquitto_passwd), у самих конфігах відкритих паролів не повинно бути.
Готово, коли sudo ./check.sh проходить усі перевірки, а датчики й
споживач працюють на момент запуску чекера.
Перед початком
Section titled “Перед початком”- Прочитайте в модулі 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/, а не в робочий каталог. Чому — у рамці нижче.
-
Топологія й перший брокер без захисту.
Збудуйте чотири простори імен і міст. Для початку напишіть мінімальний конфіг: слухач на
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. -
Топіки й шаблони.
Підпишіться на
home/+/temp(+замінює рівно один рівень) і наhome/#(#замінює решту дерева, зокрема й нуль рівнів). Опублікуйте вhome/kitchen/temp,home/kitchen/light/ceilingіhome/bedroom/temp. Запишіть, який шаблон які повідомлення бачить і чомуhome/+/tempне бачитьhome/kitchen/light/ceiling.Підпишіться на
$SYS/#і подивіться, що про себе розповідає брокер. Зверніть увагу: шаблон#службових топіків$SYSне охоплює. -
Retained.
Опублікуйте з прапорцем
-rзначення вhome/kitchen/status. Потім запустіть нового підписника: він отримує повідомлення одразу, хоча в момент публікації його не було, і бачить прапорець retain (-F '%r %t %p'). Очистіть retained порожнім повідомленням (mosquitto_pub -r -n -t ...).Поясніть, чому
tempне слід робити retained, аstatusслід: що побачить споживач, який підключився об 11:00, і чи варто йому вірити в температуру, виміряну о 08:00. -
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, а на практиці це різні ситуації). -
QoS 0, 1, 2 на дроті.
Запустіть у
brokertshark -i any -Y mqtt(порт 1883, без TLS), опублікуйте по одному повідомленню з-q 0,-q 1,-q 2і випишіть послідовність пакетів для кожного: у QoS 1PUBLISHіPUBACK, у QoS 2 чотири повідомлення (PUBLISH,PUBREC,PUBREL,PUBCOMP).Підпишіть споживача з
-q 0, опублікуйте з-q 2і подивіться на%qу виводі: споживач отримує менший із двох рівнів.Яка ціна гарантії (кількість пакетів, стан на брокері)? І від чого вона насправді захищає, якщо MQTT іде поверх TCP, а TCP сам перепосилає втрачені сегменти? Сформулюйте гіпотезу й запишіть її у звіт: її перевіряє експеримент у розділі «Далі, якщо цікаво».
-
Автентифікація й 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 sensor1topic 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 не видно публікатору й чому це небезпечно для налагодження. -
TLS з власним CA.
Випустіть CA й сертифікат брокера (
CA:TRUEдля CA,CA:FALSEіsubjectAltName=IP:10.88.0.1для брокера). До конфігу додайте слухач:listener 8883 10.88.0.1cafile /etc/mosquitto/ca_certificates/b8/ca.crtcertfile /etc/mosquitto/certs/b8/broker.crtkeyfile /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 пароль у відкритому тексті. -
Датчики й споживач.
Напишіть датчики за специфікацією з «Завдання» (по TLS, з паролем, retained
online, last will наstatus, температура щосекунди) і запустіть їх уsensor1таsensor2. Споживач уgwпідписується наhome/#і пише у файл. Зробітьkill -9одному з датчиків і покажіть, щоhome/<кімната>/statusставoffline; потім запустіть датчик знову.
Перевірка
Section titled “Перевірка”З каталогу, де лежить pki/ca.crt, за запущених датчиків:
sudo /шлях/до/labs/b8-mqtt-iot/check.sh# або з явним шляхом до CA:sudo ./check.sh ~/b8/pki/ca.crtЧекер нічого не будує й не зупиняє. Він під’єднується до брокера
від імені sensor1, sensor2 і gw, користуючись унікальними
топіками з випадковим суфіксом, і нічого за собою не лишає. Перевіряється:
- Топологія: адреси на місці, усі бачать
10.88.0.1. - Порти: брокер слухає 8883; якщо відкритий і 1883, перевірки нижче виконуються й для нього.
- Автентифікація: з правильним паролем працює; публікація й підписка без імені й без пароля не працюють; неправильний пароль відхиляється.
- ACL: кожен датчик пише у свою кімнату й не пише в чужу;
gwне пише;sensor1не читає чужої кімнати. - Retained, last will, QoS: retained-повідомлення приходить новому
підписнику з прапорцем retain і стирається порожнім; з’єднання, що
обірвалося без
DISCONNECT, спричиняє публікацію will; QoS 0, 1 і 2 доходять із своїм рівнем;home/<кімната>/statusмає retainedonline, аhome/<кімната>/tempотримує живі (не retained) вимірювання. - 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 на обох портах |
Часті помилки
Section titled “Часті помилки”Брокер не стартує з 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 чи власна програма) мусить повторно підключатися сам.
Далі, якщо цікаво
Section titled “Далі, якщо цікаво”Напишіть датчики й шлюз на 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.