4. týždeň

Pokročile témy MQTT

Stav klienta, ukladanie správ, bezpečnosť

Ciele

  1. Naučiť sa správne používať identifikátor klienta MQTT
  2. Ukladanie stavu klienta
  3. Úprava nastavení pre nestabilné spojenie
  4. Bezpečnosť v MQTT

Postup

Krok 1: Pokračujeme s MQTT

Aktuálne budeme pokračovať s kódom, ktorý ste mali spraviť v priebehu posledného stretnutia.

Úloha 1.1

Pripravte si svoj kód z posledného stretnutia alebo si môžete stiahnuť moje vzorové riešenie a vyskúšajte či funguje správne.

Krok 2: Zaručené perzistentné doručenie správ

Client id

Pri pripájaní na MQTT server je potrebné zadávať takzvaný identifikátor klienta (client id), ktorý nám umožňuje jednoznačne označiť každého pripojeného klienta.

Na jeden MQTT broker sa nemôžu naraz pripojiť dvaja klienti s rovnakým client id, lebo starší z nich by bol automatický odpojený. Táto vlastnosť je označovaná ako client take over a je štandardná pre všetky brokery.

Tento jednoznačný identifikátor klienta slúži aj pre účely doručovania správ pri nespoľahlivom a padajúcom spojení. Broker má štandardne za úlohu si uchovať všetky správy, ktoré prišli s QoS 1 alebo 2 pre všetkých klientov, ktorí boli prihlásení na odber správ z daného topicu taktiež pomocou QoS 1 alebo 2.

Dôležité je pri opätovnom pripojení klienta s rovnakým client id použiť vlastnosť cleanSession nastavenú na false ak chceme prijať všetký správy, ktoré ešte nám neboli doručené. Broker si v takomto prípade pamätá aj všetký naše existujúce prihlásenia na odber (subscriptions), teda už nie je nutné sa znova prihlásovať na odber.

Úloha 2.1

Pracujte vo dvojiciach a vyskúšajte si príjem správ o polohe kurzora aj v čase keď nemáte stránku zapnutú. Pre správnu funkcionalitu nezabudnite sa

  • prihlásiť na odber správ zo správneho topicu s QoS 1 alebo 2,
  • posielať správy s QoS 1 alebo 2,
  • nastaviť si nemenný client id
  • a správnu hodnotu vlastnosti cleanSession (pozri dokumentáciu).

Prv sa pripojte aby ste brokeru dali vedieť, že chcete odoberať správy z daného topicu. Potom sa odpojte a Váš spolužiak nech generuje správy pohybom kurzora. Následne sa pripojte a zistite, či tieto správy získate. Mali by sa vykresliť. Samozrejme rýchlosť odozvy pri QoS 1 alebo 2 bude nižšia ako pri teste na poslednom stretnutí.

Poznámka

Verejný broker broker.hivemq.com má nastavené aby si pamätal maximalne 1000 správ pre jeden topic a jedného klienta. Všetky ďalšie prijaté správy si server už nezapamätá, teda zapamätá si len prvých 1000 správ.

Štandardné nastavenie opensource brokera Mosquitto, ktorý používame aj pre OpenLab, je nastavené na zapamätanie si len 100 správ pre každý topic a každého klienta. Tento parameter je možné samozrejme zmeniť ak by ste si nasadili vlastný broker Mosquitto.

Používanie fixného client id je v niektorých prípadoch veľmi uľahčujúce. Ale ak nepotrebujete mať k dispozícii aj správy, ktoré boli poslané v čase, keď Váš klient nebol pripojený, prípadne používate len správy s QoS 0, tak odporúčam stále používať cleanSession nastavené na true (čo je aj prednastavená hodnota) a taktiež aj client id nastavený na dynamický generovanú hodnotu.

Krok 3: Info o stave klienta

Doteraz sme si vyskúšali použitie client id za účelom aby nás broker správne identifikoval pri novom pripojení a obnovil nám všetky nastavenia.

