15 Mrz, 2011 von Michael Müller
Git als Update-Mechanismus
In diesem Beitrag möchte ich einen einfachen Mechanismus beschreiben, um in einer Client-Server Infrastruktur Updates an die Clients auszuliefern. Ich hatte bei einem Projekt einige Clients in ubiquitären Umgebungen und war dafür auf der Suche nach solch einem System. Die Clients sind einfache Kleincomputer mit einer schlanken Linux-Distribution und müssen ohne technische Wartung auskommen.
Anforderungen an den Update-Mechanismus sind:
- Authentifizierung
Die Verbindung zum Update-Server muss authentifiziert sein.
Das war in den großen Betriebssystemen früher ein großes Problem und ist in vielen Programmen noch immer ein Schwachpunkt. Der Instant Messenger-Exploit Anfang dieses Jahr war etwa ein Beispiel, in dem der Update-Server nicht authentifiziert wurde. - Fallbacks
Falls ein Update schiefgeht sollte es sehr einfach möglich sein den alten Stand wiederherzustellen. Es ist nicht akzeptabel, dass Daten verloren gehen. Alles muss abgespeichert werden. - Scripting
Ich will an jeden Zeitpunkt des Updateprozesses einen Hook setzen können (vor dem Update, danach, etc.). Dies kann später benutzt werden um beispielsweise das Gerät nach dem Einspielen von Neuerungen zu rebooten, um zu überprüfen ob das Update erfolgreich verlaufen ist oder um etwa Datenbankänderungen durchzuführen. - Autorisierung
Der Updateserver darf nicht öffentlich verfügbar sein — Clients müssen sich ihm gegenüber autorisieren um Zugriff auf Updates zu bekommen. Die Option später eine gruppenbasierte License-Policy für verschiedene Softwareversionen nachrüsten zu können soll offengehalten werden.
Ich habe mich letztlich dazu entschieden hierfür git zu verwenden. Die Versionsverwaltung deckt bereits im Vornherein einen großen Teil der Anforderungen ab. Git bot sich an, da ich auch sonst sehr gerne mit dem Tool arbeite und damit also schon umgehen konnte. Der Aufbau meines Systems sieht inzwischen so aus:
Auf der Serverseite
- Webserver, Zugriff nur über HTTPS möglich, selbstsigniertes X.509 Zertifikat.
-
Ein “bare” git repository, geschützt über HTTP-Authentifizierung:
https://updates.foo.net/bar.git
.
Neue Updates werden zu diesem Repository gepusht. -
hooks/post-update: Wird ausgeführt nachdem neue Updates gepusht wurden.
# wird von git für HTTP repositories benötigt git update-server-info # korrekte Zugriffsrechte find ~/bar.git -exec chmod o+rx '{}' \;
Auf der Client-Seite
- Cronjob. Überprüft regelmäßig ob neue Updates vorliegen. check.sh enthält:
#!/bin/sh # unser Update-Repository cd "~/bar.git" # neue Updates fetchen, git legt diese dann in FETCH_HEAD ab git fetch # liegen im origin-Repository neue Änderungen vor? newUpdatesAvailable=`git diff HEAD FETCH_HEAD` if [ "$newUpdatesAvailable" != "" ] then # fallback erstellen git branch fallbacks git checkout fallbacks git add . git add -u git commit -m `date "+%Y-%m-%d"` echo "fallback created" git checkout master git merge FETCH_HEAD echo "merged updates" else echo "no updates available" fi
- Unter hooks/post-merge können Aktionen definiert werden, die nach dem Einspielen der Änderungen ausgeführt werden (Reboot, Validierung des Updateverlaufs, etc.).
Der Client hat eine Kopie des Server-Zertifikats und benutzt dieses um die Verbindung zu authentifizieren. So können die Geräte sicher sein, dass sie zum richtigen Server verbunden sind. Die passende ~/bar.git/.gitconfig
sieht in etwa so aus:
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = https://user@updates.foo.net/bar.git [http] sslVerify = true sslCAInfo = ~/server.crt [core] askpass = ~/echo_your_https_authorization_pw.sh
Was sind Nachteile dieses Aufbaus?
Ich habe mich bewusst für ein Versionierungssystem entschieden, da meine wichtigste Anforderung ist, dass die Geräte auf keinen Fall Daten verlieren. Dies ist gleichzeitig auch ein Nachteil des Systems, da letztlich alles archiviert wird kann dies bei Geräten mit geringem Speicher kritisch sein. Ein ähnliches Problem tritt auf, falls häufig ein großer Teil des Systems geändert werden muss. In diesem Fall würde eine Lösung mittels rsync oder ähnlichen Tools eventuell mehr Sinn machen. rdiff-backup führt etwa inkrementelle Backups durch und bietet dazu noch die Möglichkeit die Anzahl der Versionen die behalten werden zu beschränken.
Do not use SSH.
Ein einfacherer Weg solch ein System aufzubauen wären SSH-Zugänge mit eingeschränkten Dateirechten für die Clients. Das Problem hierbei ist, dass Geräte Zugriff auf den Server bekommen. Ein Angreifer könnte sich mit dem private key des Clients zum Server verbinden. Es ist nicht aussergewöhnlich, dass in Betriebssystem-Kernen oder in System-Bibliotheken Sicherheitslücken entdeckt werden. Im schlimmsten Fall könnte ein Angreifer so die Dateien innerhalb des Repositories manipulieren. Die Clients würden automatischen den manipulierten Inhalten fetchen. Das manipulierter Inhalt ausgeliefert wird, ist auch in meinem Setup nicht ausgeschlossen, dazu muss der Angreifer aber über einen Zugang zu dem Server verfügen.
Witzige Idee! Wäre nicht darauf gekommen git für Updates zu verwenden. Gefällt mir sehr gut.