Freie Software lebt von den Beiträgen unzähliger Entwickler. Welchen Weg durchläuft Code vom Entwickler bis zum Endkunden und wie wird geprüft? Das Linux-Magazin hat bei Ubuntu und Suse nachgefragt.
Als Debian-Entwickler ist Michael Vogt seit dem Jahr 2000 aktiv und seit 2004 zudem als Core-Developer bei Ubuntu [1]. Er arbeitet an Programmierprojekten wie Apt, Python-apt, Snappy, Synaptic oder Unattended-Upgrades.
Debian und Ubuntu
Bekommt Vogt Code vom Kontributor geschickt, beginnt er mit dem Sicherheitsprogramm. “In meinen Upstream- Projekten lege ich Wert auf Codereview, jedes Patch sieht ein weiterer Entwickler Zeile für Zeile durch. Insbesondere bei Projekten wie Apt wird hier natürlich ein besonderes Augenmerk auf die Sicherheit gelegt.” Damit sind laut Vogt vorab die Low-Level-Prüfungen nach Format Strings und Buffer Overflows gemeint. Bei Apt kommen auch Static-Analysetools wie etwa Coverity und Clang Analyzer zum Einsatz. Es gäbe auch aktive Code-Audits.
Aber auch das Design nimmt er in Augenschein und prüft, wo die Daten liegen und was mit ihnen geschah, bevor sie per Hash oder Signatur geprüft wurden. Auf den Schultern der Maintainer der Distribution liegt laut Vogt eine besonders hohe Verantwortung.
Code von Upstream
Für den Download des Codes von Upstream gibt es für die Prüfung der Hashes und Signaturen keine starren Regeln, lässt Vogt wissen, da jedes Upstream-Projekt anders arbeite. Idealerweise signiere der Upstream-Entwickler seinen Tarball per GPG und sein Schlüssel befinde sich im Web-of-Trust des Maintainers. Dann könne der Distro-Maintainer nach dem Download per GPG prüfen, ob alles stimmt.
Alternativ sei ein Announcement per Mailingliste mit dem Hash des Upstream-Tarball und einer Signatur der Mail ebenfalls ein gangbarer Weg – ebenso wie der über Git mit den Signaturen für das Release-Tag. Jede Release ohne Signatur oder Hash sei dagegen problematisch.
Vertrauen in Upstream
Woher weiß der Maintainer, dass er Upstream vertrauen kann? Vogt hält diese Frage besonders bei kleinen und neuen Projekte für nicht so einfach zu beantworten. Der Maintainer müsse zunächst Code-Inspektion betreiben, um herauszufinden, ob er dem Upstream von dort vertrauen kann. Bei Ubuntu finde zusätzlich eine formale Security-Review des Security-Teams statt, bevor eine Software nach »main« wandern darf.
Der Distributions-Maintainer prüfe dann beim ersten Paketieren den Code zumindest stichprobenartig. Feste Regeln für neue Versionen gäbe es nicht und besonders bei großen Projekten sei es kaum praktikabel, das gesamte Diff durchzusehen. Bei den erwähnten kleineren und neuen Projekten könne allerdings ein Durchsehen der Diffs erfolgen, auch um Vertrauen zu erarbeiten.
Vom Upstream zum Paket
Bei Ubuntu signiert der Distributions-Maintainer den Code des Pakets per GPG und schickt ihn dann als Sourcecode an die Buildmaschinen, teilt Vogt zum weiteren Workflow mit. Die Buildmaschinen betreue das Canonical-IS-Team, es handle sich dabei um spezielle standardisierte und abgesicherte Umgebungen. Dies gewährleiste, dass jeder Build gleich sei, erläutert Vogt.
Diese Vorgehensweise unterscheide sich zum Teil von der bei Debian. Dort finde in der Regel mindestens ein binärer Build auf der Maschine des Maintainers statt, mit dem Nachteil, dass dann manchmal die Buildumgebung nicht “sauber” sei. Die Builds in Ubuntu seien reproduzierbar, berichtet Vogt, ein Packet, das in 14.04 »chroot« baut, werde in 14.04 immer »chroot« bauen.
Als Nächstes sei zu prüfen, ob die Builds auch binär identisch seien. Vogt nennt das eine besondere Herausforderung, an der einige Projekte derzeit arbeiten. Bestes Beispiel sei dafür Debians Reproducable-Builds-Projekt [2]. Die Schwierigkeit liege darin, dass etwa die Zeit- und Datumsinformationen in den Binaries kodiert seien, was dazu führe, dass der Build nicht Bit für Bit identisch sei. Abhilfe könne hier schaffen, die Zeit auf die Zeit der Release zu setzen. Insgesamt sei die Bit-für-Bit-Reproduzierbarkeit aber ein größeres Problem, so Vogt.
Verifikation
Will ein Nutzer verifizieren, ob das Paket exakt den Code des Upstream enthält, und auch die Änderungen des Distro-Maintainers erkennen, sei dies bei Ubuntu wie folgt möglich: Jeder Source-Upload bestehe aus drei Teilen, dem unmodifizierten Sourcecode, den Ubuntu- und Debian-spezifischen Änderungen sowie einer Steuerdatei mit Hashes und Signatur. Der User könne so per Hashes und Signatur nachvollziehen, dass der Code vom Maintainer hochgeladen und nicht verändert wurde.
Per Hash gegen das Original-Tar-File von Upstream könne er vergleichen, dass es wirklich der Original-Source ist, und mit den Diffs die Änderungen von Ubuntu oder Debian nachvollziehen. Die bestünden oft nur aus den »debian/*« -Metadaten und Informationen, die zum Bauen nötig seien. Ab und zu sind es aber auch Bugfixes und Patches.
Paket-Authentifizierung
Wie wird das Paket als Binary und/oder Source authentifiziert? Wie kann der User sicher sein, dass das Paket, das der Maintainer erzeugt hat, auch das Paket ist, dass er als User übers Internet heruntergeladen hat? Der Source-Upload des Maintainers sei bei Ubuntu per Hash/GPG-Signatur authentifiziert, erläutert Vogt. Die Buildmaschinen erzeugen daraus binäre Pakete. Die lassen sich per »apt« oder »apt-get« runterladen.
Das gesamte Archiv sei signiert, es gäbe eine Datei namens »Release.gpg« , die eine Signatur der Release-Datei enthält, Debian verwendet hier alternativ die Clear-Sign-Datei »InRelease« . In der Release-Datei von Ubuntu finden sich die Hashes (SHA256) der einzelnen Paketdateien, die Metadaten über die individuellen Pakete enthalten. Hier seien auch die SHA256-Hashes der einzelnen Pakete abgelegt. So stelle »apt« sicher, dass jede Datei wirklich zum Ubuntu- oder Debian-Projekt gehöre, und schließe eine Man-in-the-Middle-Attacke aus.
Der GPG-Key, der Inrelease und Release authentifiziere, sei Teil des Installationsmediums, erklärt Vogt, und lasse sich per Paket aktualisieren. Mittels des »Valid-Until« -Headers in einer Release-Datei sei sichergestellt, dass ein Angreifer keine Downgrade-Attacke durchführen könne. Bei solchen Attacken versuche ein Angreifer dem Nutzer ein veraltetes Archiv per Man-in-the-Middle-Attacke zu senden, um das Einspielen von Sicherheits-Updates zu verhindern. Nach der Installation könne der Nutzer bei Debian etwa mit dem Debsums-Programm prüfen, ob es Änderungen gegeben habe.
Grundsätzlich, so Vogt, hänge viel von Codereviews ab und von der jeweiligen Programmiersprache. Im unteren Bereich gelte es, Fehler wie Buffer Overflows zu beachten, im High-Level-Bereich eher Injections. Generell müsse das Augenmerk darauf liegen, nie Daten von außen zu vertrauen und immer so wenig Rechte wie möglich nach dem Motto Privilege Dropping zu vergeben.
Suse und Open Suse
Marcus Meißner ist Maintainer bei Suse und kennt die Auslieferungskette, beginnend beim Paketbauer. Laut Meißner unterscheiden sich Open Suse und Suse Linux kaum, einziger Unterschied sei, dass bei Open Suse der extern sichtbare “Open Buildservice” [3] und eine Community-Mirror-Struktur im Einsatz seien, bei Suse eine interne Buildservice-Instanz und ein kommerzielles Content Delivery Network (CDN) mit vorgeschaltetem Authorization-Framework.
Bei Suse fungiere der Buildservice als zentrale Instanz, lässt Meißner wissen. Die Pakete befinden sich in Projekten. In der Sicht der Packager seien Pakete entpackte Source-RPMs in einer Art SVN-Source-Control-System, das der Buildservice bereitstelle. Projekte enthalten eine Anzahl von Paketen. Die Open-Suse-Distribution [4] sei dabei ein eigenes Projekt mit mehreren Tausend Paketen.
Der Paketbau geht in der Sicht dieser Projekte vonstatten. Dabei lösen die Maintainer Abhängigkeiten und Buildzyklen auf. Ändere sich ein Paket mit einer Abhängigkeit, baue man das Paket neu, wenn diese Abhängigkeit sich ändere, was als Transienter-Rebuild bekannt sei, so Meißner.
Hierarchien
Die Projekte seien hierarchisch aufgebaut, es gäbe etwa die »Open Suse: Hierarchie« oder die »Home: Hierarchie« . Alles Offizielle passiere innerhalb der Open-Suse-Hierarchie, jeder Buildservice-Nutzer greife dort auf seine eigene »home:nutzer« -Hierarchie zurück. Daneben gäbe es weitere Hierarchien für andere Zwecke. Der Projektname spiegle jeweils direkt die Hierarchie wider. Und für jedes Projekt oder ganze Projekthierarchien gäbe es unterschiedliche GPG-Signing-Keys. Die Signing Keys verwalte der Buildservice, den Private Key verwende nur der Buildservice, er lässt sich nicht herunterladen.
Der Buildservice unterliege einem Rechtemanagement, das auf Projektbasis und auch auf Paketbasis arbeite. Dem normalen Packager stehe nur Schreibzugriff auf seine eigene Home-Hierarchie zu. Für die offiziellen Open-Suse-Projekt gäbe es eine kleine Gruppe von Koordinatoren, die diese verwalten oder bearbeiten, so Meißner. Bei Suse seien die Rechte intern jeweils an Teams, die diese Aufgaben wahrnehmen, vergeben.
Der Packager handhabe Pakete generell nur als Source. Das Bauen sei Aufgabe des Buildservice. Das Bauen der Pakete geschehe in jeweils frisch aufgesetzten und direkt nach dem Bauen wieder gelöschten VM-Instanzen in KVM oder Xen auf dem Buildcluster im Rechenzentrum.
Bugfix einspielen
Soll ein Bugfix einfließen, dann nähme der Packager eine Änderung an einem Paket vor oder baue ein neues in seinem Home-Projekt. Er dürfen dabei nur die Sourcen einchecken, erklärt Meißner. Der Buildservice signiere die gebauten Binärpakete direkt nach dem Bauen automatisch. Da jede Projekthierarchie eigene GPG-Keys besitze, könne der Packager keine Pakete etwa mit dem Open-Suse-Key, sondern lediglich mit seinem »home:packager« -Key signieren. Habe der Packager seine Arbeit abgeschlossen, gehen die Sourcen – nicht die Binaries – via Submitrequest an eines der Distributionsprojekte. Der Submitrequest sei einem Pullrequest auf Github ähnlich.
Bei den Distributionsprojekten selbst sei ein limitierter Kreis von Nutzern für das bauen der Distribution und die Updates zuständig. Der Packager selbst habe keine Rechte an den Projekten.
Finale
Nun können die Maintainer den Request vom Packager anschauen und ihn akzeptieren oder ablehnen. Der Buildservice baue das Paket dann erneut. Die Metadaten blieben dabei auf einem konsistenten Stand, so Meißner, auch der Packager könne sie nicht beeinflussen. Der Buildservice signiert das Paket nach dem Bauen. Für die Projekte werde zudem ein Yum-Repository generiert, das mit dem Projekt-Key signiert sei.
Danach lasse der Koordinator die Pakete fixieren und die Qualitätssicherung teste sie dann. Die Fixierung sorge dafür, dass nur getestete Pakete veröffentlicht werden, nicht etwa zufällig neu gebaute. Ist die Qualitätssicherung abgeschlossen, erteile der Koordinator das Release-Kommando, erläutert Meißner. Das Update-Projekt integriere die veröffentlichten Pakete, signiere die Yum-Metadaten neu und stelle die Pakete für die Mirrors bereit.
Sicht des Kunden
Der Suse-Kunde habe auf seiner Seite den Update-Stack integriert, der Yum-Repositories vom Server liest und verarbeitet. Lese der Update-Stack die Repositories, verifiziere er deren GPG-Signatur mit den auf dem System bereits registrierten GPG-Keys (»repomd.xml« ). Diese Keys stammen direkt von der Installations-DVD. Sollte der Update-Stack keine oder unbekannte Keys finden, gibt er Warnungen aus, die explizit zu beachten seien.
Die GPG-signierte »repomd.xml« enthalte Referenzen auf andere XML-Dateien, die mittels SHA256 gesichert sind. Diese enthalten wiederum Referenzen auf die SHA256-signierten RPM-Pakete. “Diese Signaturen werden jeweils geprüft, und wenn sie nicht stimmen, bricht der Update-Stack ab”, versichert Meißner.
Infos
- Ubuntu: http://www.ubuntu.com
- Daniel Stender, “Debian Pakete reproduzierbar bauen”: Linux-Magazin 06/2015. S. 78
- Buildservice: https://build.opensuse.org
- Open Suse: https://www.opensuse.org/de






