 Schön, dass ihr alle gekommen seid, trotz der knallenden Sonne draußen oder vielleicht gerade wegen der knallenden Sonne. Mein Name ist Michael Stapelberg und dieser Vortrag geht über Go Crazy. Zunächst zur Agenda. Wir werden erstmal motivieren, warum das Projekt überhaupt ins Leben gerufen wurde. Dann werde ich eine Übersicht erklären und mit einer Demo veranschaulichen, damit ihr einfach mal sehen könnt, wie sich das Ganze anfühlt und aussieht. Das macht es glaube ich ein bisschen konkreter. Danach werde ich erklären, auf welcher Hardware das ganze Projekt läuft, wie Cross Compilation Grop funktioniert in Go. Ich werde auf Partitionen, Dateisysteme und deren Updates eingehen. Dann geht es um Firmware, Kernel und deren Updates wieder. Dann werde ich einige Anwendungsfälle vorstellen, damit ihr anschauliche Beispiele habt, was man mit der Plattform tatsächlich machen kann. Und dann haben wir Zeit für Fragen und Antworten. Sofern irgendjemand eine Frage hat, die unfassbar dringend ist, könnt ihr die auch direkt stellen, einfach die Hand heben, aber ansonsten haben wir Zeit am Ende. Zur Motivation zunächst. 2012 war ich im Hackerspace-Round-Zeit-Labor aktiv und habe dort unter anderem am Hausbus-Projekt mitgearbeitet. Das war ein Projekt, um die verschiedenen Bestandteile in diesem Hackerspace zu vernetzen, über eine RS485-Verkabelung. Und wir haben dafür Mikrocontroller verwendet an verschiedenen Punkten in dem System. Jetzt muss ich sagen, dass Mikrocontroller zu programmieren nicht das Spaßigste der Welt ist, vielleicht für manche schon, aber trotz der Tatsache, dass ich C zu dem Zeitpunkt ganz gut konnte, war das eine schwierige Sache. Es haben sich immer wieder Fehler eingeschlichen, es ging langsam voran, es ging vor allem langsamer voran als gehofft und erwartet. Und irgendwann ist dann allerdings der Raspberry Pi auf den Markt gekommen. Und ich dachte, oh, das sieht ja super aus, ein Singleboard-Computer, mit dem man aber auch direkt Hardware ansprechen kann. Also er vereint quasi den direkten Hardwarezugriff und das Programmieren in Hochsprachen. Und dann habe ich für mich persönlich den Beschluss gefasst, dass ich Automatisierung grundsätzlich nur noch in Hochsprachen umsetzen möchte, und zwar auf Basis von einem Raspberry Pi. Ich habe dann ein reproduzierbares Read-Only-Image angefertigt für den Raspberry Pi, einfach damit alle anderen Mitglieder in diesem Hackerspace das Nachvoll ziehen können und zu demselben Ergebnis kommen können, wie ich, und da an dem Setup auch arbeiten können. Und das habe ich gemacht, indem ich ein eigenes Bildskript geschrieben habe. Also einfach so ein bisschen Shell, das macht dann so einen Debootstrap, um den Debian-System vorzubereiten und das wird dann in ein SD-Card-Image verpackt. Das hat auch soweit ganz gut funktioniert. Der Gedankengang hinter einem Read-Only-Image ist, dass wir festgestellt haben, dass bei Embedded Devices das erste Bauteil, was anfängt kaputt zu gehen, üblicherweise das Speichermedium ist, wenn man zum Beispiel eine CF-Karte hat oder eine SD-Karte oder sowas, dann gibt die üblicherweise deutlich früher den Geist auf als alle anderen Bestandteile. Zumindest, wenn man einfach ein reguläres Linux-System installiert und halt einfach viele Streibzugriffe auch hat. Das hatten wir in einem anderen Gerät im Raumzeit-Labor gesehen und deswegen haben wir gesagt, okay, wir machen das jetzt grundsätzlich als Read-Only-Image. Und wie gesagt, das hat gut funktioniert. Ich habe im Jahr 2013 so ein Image angefertigt und habe das dann tatsächlich auch zu Hause benutzt bis 2017. Der Grund, warum das so lange im Einsatz war, war, dass Updates große Blöcke Zeit erfordern. Man muss also dieses Bildskript aktualisieren und dabei stellt man dann üblicherweise fest, dass sich einige Dinge geändert haben. Als kleines Beispiel, als ich das zum ersten Mal geschrieben habe, gab es noch M-Debian, das war so eine Embedded-Debian-Geschichte. Das ist mittlerweile in das richtige Debian übergegangen und so sind eben an allen Ecken und Enden Kleinigkeiten, die man irgendwie anpassen muss. Also man muss quasi von ganz neuem Anfang ein komplett neues Image bauen. Dann muss man das nicht nur bauen, sondern man muss es auch testen und die Fehler sieht man gegebenenfalls erst viel später. Das braucht also echt viel Zeit. Und diese Art an Zeit oder die passende Motivation und Zeitkombinationen hatte ich eben nicht mehr in all den Jahren. Und das Resultat ist, dass sich die ganzen Sicherheitslöcken in den verschiedenen Bestandteilen, dieses typischen Read-only-Images, über vier Jahre hinweg angesammelt haben. Wenn ihr mal in einem regulären System, also zum Beispiel euer Desktop-Computer oder irgendein Server, den ihr habt, Tools installiert, wie zum Beispiel Depsec an, Security Analyzer, der schickt euch dann täglich Emails, Summaries, was für neue Sicherheitslöcken in den Paketen aufgetaucht wurden und welche geschlossen wurden, dadurch, dass ihr Updates gemacht habt, dann wird man ganz schnell recht deprimiert, weil einfach überall so viele Sicherheitslöcken drin sind. Das ist unfassbar. Und dann habe ich gesagt, okay, das kann so nicht weitergehen. Wir müssen irgendwie was anders machen und ich habe folgende Leitfragen aufgestellt. Zum Ersten kann man die Angriffsfläche stark reduzieren. Also kann man quasi alle Sachen wegwerfen, die man nicht unmittelbar braucht, um das zu machen, was man eigentlich mit diesem Pi machen will. Also wenn ich zum Beispiel Musiksteuerung auf meinem Pi implementieren will, dann will ich da nicht auch noch ein typisches Debian-Base-System mit Shell und allem maintainen müssen dafür. Das ist nicht der richtige Trader, finde ich. Zweite Frage, kann man die Updates komplett automatisieren? Wie gerade eben gesagt, ich hatte vier Jahre lang keine Updates gemacht. Offenbar bin ich nicht der Typ, der regelmäßig Updates macht. Ich muss das komplett automatisieren, sonst passieren sie einfach nicht. Und drittens, wenn wir schon dabei sind, kann man die Einrichtung eines Raspberry Pies nicht eigentlich drastisch vereinfachen. Wenn ihr euch typische Open-Source-Projekte anschaut, die veröffentlicht werden für Raspberry Pies, dann haben die so Anleitungen von wegen, ja, nehmt da so einen Raspberry-Image und dann führt folgende 50 einfache Schritte aus, bis ihr dann die Software am Laufen habt. Kann man da nicht was machen, was viel einfacher funktioniert, sowohl für die Entwickler als auch für die Endanwender. Als Übersicht über das Projekt Go Crazy, wenn ich es in einem Satz beschreiben müsste, würde ich sagen, es ist ein Projekt, in dem wir eine beschriebene Programme auf Raspberry Pies zu betreiben. Das heißt, die Idee ist, ihr habt ein beliebiges in Go geschriebenes Programm und dann packt ihr das auf Go Crazy und auf den Raspberry Pi und der Rest wird einfach für euch gemacht, also sowohl der eigentliche Linux-Könel als auch das komplette User-Land. Das impliziert natürlich auch, dass wir alle Bestandteile brauchen, die man eben in so einem normalen Linux-System so hat, also einen In-In-System und einen User-Land. Bei uns ist aber der Clue, sondern auch das komplette System in Go schreiben. Einfach damit wir eine angenehme, speichersichere Sprache haben, die von sich aus schon mal die Klasse an Fehlern reduziert, die man da haben kann. Und das Resultat davon ist eben, dass wir ein simples In-System in Go implementiert haben. Dieses In-System ist ein bisschen unüblich, es ist stark integriert, es bringt direkt einen Web-Interface mit, ein Logging-Mechanismus, so wie das Journal quasi oder Syslog und ein Update-Mechanismus. An User-Land-Komponenten haben wir derzeit einfach nur einen NTP und einen DHCP-Kleint. Man braucht aber eigentlich auch gar nicht viel mehr, zumindest für die Anwendungsfälle, die wir bislang abgedeckt haben. Denn der Linux-Könne macht den Rest, also zum Beispiel die Adress-Konfiguration in einem IPv6-Netz passiert vollkommen automatisch. So, jetzt schieben wir mal schnell eine Demo ein, was ich hier vorbereitet habe, ist, ich habe auf meinem Computer einen DHCP-Server installiert, damit quasi mein Heimnetz simuliert wird. Dann werde ich ein Tool verwenden, das nennt sich Go Crazy Packer. Das packt einfach eine Anwendung und legt sie dann auf die SD-Karte. Und was wir da machen werden, ist den Bootvorgang über eine serielle Konsole verifizieren. Das heißt, was ich hier habe, ist einfach so ein handelsüblicher Raspberry Pi 3. An dem ist hier über diese kabelenden USB-to-Serial angeschlossen, damit wir die Gutmeldungen mitverfolgen können. Und die SD-Karte, die ich gleich in diesen Pi stecken werde, habe ich hier in so ein Card-Reader gepackt, der an meinem Computer angeschlossen ist. Das wechseln wir mal hier und schauen uns kurz die SD-Karte an. Wie ihr sehen könnt, ist da noch nicht mal eine Partitionstabelle drauf, die ist also blank. Wenn ich die einstecken würde, dann würde er davon nicht buden. Also würde einfach nichts passieren. So, was wir jetzt machen, ist, wir starten den Packer. Und was wir angeben an Argumenten ist, wir sagen DefSDB, also diese SD-Karte, die ich gerade gezeigt habe, wird überschrieben werden, mit dem folgenden Go-Programm, das gibt einfach Hello World aus und beendet sich dann wieder. Ich starte das jetzt. Er sagt dann Installing go crazy slash hello. Im Hintergrund hat er das Ganze jetzt cross-compiled. Dann, ich scroll noch mal kurz hoch, damit wir das verfolgen können, dann hat er die SD-Karte partitioniert, hat ein Boot-Datei-System geschrieben und ein Rout-Datei-System hat uns mitgeteilt, dass, sofern wir persistente Daten speichern möchten und nicht nur eben ein Read-Only-System haben wollen, müssen wir jetzt auch noch das System erzeugen. Und dann sagt er, wenn wir jetzt das System starten wollen, müssen wir diese SD-Karte in Raspberry Pi 3 packen und anschließend spuckt er uns noch so ein URL aus, mit der wir hinterher mit dem System interagieren können. Ich nehme jetzt also diese SD-Karte und stecke sie in den Raspberry Pi und dann wechsel ich hier mal kurz auf das andere Fenster. Da habe ich die Ausgabe der serialen Konsole. Ich gebe dem Raspberry Pi Strom. Das ist der SD-Karte und jetzt sehen wir hier die Boot-Meldungen. Das ist einfach ein ganz normaler Linux-Könnl, wie er eben so startet. Aber wir sehen hier eine Meldung, die unüblich ist. Ich schaue noch mal kurz hoch, dass man das auch noch sehen kann. Das ist diese hier. Da sieht man go crazy build timestamp und wenn ihr die Zeit vergleicht, dann werdet ihr sehen, das ist der Zeitstempel von gerade eben. Das ist wirklich die Version, die wir gerade eben zusammengebaut haben. Wir können jetzt diese URL hier nehmen und machen einfach mal das auf. Jetzt sind wir hier im Web-Interface angelangt von go crazy. Wir sehen drei Dienste sind am Laufen. Der DHCP client, der NTP client und das Hello World Programm, was wir installiert haben. Bei dem DHCP client sieht man, der hat sich einen Lease geholt, wie wir eben auch auf der Server-Seite gesehen haben. Der NTP client startet neu. Der hat irgendeine Art von Problem. Das liegt einfach daran, dass in dem aktuellen Setup gesetzt habe. Das heißt, er kann nicht ins Internet und kann kein NTP Server kontaktieren und kann sich daher keine Zeit holen. Das ist aber auch nicht weiter schlimm. Und in dem Hello Programm sehen wir, dass der die ganze Zeit einfach Hello World ausgibt und das go crazy den die ganze Zeit immer wieder neu startet. Das heißt, die Idee ist, dass wir einfach Services haben, die die ganze Zeit hier laufen sollen und wenn sie nicht laufen, dann werden sie neu gestartet. Zusätzlich sehen wir eine Übersicht über das System. Wir sehen den Speicher und das Innet-System nimmt einen relativ großen Bestandteil weg, aber absolut gesehen 6 Megabyte. DHCP und NTP client sind signifikant kleiner. Es gibt einen Haufen Speicher, der nicht genau zugeordnet werden kann, aber im Wesentlichen ist der meiste Speicher eigentlich verfügbar für beliebige Anwendungen, die ihr so draufpacken wollt. Bei dem Datei-System habe ich noch von dem vorherrigen Partitionierungslauf ein persistentes Datei-System drauf. Da sehen wir, wir könnten jetzt noch 12 GB voll schreiben und dann sehen wir zu guter Letzt noch, unter welchen Netzwerk-Adressen GoCrazy verfügbar ist. Standardmäßig ist er nur auf privaten IP-Adressen verfügbar, weil er auch HTTP spricht und kein HTTPS, sonst hätte man mit den zertifikatenden Bootstrap-Problemen. Damit ist die Demo erstmal beendet. Ihr habt gesehen, wie fühlt sich das an? Was braucht man eigentlich, um einen Raspberry Pi ans Laufen zu bekommen? Wie wir sehen, im Wesentlichen den Namen des Go-Programms, den Packer befehlen, eine SD-Karte rein und los geht's. Das ist nicht mehr schwer. An unterstützter Hardware haben wir exakt den Raspberry Pi 3. Das ist eine häufige Frage online, Leute kommen und sagen, ich habe da letztens gesehen, es gibt den Raspberry Pi Zero W. Ich habe mir den bestellt, kann ich da GoCrazy verwenden? Die Antwort lautet, nein. Das Zero W-Modell hat eben eine ganz andere Architektur überhaupt, auch als der Raspberry Pi 3. Das heißt, das ist eine von den alten CPUs, nicht eine von den neuen CPUs. Und der Grund, warum wir nur den Raspberry Pi 3 unterstützen, ist, dass das die erste Version ist, die von einem mit regulären, ganz normalen Upstream-Linux-Körnel unterstützt wird. Alle anderen Raspberry Pi-Versionen haben vom Raspberry-Projekt eine spezielle Körnel-Version gebraucht, mit eigenen Patches und so weiter. Das war am Anfang bei dem Pi 3 auch so, aber diese mittlerweile alle Upstream eingeflossen. Das heißt, den kann man jetzt einfach direkt verwenden. Das ist schön, weil dadurch einen Mittelmann wegfällt. Wir können jetzt einfach sagen, wenn man den Raspberry Pi 1.org krallen und den Source Code kompellieren, den Schmeißenden auf den Pi läuft. Das heißt, da ist kein Mittelmann mehr für Security Updates, was das verzögern könnte. Weiterhin, der Raspberry Pi 3 hat ein Ethernet-Port und Go Crazy kann derzeit noch kein Wi-Fi. Verlinkt auf den Folien, die auch nachher online landen, ist die Github-Issue, wo man sehen kann, wie wir diskutieren, in welchem Ausmaß er überhaupt. Ein sinnvoller Kompromiss wäre wahrscheinlich offene Wi-Files, weil für geschlossene Wi-Files müsste man eben ein Sublicant implementieren, was schon sehr kompliziert ist. Der letzte Grund, warum wir genau eine Hardware-Version vom Raspberry Pi unterstützen, ist, dass wir die Test-Markets möglichst klein halten wollen. Das heißt, immer wenn Leute Änderungen anfordern oder vorschlagen, müsste ich sonst auf ganz vielen verschiedenen Raspberry Pies diese werden. Als Randnotiz, Cross-Compilation in Go ist super, super einfach. Für diejenigen Leute, die noch nicht mit Go gearbeitet haben, der erste Befehl ist, wie man vorausgesetzt, man hat den Go-Compiler installiert, einfach ein Paket kompilieren und installieren würde auf dem Host-Betrieb-System, also einfach mit Go install und dann wieder den Fahrt zu dem Hello World Programm, würde ich das bauen und gegebenenfalls Go OS, sofern man unter eine Mac oder Windows kompiliert oder mal anderen Betriebssystemen, aber das war's dann auch schon und dann bekommt man ein Cross-Compiled-Binary, also keine kompliziert Toolchain oder so was, nichts aus Drittherstellerquellen, sondern das läuft einfach alles mit der Standard-Installation. Der einzige Schritt, den man jetzt noch braucht, nachdem man weiß, wie man Cross-Compiled um quasi einen Prototyp von Go Crazy auf dem Pi zum Laufen zu bringen und das war auch das erste, und wir wollen jetzt keine Lip-C aufsetzen, sondern wir wollen direkt von dem Kernel in den Go-Programm starten und das einzige, was man dafür noch braucht, ist die Environment-Variable cGoEnabled auf 0 zu setzen, damit er davon ausgeht, dass eine C-Umgebung nicht verfügbar ist und dann kann man tatsächlich den Kernel starten mit init gleich slash Hello und dann startet er das Programm. Also das war der erste Proof-of-Concept und der hat super funktioniert und dann haben wir gedacht, okay, das scheint ein Init-System in Go in Anführungszeichen ist gar nicht so schwer. Wenn ihr euch mal anschaut, wie minimale Init-Systeme üblicherweise implementiert sind, dann ist es auch nicht arg viel mehr, aber typischerweise benutzen die eben Asynchronous Signal Handling und so mit Weight-Child und lauter so Sachen. Das braucht man alles in Go nicht, wenn man das so macht, dass einfach jeder Befehl, den man im Hintergrund laufen lassen möchte, in der eigenen Go-Routine ausgeführt wird und dann kann man da auch blockieren, dass quasi das Modell, das ist eine Superweisfunktion und es ist ein bisschen hochgestochener Name dafür, dass es eigentlich einfach eine endlose Vorschleife ist, die halt den Befehl immer wieder neu startet mit einem eine Sekunden Delay zwischendrin. Und das eigentliche Programm macht dann nichts weiter als zu sagen, okay, für jeden Befehl, den wir ausführen wollen, superweisen wir den im Hintergrund und dann schlafen wir endlos lange. Fertig. Jetzt haben wir also nicht nur einen Cross-Compile des Binary auf den Pi ans Laufen gebracht, sondern auch wie kommt jetzt eigentlich die Software genau auf den Raspberry Pi. Ich habe euch vorhin gezeigt, wie man eine SD-Karte, wenn sie komplett leer ist, mit dem Go Crazy Packer bespielt. Was er im Hintergrund macht, ist, dass er die SD-Karte mit einer MS-DOS Partition Table versieht. In dieser Partition Table ist die erste Partition ein FUD-Datei-System. Das ist deswegen wichtig, weil der Bootloader vom Raspberry Pi das erwartet. Wenn er diese Partition nicht findet, dann startet er nicht. Also der lädt davon einfach die Firmware und das muss FUD eine MS-DOS Partition Table sein. Wie man das also machen würde, auf einem regulären Linux-Computer, ohne das komplette Go Crazy Projekt, ist wie über die Befehle, wie ich sie hier unten auf der Folie gelistet habe. Also man benutzt F-Disk, um die SD-Karte mit einer Partitions-Tabelle zu versehen. Man benutzt mkfs.vfad, um die Partition zu erstellen, das Datei-System zu erstellen auf der Partition. Dann mounted man das und dann kopiert man die ganzen Dateien drüber. Am Anfang in unserem Proof-Concept war das einfach so, man sollte den Linux-Körnel, die Text-Konfigurations-Dateien für den Bootloader und die Firmware-Dateien. Damals war das auch alles noch auf einer Partition. Dann mounted man das, steckt das in den Pi und los geht's. Also unter einem normalen Linux-System ist es gar nicht so schwer. Ein paar Sachen haben uns aber gestört, daran, wie man das unter einem Linux-System macht. Zum einen, also diese ganze F-Disk, mkfs und FileSystem und LoopMount-Geschichte hat ein paar Nachteile. Zum einen finde ich furchtbar unbequem zu automatisieren. Wir sind für interaktive Benutzung gedacht. Vor allem aber sind die Cleanups manchmal nicht sauber. Also ich hatte zahllose Male. Es ist passiert, dass mein Computer sich irgendwie verhakt hat. Irgendwie war noch ein alter LoopMount da oder so und den konnte man nicht mehr entfernen und da muss man irgendwann rebooten und das ist einfach alles sehr unsauber, finde ich. Außerdem erfordern die Befehle natürlich alle Rotrechte und auf manchen Betriebssystemen sind die gar nicht vorhanden. Das Mac OSF-Disk zum Beispiel erstellt keine MS-DOS Partition-Table. Und zu guter Letzt sind die Partitions-Tabelle und in dem Projekt ging es ja darum, dass wir alles in Ghost schreiben. Die einzige logische Konsequenz ist also, dass wir eigene Implementationen anfertigen von diesem ganzen Verfahren. Eine Partition-Table zu schreiben ist aber, wie sich rausstellt, auch gar nicht so schwer. Das ist der komplette Code abgesehen von Partition 2 bis 4, die dem gleichen Schema folgen wie Partition 1. Eine Partition-Table ist nichts weiter als 16 bytes, die man eben richtig auf dieses Device malen muss. Und im Wesentlichen, wie das funktioniert, ist man hat ein paar Werte, die jedem der F-Disk benutzt hat, sicherlich auch sehr bekannt vorkommen. Ich finde, der F-Disk ist einfach ein sehr kompliziertes Programm, um 16 bytes in der richtigen Reihenfolge zu schreiben. Man hat hier zum Beispiel das Active Partition Flag, das sogenannte Boot Flag, das ist entweder 0x80 oder 0x00. Dann hat man die Start- und Längenwerte, die aber im Cylinder, Head- und Sektor Verfahren angegeben werden. Das Verfahren ist mir zu kompliziert gewesen, aber glücklicherweise ist es auch gar nicht mehr nötig, dieses alte Verfahren zu implementieren, sondern wenn man da einfach den ungültigen Wert streibt, dann nimmt der die die logischen Werte, die in den separaten, also in den anderen Feldern gespeichert werden, nämlich hier Start- und Länge. Die braucht man ohnehin, wenn man eine gewisse Größe überstreitet, deswegen haben wir einfach die alten Verfahren komplett unter den Tisch fallen lassen. Dann gibt man den Dateistim Typ an, in diesem Fall für FAT 0xC und dann ist man auch schon fertig. Dann hat man eine Partition beschrieben, dass die ganze Partitions- Tabelle, also Partition 1 bis 4, wird dann am Ende mit einer Signatur noch abgefertigt und dann schreibt man das Ganze im Little-Indian-Verfahren auf das Device und dann ist man auch schon fertig. Als FAT-Dateisystem haben wir uns konkret für das FAT16B-Dateisystem entschieden. Das kann man in weniger als 500 Zeilen Go-Code implementieren. Ich habe den Link auf die Folie gepackt für diejenigen, die es interessiert. Das ist wohl gemerkt kein kompletter FAT- Dateisystem, sondern nur ein Imageerzeuger quasi. Wenn man genau weiß, welche Dateien man auf dieses Image packen will, dann kann man diesen Code benutzen. Wir unterstützen nur 8.3 Dateinamen wegen Patenten für längere Dateinamen. Das ist ein bisschen unglücklich, aber funktioniert erstmal. Ein FAT-Dateisystem besteht im Wesentlichen aus dem Bootsektor. Das ist ein Sektor in dem Metadaten über das Dateisystem festgehalten werden. Aus einer File Allocation Table, die auch namensgebend ist für das Dateisystem, also daher FAT, den Route Directory einträgen. Also für das Route Directory werden diese Directory-Einträge direkt in einem fixen Sektor gespeichert und sind auch begrenzt in der Anzahl. Alle weiteren Directries werden dann in der Data Area beschrieben und dann eben in der Data Area nicht nur Directries, sondern auch Dateienhalte. Das einzig schwierige, was man bei einem FAT-Dateisystem eigentlich beachten muss, ist das ganze Padding. Man muss an einigen Orten auf die Sektorsize padden und dann an Orten auf die Cluster Size. Die Cluster Size kann man frei wählen. Wir wählen sie z.B. derzeit auf 4-mal die Sektorsize und durch diese Größe wird dann auch der Overhead bestimmt und die maximale Dateisystemgröße. FAT ist also noch eins von diesen Dateisystemen, wo man entweder eine bestimmte Anzahl an Dateien nicht überschreiten darf oder eben auf die Größe der Dateien achten muss. Da muss man so ein bisschen den Trade-off finden. Wir haben gesagt, 127 Megabyte wird uns jetzt erst mal lang und dann schauen wir mal, wie sich das entwickelt, ob das eine gute Wahl war als Einschub, wie funktionieren eigentlich diese Updates und für was für eine Partitions-Tabelle haben wir uns letztendlich entschieden. Wir haben vier Partitionen. Die erste ist die Boot-Partition. Dort befindet sich Kernel und Firmware. Das ist, wie schon gesagt, ein FAT-16B-Dateisystem, weil der Raspberry Pi das so erfordert. Die zweite und dritte Partitionen sind jeweils quasi mit dem gleichen Inhalt. Die beinhalten das Route-Dateisystem, nämlich Go Crazy, also das Userland und die Anwendungen, die man drauf gepackt hat. Das ist auch ein FAT-16B-Dateisystem, aber eigentlich nur, weil wir jetzt schon den FAT-16B-Code hatten und dann gesagt haben, na gut, dann nehmen wir das auch, der Linux-Kanal kann davon starten, dann ist das gut. Da wollen wir aber vielleicht noch auf SquashFS wechseln oder so, mal schauen. Das sind deswegen zwei Partitionen, weil wir einen AB-Mechanismus benutzen für das Route-Dateisystem. Das heißt, wenn man standardmäßig erst mal das Image schreibt, dann startet er von Partition 2. Wenn man ihm dann ein Image aktualisiert, eine Update-Funktion, dann wird das auf die Partition 3 geschrieben und dann wird ein Reboot gemacht in der startenden Partition 3 und dann immer umgedreht. Er wechselt quasi immer hin und her. Das ist deswegen nötig, weil die erste Partition vom Raspberry Pi Bootloader in den Speicher geladen wird, aber die Route-Partition wird eben nicht komplett in den Speicher geladen, sondern wird auch beim Benutzen noch weiter gebraucht. Daher kann man nicht quasi unter dem Kernel weg einfach diese komplette Partition austauschen, das führt zu Chaos. Der SD-Karte wird in der vierten Partition verwendet für persistente Daten. Man kann sich entschließen, gar keine persistente Datenpartition zu haben, wenn man das nicht braucht. Man könnte ansonsten, sofern man das möchte, ein beliebiges Dateisystem verwenden, was der Linux Kernel unterstützt. Meine Wahl wäre zum Beispiel in X4, aber spricht auch nichts gegen BataFS oder was auch immer ihr gerne benutzen möchtet. Wenn man sich das jetzt genau überlegt, ich habe gerade gesagt, der FAT16B-Code ist nur ein Schreibcode, und die CommandLand.txt ändern können, mit der wir angeben, in welche Route-Partition gestartet werden soll, müssen wir natürlich auch einen Reader haben, der uns das Offset dieser Datei gibt, damit wir die quasi überschreiben können. Das heißt, wir haben auch eine ganz minimalen FAT16B-Reader, der aber wirklich nur für diesen Zweck verwendet wird. So, jetzt habe ich beschrieben, wie das Ganze auf die SD-Karte kommt und was in den Partitionen ist, aber wo kommen die Inhalte eigentlich her? Die Firmware, von der ich mehrfach gesprochen habe, lebt in einem Repository auf GitHub namens github.com.go-crazy-slash-Firmware. Das sind im Wesentlichen Dateien, die von einem anderen GitHub-Repository kommen, nämlich von dem Raspberry Pi-Firmware-Repository. Dort gibt es einen Unterordner-Boot und dort gibt es Dateien, nämlich diese Elf-Bin-Dat-Dateien, das sind Propreterter-Binaries, die die Hardware initialisieren und dann den Linux-Körnel starten. Es ist ein Projekt in Arbeit, welches eine freie Firmware implementiert. Den Link habe ich hier auf die Folie gepackt, allerdings zu dem Zeitpunkt, wo ich mir das schon mal auf den Kanal habe, ist die Freifirmware noch ohne Unterstützung für USB, DMA und Ethernet dahergekommen, ist also leider noch nicht einsatzfähig für unseren Anwendungsfall. Der Kernel hingegen lebt in einem Repository, welches Kernel heißt, kommt von kernel.org und es gibt in diesem Repository ein Build-Programm, welches in Go geschrieben ist, also auch das komplette Tooling ist nicht etwa in einem Shellscript oder so, sondern alles in Go geschrieben und dieser Kernel wird dann reproduzierbar mit einem ARM64-Architektur kompiliert, das ist das, was der Raspberry Pi 3 verwendet. Wir benutzen dafür einen Docker-Container, damit wir eine fixe Umgebung vor uns haben, die man auch ganz einfach replizieren kann, zum Beispiel auf Travis, ein Continuous Integration Service oder auf anderen Computern, wenn Leute damit entwickeln wollen. In diesem Docker-Container machen wir auch gar nichts Spannendes an Setup, sondern wir installieren einfach einen Debian Stretch und in Debian Stretch gibt es ein hübsches Paket namens Cross-Build Essential und eine komplette Toolchain, mit der man für ARM64 kompilieren kann. Es ist super angenehm, wenn ihr Cross-Compiled und das noch nicht kennt, dann sei ich das hier mit ans Herz gelegt. Das Resultat von diesem Programm ist dann ein Formlinux-Körnel-Abbild, eine Raspberry Pi 3b.dtb Device-Tree-Datei, das quasi eine Konfigurationsdatei für den Kernel und eine Init-RD haben wir derzeit nicht, einfach weil ich keine Notwendigkeit dafür sehe. Könnte man theoretisch machen, aber wozu? Hier ist der Quelltext dieses Programmen. Das einzige, was hier ausgelassen wurde, sind die Änderungen an der .config-Datei, wo wir sagen, wir brauchen folgende Module, damit wir den Kernel bekommen, der auf dem Raspberry Pi tatsächlich hardware unterstützt, die dort vorhanden ist. Aber im Wesentlichen ist es nichts anderes als ein MakeDev-Config für die Default-Config, dann Config-Endern, OldDev-Config, damit das auch alles schön eingepasst wird. Dann rufen wir Make auf Image.gz, machen das Ganze parallel und diese Umgebungsvariablen, die wir hier setzen, sorgen dafür, dass der Kernel reproduzierbar gebaut wird. Das heißt, dass das Resultat, wenn ich den Kernel auf meinem Computer baue, bit für bit identisch ist, wie wenn irgendjemand von euch den Kernel auf deinem Computer baut, egal auch zu welcher Uhrzeit usw., es ist wirklich reproduzierbar. Das heißt, wenn auch irgendjemand nicht die vorgefertigten Binarys nehmen möchte, die wir zur Verfügung stellen, dann ist das auch super, dann könnt ihr einfach euch den Quelltext holen und dasselbe übersetzen, geht auch. Wir funktionieren jetzt die Updates. Ich hatte ja angedeutet, dass die alle automatisch laufen sollen und wir haben dafür uns ein bisschen Automatisierung ausgedacht. Wir haben auf Travis nicht nur Konfigurationen, die reaktiv auf PullRequest und neue Commits reagiert, sondern Travis unterstützt auch CronJobs. Ich habe hier links zu den Definitionen auf die Folie gepackt. Und was diese CronJobs machen, ist, die gehen täglich her und schauen sich an, ob es eine neue Version gibt, nachdem entweder in dem upstream Git Repository für die Firmware oder eben auf kernel.org gibt es so einen JSON-File mit den Metadaten, was der aktuell unterstützte stable kernel ist. Und sofern sich da was geändert hat, dann gehen die her und nehmen die Buildscripts, in denen nämlich die Versionsnummer auch drin steht, damit das Ganze reproduzierbar ist und ändern die Versionsnummer z.B. von Linux 4.10 auf 4.11. Ich kann ja auch hier mal solche PullRequest im Hintergrund Hier haben wir z.B. einen Auto Update auf Linux 4.10.6. Das war eines der ersten, also der erste PullRequest, den wir hier haben. Und wir sehen, der PullRequest-Inhalt ist einfach 14.3 auf 4.10.6. Was dann passiert, ist, da wird also ein PullRequest aufgemacht und Travis reagiert auf PullRequest. Das heißt, es geht her und sagt, okay, hier ist ein PullRequest, ich baue den mal und im Falle der Firmware, im Falle des neuen Kernels, baut er tatsächlich den neuen Kernel. Das Resultat davon wird dann in den PullRequest eingearbeitet. Das heißt, es quasi in den Git commit amend und dann ein Force push und dann wird der PullRequest aktualisiert mit entweder den neuen benähern Firmware-Dateien oder eben dem Kernelabbild und diese Änderung an den PullRequest wiederum triggert noch mal Travis, weil da hat sich ja jetzt was geändert, da muss es noch mal neu testen. Das heißt, da gibt es noch keine Resultate in dem PullRequest drin. Das heißt, das einzige, was jetzt noch übrig bleibt, ist, einen tatsächlichen Test zu machen auf echter Hardware. Wie das funktioniert, ist, dass ich zu Hause einen Raspberry Pi habe, der sozusagen als Opfergabe läuft und der wird einfach aktualisiert mit, was auch immer, da als PullRequest reinkommt. Wenn der nach der Aktualisierung noch ordentlich startet und auf der Serialen Konsole eine Erfolgsmeldung ausspuckt, das Resultat von dieser Infrastruktur ist, dass ich gelegentlich aufwache und in meiner Inbox eben einen E-Mail-Thread habe und wenn der drei Updates hat, dann weiß ich genau, okay, das ist der Vorschlag, das zu aktualisieren, das ist der erfolgreiche Boot-Test und das ist die erfolgreiche Merch-Meldung und sofern da nicht drei Updates in dem Thread sind, dann muss ich mich hinsetzen und schauen, okay, was ist da jetzt schief gegangen. Üblicherweise funktioniert das sehr gut. Ich hatte, glaube ich, bislang zwei Updatefehler oder so und üblicherweise mache ich irgendwann auf und das System hat sich vollständig selbst aktualisiert auf die neue Version. Das heißt, wenn ich manchmal auf Heise lese an, neuer Kernel kam raus, schaue ich nach, ah ja, den benutze ich schon. Das ist echt gut. Als Zusammenfassung für das Go Crazy Projekt also die Leitfragen, die ich vorhin aufgestellt habe, können wir wie folgt beantworten. Die Angriffsfläche haben wir stark reduziert. Wir sind runter auf nur vier Komponenten und eben alle Anwendungen, die ihr so draufpackt, aber das waren ja nicht vorbei. Und alles ist tatsächlich in Go implementierbar und implementiert. Das heißt, speichersichere Sprache, alles super. Die Updates komplett automatisch, wie gerade gezeigt, sehr bequem und die Installation von Software auf einem Raspberry Pi super einfach. Man hat jetzt einfach einen Befehl für die Installation auf einer frischen SD-Karte, wie wir es vorhin gezeigt haben in der Demo. Wenn man möchte, kann man diesen Befehl auch benutzen, um den vorgefertigtes Image zu erstellen, statt es direkt auf die Karte zu schreiben, um das zu archivieren möchte oder da eine komplizierte Infrastruktur hat. Und man kann auch direkt über diese URL, die ich vorhin gezeigt habe, nicht nur das Web-Interface aufmachen, sondern auch neue Versionen auf den Pi aufspielen. Jetzt behandeln wir noch ein paar Anwendungsfälle und dann haben wir noch Zeit für Fragen und Antworten. Ein Anwendungsfall, den ich zu Hause habe, ist Media-Automatisierung. Ich habe einen AV-Receiver und an den angeschlossen sind verschiedene Medienquellen und das, was es hier geht, ist überwacht verschiedene Computer in meinem Heimnetz, über z.B. Pings oder komplizierter Mechanismen und Geräte wie Chromecasts und schaut dann an, wo wird gerade irgendwas abgespielt und schaltet dann automatisch die passenden Geräte ein und um und so weiter, passt die Pegel an und alle solche Sachen. Das klingt ganz ähnlich wie HDMI, CEC, aber funktioniert natürlich auch für Geräte, die nicht über HDMI angeschlossen sind, z.B. über Toslink und das macht das Ganze sehr bequem. Das heißt, ich kann einfach vorher was kasten und dann macht es klick, klick, klack und alles funktioniert einfach automatisch. Der zweite Anwendungsfall, den ich habe, ist Hausautomatisierung. Ich habe dafür im Einsatz eine Lösung von Homematic. Vielleicht kurz Handzeichen, wer von euch benutzt auch Homematic-Geräte? Die sind relativ verbreitet in Deutschland, aber ich sehe kaum Hände. Vielleicht uns nachher unterhalten, was ihr benutzt, sofern ihr was anderes benutzt. Die Geräte wurden mir damals empfohlen, das tut einfach und das stimmt auch auf der Hardware-Ebene. Die verwendenden Protokoll, das funktioniert auf den gleichen Frequenzen wie SIGBI, aber ist ein eigenes, properitäre Protokoll. Und das einzige, was mich an diesem ganzen System massiv gestört hat, ist die Software. Das System wird nämlich ausgeliefert mit einer sogenannten Central Control Unit. Das ist einfach so ein kleines Embedded Device. Da kann man sich auch rein SSH'en, hat man sogar Routzugriff drauf, sehr bequem. Das ist so ein Homematic drauf und das steuert dann diese ganzen Geräte an. Diese Software-Lösung war so grauenvoll für mich, dass ich mich entschlossen habe, das komplett neu zu implementieren. Der Grund dafür ist, dass nicht nur die ganze UI furchtbar schrecklich ist, also im Sinne von, man klickt auf einem Button und man wartet fünf Sekunden und dann kriegt man vielleicht ein Antwort. Und es ist kompliziert zu verstehen für mich und das ist ja schön und gut und da kann man ja sagen, komm, stelle dich nicht so an, im Normalfall funktioniert das doch einfach. Aber der Punkt ist, ich wollte eben die Daten auch abgreifen und Langzeit archivieren, so was wie Sensorwerte, so Temperaturfeuchtigkeiten, so Sachen. Allein schon diese Daten daraus zu holen, das funktioniert per se, aber ich habe dann festgestellt, dass irgendwie alle zwei Wochen mein Exporter auf einmal nicht mehr funktioniert. Und ich dachte, warum denn? Und dann habe ich mich, als das mal wieder passiert ist, auf die Central Control Unit eingeloggt und habe gesehen, im Syslog, dass da der Umkiller einfach das komplette von dem Hersteller weggeschossen hat. Und das passiert, reproduziere bei alle zwei Wochen. Und der Hersteller hat auf meinen Bug Report einfach gar nicht geantwortet. Also, das ist keine tragbare Lösung und dann habe ich mich umgeschaut, wie könnte man das denn machen und stellt sich raus, die haben quasi ein kleines Hardware-Modul, welches man auf den Raspberry Pi aufstecken kann und dann hat man da Zugriff auf diese Funkschnittstelle und kann einfach über die seriale Schnittstelle dort kommunizieren. Für diese seriale Schnittstelle, also für das Protokoll, was die benutzen und auch das proprietary Protokoll und die Geräte anzusprechen, gibt es verschiedene freie Software-Implementation. Eine in C++, eine andere in Pearl. Und es gibt natürlich auch die proprietary Lösung, die man auch einfach tracing kann, dass man schauen kann, was die macht. Ich habe das auf meiner Website ein bisschen genauer beschrieben, wie ich da vorgegangen bin, falls Leute die Details interessieren. Und dann habe ich das also nachimplementiert und der Clue ist, dass ich absichtlich verzichtet habe auf die üblichen Sachen, die Software groß und kompliziert und unverständlich machen, nämlich Konfigurierbarkeit und User-Interface. Das heißt, wo andere Heimautomatisierungslösungen da hergehen und irgendwie Fans-User-Interfaces haben, wo man eine halbe Stunde braucht um festzustellen, wie man seinen Schalter mit der Lampe verbindet. Habe ich das einfach alles gar nicht, sondern ich habe einfach ein paar kleine Libraries, die direkt mit dieser Hardware sprechen und der Anwendungsfall, den ich habe, ist einfach hardcoded in einem kleinen Go-Programm. Also ich habe es quasi gescriptet, aber in Go. Und das funktioniert soweit ziemlich gut. Ich habe das seit einigen Wochen im Einsatz. Man sieht auf der Folie hier in den Screenshot davon, man hat hier so Sachen wie wann sich die verschiedenen Geräte, die ich in meinem Haus habe, zuletzt gemeldet haben und der Batteriestand und solche Sachen und eben auch die Sensorwerte. Und als kleines Schmankerl hier oben, die Größe im Speicher, die ist persistent bei ungefähr 11 Megabyte, also nichts mehr mit Umkiller alle zwei Wochen. Die komplette Auswertung und das Monitoring von dem System funktioniert es über Prometheus. Das ist ein System über das ich hier auch schon gesprochen habe, was auch in Go geschrieben ist. Und das Dashboard hier ist mit Grafana gemacht. Der dritte Anwendungsfall, den ich habe, ist ein bisschen einfacher gestrickt. Das sind Backups. Ich habe dann ein Programm namens Don Dröschchen. Das steuert Rechner und Network Storages. Nämlich immer wenn ich also ins Büro gehe, dann weckt er die Rechner auf und weckt die Network Storages auf und lockt sich dann in die Rechner ein und stößt über SSH ein Backup an. Das läuft einfach über AirSync, also recht einfache Lösung. Das macht nichts weiter. Ganz einfach. Der letzte Anwendungsfall ist deutlich komplizierter. Da geht es um das Ein-Scan von physikalischen Dokumenten, also Briefe und solche Sachen, die einem Leute schicken. Und was ich da habe, ist ein Fujitsu ScanSnap EX500. Das ist so ein Einzugscanner. Da kann man also duplex Seiten einfach durchziehen lassen. Das heißt, wenn ich einen Brief bekomme, dann möchte ich den einfach reinlegen. Alle Seiten drücken Knopf und dann soll der automatisch gespeichert werden und idealerweise auch noch mit Volltextsuche zur Verfügung stehen. Ich habe mir damals diesen Fujitsu Scanner gekauft, nicht nur weil er super Testberichte hatte und von der Qualität her super sein soll und so weiter, sondern auch weil er eine Cloud-Anbindung hatte. Aber stellt sich heraus, was Fujitsu unter Cloud-Anbindung versteht, ist, dass man nicht diesen Scanner hatte und ihn einmal konfiguriert. Nein, man hat diesen Scanner und jedes Mal, wenn man ein Dokument in die Cloud scannen will, dann muss man sich eine mobile App installieren und dann einen Knopf drücken oder eben an den PC gehen und dort einen Knopf drücken. Das ist für mich auch ein guter Problem, weil ich das vollkommen indiskutabel für mich. Außerdem benutzt er also nicht direkt zum Beispiel Google Drive, wenn man als Ziel Google Drive einstellt, sondern der benutzt per se erstmal einen Fujitsu Cloud Service, wo die Daten erstmal hochgeladen werden und von dort aus gehen sie dann in zum Beispiel Google Drive. Das ist für mich auch keine gute Lösung und deswegen wollte ich eine Lösung haben, die das Ganze einfach direkt macht. Also die, wenn ich diesen Knopf drücke, einfach losläuft und sagt, man sieht auf der Folie das Webinterface dafür, aber im Wesentlichen ist der eigentliche Use Case, dass man diesen Knopf drücken kann und auch mit dem Webinterface gar nichts zu tun haben muss. Der scannt also die Dokumente ein, verschönert und verkleinert die JPEGs in den PDF. Das PDF muss klein genug sein, damit Volltextsuche noch funktioniert, das wird dann auf Google Drive hochgeladen und dann ist das fertig. Ich habe da morgen auch noch einen Lightning Talk dazu, wenn euch das noch mehr interessiert und kann ansonsten auch gerne da noch mehr drüber erzählen. So, jetzt sind wir am Ende angekommen mit dem planmäßigen Inhalt. Ich bedanke mich erstmal für die Aufmerksamkeit. Mehr Infos zu dem Projekt findet ihr unter gocrazy.org. Wenn ihr Feedback zum Vortrag habt, würde mich das sehr freuen. Ich habe entweder den Link hier draufgepackt oder wenn ihr jetzt auch gleich das schon mal euren Browser aufmachen könnt, könnt ihr den QR-Code scannen. Jetzt haben wir noch Zeit für Fragen. Dankeschön. Hier vorne. Du hast den Update-Mechanismus von den Gocrazy erklärt. Wie aktualisierst du deine Anwendungen, die auf den Raspberry Pi laufen? Nicht nur der Update-Mechanismus an sich. Die Antwort ist, derzeit funktioniert es so, dass ich auf einem anderen Computer, also nicht auf dem Pi selber, einem Programm Zeitgesteuert laufen habe, also einfach einen Cronjob, der jeden Wochentag da hergeht und sagt, okay, ich nehme jetzt die verschiedenen Go-Repositories, die hier beteiligt sind, also Kerneln und Firmware, aktualisiert die und spielt dann einfach nochmal ein komplett neues Image auf den Pi auf übers Netz. Und das funktioniert soweit auch sehr zuverlässig. Es ist aber ein Projekt in Arbeit, mit dem das Ganze self-hosted funktionieren soll, so dass man nicht mehr noch ein Dritt-Computer braucht, sondern dass der Pi sich dann auch selber aktualisiert. Aber das ist noch am Anfangs-Stadium. Ja. Du hast ja gesagt, die Kerneln sind reproduzierbar. Die Frage ist, ist der Rest auch reproduzierbar und nicht nur der Kernel? Ich glaube ja, aber es kann sein, dass man da vielleicht noch ein Flyg angeben müsste, damit irgendwie alle Timestamps auch letztendlich raus sind. Ich habe das nicht ernsthaft mehr angeschaut. Du kannst ja gerne mal schauen. Und wenn es irgendwas gibt, was wir ändern müssen, dann können wir das gerne tun. Die Frage war, ob ich in diesem CronJob, der die Sachen aktualisiert, auch die Go-Libraries, also die Abhängigkeiten von meinen Programmen automatisch aktualisieren. Die Antwort lautet im Moment nein. Da gibt es auch, je nachdem, was man so philosophisch für einen Stand fährt, zum Beispiel die Option, die Abhängigkeiten zu wendern, das heißt quasi zu kopieren in das eigene Projekt. In dem Fall könnte man sie aktualisieren, so viel man will. Sie würden immer auf der gleichen Version gepinnt bleiben, bis man sie manuell aktualisiert. Ich habe quasi einen Verfahren, was genau das Gleiche bewirkt, aber halt kein Wendering verwendet, formell. Ich lasse also einfach meinen Workspace so, wie er ist, was auch zur Folge hat. Und deswegen mache ich das auch, dass ich entwickeln kann und mir der Updatemechanismus nicht irgendwie meinen Entwicklungsstand zerschießt. Also ich baue nicht darauf, wie ich das selber machen. Und es wird immer nur Firmware und Kernel vollautomatisch aktualisiert. Und wenn ich den Rest aktualisieren will, muss ich das selber machen. Aber das ist quasi eine Policy-Entscheidung, die du persönlich machen kannst, wie du möchtest. Weitere Fragen. Ansonsten bin ich auf die ganze Veranstaltung über noch da. Wenn euch noch was einfällt, könnt ihr mich gerne ansprechen. Ich freue mich auf eure Diskussion. Und dann sage ich nochmal, vielen Dank.