Ako ale zabezpečiť ak chceme informáciu o našom neočakávanom odpojení poslať aj ostatným klientom? Alebo len jednoducho chceme vedieť, ktorý klient je online a ktorý nie.

Last Will

Pre účelý funkcionality poslania správy v prípade nečakáneho odpojenia na vopred definovaný topic a s vopred definovaným obsahom sa v protokole MQTT nachádza funkcionalita Last Will and Testament (v skratke Last Will, teda záveť).

Pri pripájaní je možné zadefinovať viacero vlastnosti a jedná z nich je ja Last Will Message. Konkrétne v JavaScript implementácii Paho MQTT klienta je táto vlastnosť nazvaná willMessage (pozri dokumentáciu).

My si ideme teraz zobraziť stav každého klienta pre kurzory na našej web obrazovke. Vyskúšame pri tom použitie:

  • retained typu správy
  • Last Will správy

Úloha 3.1

Zabezpeč aby tvoj klient stále po pripojení odoslal retained správu o pripojení s textom online a QoS 1 do topicu kpi/test/tvojepriezvisko/status (topic si uprav podľa svojho priezviska).

Úloha 3.2

Nastav aby tvoj klient poslal brokeru pri pripojení Last Will správu, ktorá pôjde do topicu kpi/test/tvojepriezvisko/status (topic si uprav podľa svojho priezviska), bude retained s QoS 1 a jej text bude offline (pozri v dokumentácii willMessage).

Správnu funkcionalitu si vyskúšaj so svojim spolužiakom tak, že sa prihlásiš na odber jeho topicu so statusom. Alebo sa budeš sám tváriť ako dvaja rozdielni klienti v dvoch taboch prehliadača.

Upozornenie

V MQTT kliente v Javascripte existuje len jedna metóda, ktorá spracuváva všetký prijaté správy, musíš preto zabezpečiť alternatívne spracovávanie správy zo ../status topicu, najlepšie cez if vetvu pričom sa budeš pýtať na message.destinationName.

Taktiež si daj pozor, lebo ak v metóde pre spracovanie správy dôjde k chybe v tvojom kóde (Javascript vyhodí nejakú výnimku), tak celé spojenie s MQTT brokerom padne. Ak máš nastavený reconnect, tak sa znova obnoví, ale môže potom aj cyklický padať.

Správnu funkcionalitu si môžeš overiť aj na projektore, ak to mám zobrazené.

Poznámka

Last Will správa môže byť len jedna pre každého klienta a zvyčajne sa kombinuje so stavom retained, avšak nie je to nutnosť. Broker sa postará o odoslanie tejto správy len v prípade ak zistí neočakávaný pád klienta. Teda ak sa klient úspešne odpojí volaním potrebných metód, táto správa nebude doručená, vtedy sa má sám klienta postarať prv o informovaní o zmene stavu a potom až sa odpojiť.

Úloha 3.3

Spojazdnite si zobrazovanie stavov klientov aj od svojich spolužiakov.

Pridajte si do HTML časti za <h2> element <div> pre zobrazovanie statusov:

<div id="statusData">?statuses?</div>

Pridajte si metódu na zobrazovanie stavu v časti JavaScriptu:

var statuses = {};
function statusArrived(name,messageText) {
    statuses[name] = messageText;
    var statusHtml = "";
    for(var name in statuses) {
        var color = "red";
        if (statuses[name] === "online") {
            color = "green";
        }
        statusHtml += "<span style='color: transparent; text-shadow: 0 0 0 "+color+";'>&#9899;</span>"+name+"<br/>";
    }
    document.getElementById("statusData").innerHTML = statusHtml;
}

Do metódy kde spracovávate prijaté správy (metóda onMessageArrived) si pridajte nasledujúci kód aby sme rozlíšili z akého topicu správa prišla a ináč ju spracovali:

var regex_status = /kpi\/test\/([a-z0-9]+)\/status/i;
var match_status = regex_status.exec(message.destinationName);
if (match_status !== null) {                
    statusArrived(match_status[1],message.payloadString);
} else {
    ... tu pôvodný kód z metódy vložiť ...
}

