IOException.de

Icon

Ausgewählter Nerdkram von Informatikstudenten der Uni Ulm

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:

 

System-Aufbau

 

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.

Kategorie: linux, security

Tags: , ,

Diese Icons verlinken auf Bookmark Dienste bei denen Nutzer neue Inhalte finden und mit anderen teilen können.
  • MisterWong
  • Y!GG
  • Webnews
  • Digg
  • del.icio.us
  • StumbleUpon
  • Reddit
  • Facebook

Ein Kommentar

  1. matou sagt:

    Witzige Idee! Wäre nicht darauf gekommen git für Updates zu verwenden. Gefällt mir sehr gut.

Kommentar