Vielfach in der Embedded-Welt scheinen Geräte jahrzehntelang ohne Updates auszukommen. Over-The-Air und IoT-Schnittstellen sind noch immer eher die Ausnahme. Wohl auch wegen der Verhältnismäßig hohen Stückkosten von Telemetrie-Modulen, die abwärtskompatibel in allen Mobilfunk-Bandbreiten eingesetzt werden können. Unverständlich eigentlich, da Fehler und neue Features natürlich zu behandeln sind und nicht millionenfach per Einsatz durch einen Service-Techniker und eine Flash-Schnittstelle per Kabel zu beheben. Aber es geht auch immer um das Ausmerzen von Vulnerabilities. Nehmen wir das Beispiel CVE-2014-01650 (Heartbleed). Diese Schwachstelle betraf die OpenSSL-Kryptobibliothek und damit indirekt zwei Drittel der Websites im Netz. Selbst drei Jahre später gibt es immer noch viele Embedded Linux-Geräte, die eine ungeschützte Version von OpenSSL ausführen und somit weitgehend anfällig für Angriffe sind. Gerade solche Sicherheitslücken unterstreichen die zwingende Relevanz von Over-the-Air-Updates (OTA-Updates).
OTA-Updates ermöglichen es Herstellern, Sicherheitspatches und Aktualisierungen schnell und effizient an die betroffenen Geräte zu verteilen, um potenzielle Schwachstellen zu schließen und die Sicherheit der vernetzten Systeme zu gewährleisten. Dies ist von entscheidender Bedeutung, um die Integrität und Vertrauenswürdigkeit von IoT-Geräten und anderen Embedded-Systemen sicherzustellen, insbesondere angesichts der anhaltenden Bedrohungen durch Cyberangriffe.
Ein Software-Update Over The Air funktioniert prinzipiell immer nach demselben Ablauf:
Erkennung und Benachrichtigung über das Update: Das Gerät, das OTA-Updates unterstützt, überprüft regelmäßig automatisch auf Softwareaktualisierungen. Der Benutzer kann diesen Prozess auch manuell verwalten. Immer wenn eine Softwareaktualisierung verfügbar ist, erhält der Benutzer eine Benachrichtigung, in der die aktualisierte Softwareversion und die darin enthaltenen Verbesserungen angegeben sind.
Herunterladen der Updates: In den meisten Fällen kontrolliert der Administrator den Download des Updates, aber in einigen Fällen muss der Benutzer die Genehmigung für das Update erteilen. Wenn die Update-Genehmigung akzeptiert wird, startet das Gerät den Prozess, indem es das Update-Paket von einem Server oder einem dafür vorgesehenen Repository herunterlädt. Das Update-Paket enthält in der Regel neuen Code sowie Konfigurationsdateien, Patches und andere Komponenten.
Update-Verifizierung: Nach dem Herunterladen überprüft das Gerät die Funktionsfähigkeit des Update-Pakets. Auf diese Weise stellt das Gerät sicher, dass das Update während des Transports nicht beschädigt oder gefälscht wurde. Zur Bestätigung der Authentizität der Software kann das Gerät kryptografische Hashes oder digitale Signaturen verwenden.
Update-Installation: Das Gerät führt die Update-Installation nur durch, nachdem das Update verifiziert wurde. Um Daten- und Einstellungsverluste zu verhindern, sichert das Gerät alle erforderlichen Informationen während der Installation.
Neustart und Aktivierung: Es ist üblich, dass das Gerät nach der Aktivierung des Software-Updates neu startet. Während dieses Prozesses kann das Gerät nach der Installation Aufgaben ausführen, wie das Migrieren von Daten oder das Aktualisieren von Konfigurationen.
Aufräumen und Abschluss: Das Gerät entfernt automatisch die alte Softwareversion und säubert unnötige temporäre Dateien, die während des Update-Prozesses eingeführt wurden. Nun läuft das Gerät mit der aktualisierten Software, die neue Funktionen, Fehlerkorrekturen und Sicherheitspatches bietet.
Spannend ist zudem die Frage nach der Art des Updates. Bei der Aktualisierung von Linux wird häufig zwischen "Block" und "File" als Aktualisierungssystematik unterschieden. Dies bezieht sich auf die Aktualisierung eines gesamten Abschnitts gleichzeitig, indem direkt auf das Blockgerät geschrieben wird, im Gegensatz zur Aktualisierung von einzelnen Dateien, um eine Aktualisierung durchzuführen.
In Embedded Linux sind blockbasierte Upgrades aufgrund ihrer Atomarität und der Tatsache, dass ein gesamtes Dateisystem normalerweise das Ergebnis eines Embedded Linux-Bausystems ist, die bevorzugte Methode. Erwartbar ist der Speicherplatz auf jedem Embedded-Devikce für ein bestimmtes Produkt konstant, sodass wir bei jedem Embedded Linux-Build die gleiche Größe der Partition erstellen. Diese Art von Update geht oft mit einer Art von Ausfallsicherung oder Wiederherstellungsbild einher.
Wir stellen vier Strategien vor, wie Betriebssystem- und Applikations-Images aktualisiert werden können.
Single-Copy mit Standalone Image: In diesem Ansatz besteht die Software-Upgrade-Anwendung aus einem Kernel (eventuell reduziert, indem nicht benötigte Treiber entfernt werden) und einem kleinen Root-Dateisystem mit der Anwendung und ihren Bibliotheken. Die Gesamtgröße ist viel kleiner als eine einzige Kopie der Systemsoftware. Der System kann in den "Upgrade"-Modus versetzt werden, indem er dem Bootloader signalisiert, dass die aktualisierte Software gestartet werden muss. Dies kann beispielsweise durch das Setzen einer Bootloader-Umgebung oder die Verwendung eines externen GPIO erfolgen. Der Bootloader startet dann die Updater-Software, indem er die Updater-Software startet. Da es im RAM läuft, ist es möglich, den gesamten Speicherplatz zu aktualisieren. Im Gegensatz zur Double-Copy-Strategie muss das System immer neu gestartet werden, um in den Update-Modus zu gelangen. Dieser Ansatz spart Speicherplatz, garantiert jedoch keinen automatischen Rollback ohne erneutes Aktualisieren der Software.
Single Copy mit Standalone Image (Quelle: swupdate)
Double-Copy mit Fall-Back (Zwei-Kopien-Strategie): Wenn ausreichend Speicherplatz auf dem Speicher vorhanden ist, können zwei Kopien der gesamten Software gespeichert werden, um sicherzustellen, dass immer eine funktionierende Kopie vorhanden ist, selbst wenn das Software-Update unterbrochen wird oder ein Stromausfall auftritt. Jede Kopie muss den Kernel, das Root-Dateisystem und alle weiteren aktualisierbaren Komponenten enthalten. Es ist ein Mechanismus erforderlich, um festzustellen, welche Version ausgeführt wird. Der Updater sollte in die Anwendungssoftware integriert werden, und die Anwendungssoftware löst das Update aus. Es wird immer die Standby-Kopie aktualisiert, während die laufende Kopie der Software unberührt bleibt. Nach einem Neustart entscheidet der Bootloader, welche Kopie ausgeführt werden soll. Der offensichtlichste Nachteil ist der benötigte Speicherplatz. Der verfügbare Platz für jede Kopie ist weniger als die Hälfte der Speichergröße. Ein Update ist jedoch immer sicher, auch bei einem Stromausfall.
Kombinierte Double-Copy mit Rettungssystem: Dieser Ansatz bietet ein Wiederherstellungsverfahren, um Update-Fehler in schweren Fällen zu beheben, wenn die Software beschädigt ist. Wenn keine der Kopien gestartet werden kann, startet der Bootloader das Rettungssystem (möglicherweise auf einem anderen Speicher als das Hauptsystem), um zu versuchen, das Board zu retten. Das Rettungssystem kann ebenfalls während eines Standard-Updates aktualisiert werden.
Kombinierte Double-Copy mit Rettungssystem (Quelle: swupdate)
Split-Systemupdate und Anwendungsupdate: Dieser Ansatz besteht darin, das gesamte Bild in mehrere kleinere Teile aufzuteilen, um die Transfergröße zu reduzieren, wenn nur wenige Dateien aktualisiert werden. Dabei muss besonders auf die Kompatibilität zwischen System und Anwendung geachtet werden. Es muss ein Framwork verwendet werden, welches die Versionierung für jedes Artefakt unterstützt. Dies ermöglicht eine effiziente Aktualisierung von Softwarekomponenten, kann allerdings zu zahlreichen Versionspermutationen führen.
Split-Systemupdate und Anwendungsupdate (Quelle: swupdate)
Die Verwaltung dieser Artefakte ist ein entscheidender Schritt im Update-Prozess. Mender bietet hierfür ein Befehlszeilentool, mit dem Artefakte manuell installiert werden können. Alternativ können Artefakte über eine benutzerfreundliche Weboberfläche auf den Backend-Update-Webdienst hochgeladen werden. Von dort aus können sie gezielt an einzelne Geräte oder Gruppen von Geräten weitergeleitet werden.
Die Weboberfläche zur Steuerung des Backend-Update-Servers ist gut durchdacht und benutzerfreundlich gestaltet. Die Einrichtung erfolgt über Docker, was die anfängliche Konfiguration erleichtert. Allerdings erfordert ein voll funktionsfähiges System einige Kenntnisse in Docker. Mit diesem Dashboard können Sie problemlos alle aktiven Geräte anzeigen und Updates sowohl individuell als auch in Gruppen verteilen.
Es ist jedoch wichtig zu beachten, dass Mender an ein fest kodiertes Boot-Gerät (z. B. /dev/mmcblk0) gebunden ist, was bedeutet, dass das Booten von verschiedenen Medien nicht einfach möglich ist. Zudem trifft Mender einige Annahmen über Ihr System, wie etwa spezifische Optionen im Bootloader und Kernel (z. B. die Unterstützung bestimmter Dateisysteme). Zudem setzt es die Verwendung von systemd voraus, was es für einige Projekte möglicherweise weniger geeignet macht.
SWUpdate ist ein äußerst anpassbares Aktualisierungssystem, das eine Vielzahl von Konfigurationsoptionen bietet. Dies ermöglicht es Entwicklern, die Software-Aktualisierung genau nach ihren Bedürfnissen anzupassen. Die Konfiguration erfolgt über das "kconfig"-System, das vielen Embedded-Linux-Entwicklern bereits vertraut sein dürfte.
Einige der Konfigurationsoptionen von SWUpdate sind beeindruckend vielseitig. Das System unterstützt mehrere Bootloader, darunter U-Boot, GRUB und EFI Boot Guard. Es bietet die Möglichkeit, signierte Images auf der Grundlage bestimmter öffentlicher Schlüssel zu signieren und zu verifizieren. Zudem kann es die Verschlüsselung von Images mit symmetrischen Schlüsseln unterstützen.
Die Struktur von SWUpdate ist klar definiert. Jede Aktualisierung besteht aus einem CPIO-Archiv, das Metadaten in Form einer "sw-description"-Datei und die eigentlichen Datendateien enthält, die zur Durchführung der Aktualisierung erforderlich sind. Diese "sw-description"-Datei enthält normalerweise eine Liste der Dateisysteme und der Blockgeräte, in die sie eingefügt werden sollen. Im Gegensatz zu einigen anderen Aktualisierungssystemen muss diese Aktualisierungsdatei jedoch vom Benutzer manuell erstellt werden und wird nicht automatisch generiert.
Eine wichtige Unterscheidung zu Mender liegt darin, dass SWUpdate sich nicht um die Details kümmert, wie Ihr System eingerichtet werden muss. Während Mender beispielsweise das Patchen der u-boot-Umgebung übernimmt, müssen Sie bei SWUpdate die Low-Level-Implementierungsdetails für das "Ping-Pong"-A-B-Dual-Rootfs-Setup selbst ausarbeiten.
SWUpdate bietet verschiedene Möglichkeiten zur Installation, einschließlich der Verwendung der Kommandozeile. Zudem stellt es ein Backend-Aktualisierungssystem bereit, das auf Eclipse hawkBit basiert. Dieses System arbeitet in Verbindung mit einem auf dem Zielsystem laufenden Daemon namens "Suricatta", der die Aufrufe an SWUpdate verarbeitet. Darüber hinaus kann SWUpdate sogar einen eingebetteten HTTP-Server auf dem Gerät selbst betreiben, über den Updates hochgeladen und installiert werden können.
Insgesamt ist SWUpdate eine äußerst flexible Lösung für die Software-Aktualisierung in Embedded Linux-Systemen. Mit seiner umfangreichen Konfigurierbarkeit und den vielseitigen Möglichkeiten zur Integration und Installation bietet es Entwicklern die Freiheit, die beste Lösung für ihre spezifischen Anforderungen zu gestalten.
RAUC verwendet sogenannte "Bundles", die vom Build-System erstellt werden. Diese Bundles enthalten komprimierte Dateisysteme sowie Metadaten und müssen immer digital signiert sein, was ein zentrales Konzept von RAUC ist. Die Aktualisierung erfolgt, indem Dateisystem-Images in verschiedene "Slots" programmiert werden, die dann als funktionsfähig, defekt oder bereit für ein Update markiert werden können.
RAUC arbeitet eng mit der Bootloader-Umgebung zusammen, um zu entscheiden, ob ein Update erforderlich ist. Es kann bestimmte Optionen im Kernel erfordern, wie beispielsweise die Unterstützung für das SQUASHFS-Dateisystem. Es hat auch einige Software-Abhängigkeiten im Root-Dateisystem.
Die Implementierung von RAUC erfolgt in der Programmiersprache C. Im Gegensatz zu Mender unterstützt es kein Streaming, wie SWUpdate und Mender es können. Dennoch bietet es eine effektive Möglichkeit zur Aktualisierung von Software auf Embedded-Systemen.
Ähnlich wie SWUpdate unterstützt auch RAUC die Verwendung von Eclipse HawkBit als Backend-Update-System. Dies ermöglicht es, Updates über eine benutzerfreundliche Weboberfläche auf die Geräte zu übertragen und zu verwalten.
Insgesamt ist RAUC eine leichte und dennoch leistungsstarke Lösung für die Aktualisierung von Software in Embedded-Systemen. Mit seiner geringen Binärdateigröße und seiner Fähigkeit zur digitalen Signierung von Bundles bietet es eine zuverlässige Möglichkeit, Embedded-Systeme auf dem neuesten Stand zu halten.
Für Embedded-Linux-Anwendungen bieten sich in Sachen OTA-Updates zahlreiche Frameworks an. Insbesondere im industriellen Kontext ist ein Open-Source-Modell (ggf. mit Subscription-Features) immer eine gute Wahl, da größere Frameworks wie auch die zum Update sich einer großen Community erfreuen. Die hier vorgestellten Strategien für Software-Updates stellen probate Lösungsansätze unter verschiedenen Gesichtspunkten dar, mit denen sich intelligente Systemlösungen schaffen lassen.