Nezabudnite si k svojim subscribe volaniam dopísať (alebo prepísať) aj všeobecný wilcard subscribe pre statusy:

mqttClient.subscribe("kpi/test/+/status");

Otestujte si funkcionalitu medzi sebou.

Krok 4: Nestabilné spojenie

Pri komunikácii cez TCP môže nastať situácia keď spojenie spadne a druhý účastník nedostane informáciu o tom, že spojenie padlo. Takýto prípad nastane najskôr pri rôznych druhoch nespoľahlivých spojení ako sú aj mobilné siete alebo iné spojenia používané pre pripájanie IoT zariadení.

MQTT protokol má práve pre zaistenie fungovania spojenia funkcionalitu keep-alive. Pri pripájaní má klient zadefinovať svoj interval pre udržanie spojenia. Hodnota môže byť nastavena podľa toho aké kvalitné spojenie má daný klient.

Tento interval je štandardne nastavený na hodnotu 60 sekúnd. To znamená, že ak broker za 60 sekúnd nepríjme žiadnu správu od klienta, tak odpojí klienta a odošle jeho last-will správu (ak bola zadefinovaná). Klient má na starosti odoslať aspoň raz za daný interval nejakú správu. Buď to môže byť klasická správa alebo ak žiadnú takúto správu klient neposiela musí poslať špecifickú správu ping. Taktiež ak nedostane klient odpoveď za daný čas od brokera, tak sa odpojí.

Pre Javascript Paho MQTT klienta je to parameter keepAliveInterval.

Úloha 4.1

Vyskúšajte si nastaviť vlastnú hodnotu keepAliveInterval s tým, že si vyskúšate aj odpojiť sieťové spojenie nečakane, napríklad zrušite pripojenie na WiFi alebo odpojíte ethernet kábel. Ak len vypnete tab v prehliadači, tak prehliadač zaručí správne zrušenie WebSocket spojenia a tým bude broker o vypnutí hneď informovaný a nepoužije sa proces keep alive.

Taktiež si skúste aj zmeniť hodnotu na väčšiu alebo menšiu.

Poznámka

Ak nastavíte veľmi malú hodnotu keep alive, tak sa niekedy môže zbytočne zvýšiť počet správ pre komunikáciu. Ak však je hodnota príliš vysoká tak sa broker o odpojení dozvie o toľko neskôr.

Ak klient bežne odosiela správy každú sekundu, tak je celkom v poriadku nastaviť keep alive aj len na napríklad na 3 násobok bežného času odosielania správ.

Práve nastavenie správneho keep alive parametru má značný vplyv na rýchlosť zaslania správy last will v prípade straty spojenia medzi klientom a brokerom.

Krok 5: Bezpečnosť

Aj v prostredí IoT netreba zabúdať na bezpečnosť, aj keď niekedy sa práve táto časť môže prehliadať aj z dôvodu menšieho výkonu niektorých IoT zariadení.

Základným spôsobom zabezpečenia prenosu je použitie enkrypcie na úrovni transportnej vrstvy. Ide hlavne o použitie bezpečného spojenia vo forme TCP s TLS/SSL alebo WebSocket s TLS/SSL teda cez HTTPS. Často použitie takéhoto spojenia je potrebné špecifický zadať pri pripájaní klienta.

Ďalšia z podstatných vlastnosti podporujúcich bezpečnosť je používanie mena a hesla pri prihlasovaní sa do brokera. Doteraz sme sa pre jednoduchosť prihlásovali bez akéhokoľvek mena a hesla, avšak možnosť meniť všetko nie je stále ideálna. Preto je možné vyžadovať prihlásenie menom a heslom a zároveň broker môže na základe tohto údaja obmedziť prístup pre používateľa len k niektorým topicom. Pričom je možné zadať oprávnenie na čitanie alebo zápis. Používanie mena a hesla takmer stráca svoj význam pri spojení bez TLS/SSL, keďže heslo sa prenáša bez akéhokoľvek zašifrovania kompletne v plain-text formáte.

Poďme si teda vyskúšať pripojenie pomocou TLS/SSL a s využitím mena a hesla.

Úloha 5.1

Pripojte sa k zabezpečenému brokeru s nasledujúcimi údajmi:

  • server: m23.cloudmqtt.com
  • port WebSocket Secure (WSS HTTPS) 35071, prípadne TCP TLS 25071 (to je len ak by ste použili iného klienta ako JavaScript)
  • username: kpi
  • heslo: kpi123

S týmto pripojením budete mať nasledujúce práva:

  • topic kpi/test/+/status čítanie a zápis
  • topic kpi/test/+/cursor čítanie

Skúste sa prihlásiť na odber topicu kpi/test/+/status a následne tam aj poslať nejakú správu. Vidíte túto správu?

Skúste sa prihlásiť aj na odber topicu kpi/test/+/cursor a tiež tam poslať nejakú správu, čo sa stane? Prípadne ak pošlete správu do akéhokoľvek iného topicu, čo sa stane?

Upozornenie

Tento server podporuje maximálne 5 pripojených klientov, preto sa nebudete môcť pripojiť všetci naraz. Môžete si ale vytvoriť aj vlastné bezplatné kontá a v nich si skúšať rôzne nastavenia aj s možnosťou pridávať vlastných používateľov a vlastné pristupové práva vo forme ACL.

Poznámka

V protokole MQTT nie je jasne špecifikované ako sa má správať broker v prípade, že klient nemá prístup pre čitanie alebo zápis do nejakého topicu. Často preto broker správu len ignoruje, ale klient sa nemusí dozvedieť, že jeho správa bola brokerom pre nedostatočné oprávnenie ignorovaná. Pre klienta sa táto správa javí ako odoslaná ale nikto ju nikdy nepríjme.

Iné formy zabezpečenia

Okrem spomínaných a skúšanych foriem zabezpečenia použitím mena, hesla a TLS/SSL je možné ešte použiť aj iné spôsoby pre zlepšenie zabezpečenia správ.

Pridávanie kontrolných súčtov do správ je metóda, kde sa do tela správy okrem samotnej správy môže pridať ešte číslo kontrolného súčtu alebo hash. Toto zabezpečenie však len umožni zistiť, že či sa správa nezmenila počas komunikácie, ale nijak nezvýši bezpečnosť prenášanej informácie.

Použitie kryptovania samotného tela správy je asi najlepší spôsob ako zabezpečiť samotný obsah správy, avšak tento prístup nie je nijako podporovaný v samotnom MQTT protokole. Protokol nám umožňuje len prenášať binárne správy, čo môže znížiť počet bajtov pri prenose kryptovanej správy oproti nutnosti využiť textové kódovania binárných údajov ako je base64 alebo iné. Pri tomto spôsobe je nutné aby aj odosielateľ aj prijímateľ vedeli zašifrovať a dešifrovať správu sami, pričom samozrejme je možné použiť už akýkoľvek spôsob šifrovania, či je to synchrónne alebo asynchrónne (verejný a súkromný kľúč).

Použitie X509 certifikátov pre klientov taktiež umožňuje zlepšiť bezpečnosť a to tým, že klient pri pripájaní použijé svoj vlastný X509 certifikát, ktorý mu bol dopredu distribuovaný a broker ho tak dokáže overiť ešte v čase vytvárania spojenia. Broker tak môže rýchlo odhaliť pokusy o pripojenie sa klientom, ktorí nemajú správny X509 certifikát. Táto možnosť ale nie je implementovaná v každom MQTT brokeri.

Bezpečnosť pre OpenLab broker

OpenLab broker openlab.kpi.fei.tuke.sk aktuálne podporuje nezabezpečené aj zabezpečené spojenia:

  • TCP port 1883
  • TCP s TLS/SSL port 8883
  • WS port 80
  • WSS port 443

Zdroje

  1. Vypracované Javascript riešenie z predošlého stretnutia
  2. Séria blogov HiveMQ o MQTT
  3. Séria blogov HiveMQ o MQTT a bezpečnosti