Sichere Software entwickeln ist das eine. Genauso wichtig ist es, die Programme auf verlässlichen, nachvollziehbaren Wegen zum Anwender zu bringen. Dieser Artikel zeigt, wie Entwickler RPMs erstellen, signieren, in ein Repository stellen und sie vor Fehlern oder Manipulationen durch Dritte schützen.
Linux lebt von seiner Vielfalt, aber genau die macht es den Entwicklern bisweilen auch schwer, ihre Programme an die Besonderheiten und Unterschiede der einzelnen Distributionen anzupassen. Traditionell veröffentlichen Programmierer deshalb einfach den Quellcode und überlassen es dem Nutzer, diesen auf seinem Computer zu übersetzen, üblicherweise mit dem berühmten Dreisprung »configure; make; sudo make install« .
Dabei passen die Build-Tools die Software – zumindest im Idealfall – automatisch an das jeweilige System an.
Unhandlich: Source Code
Das Vorgehen hat jedoch ein paar Haken: Es ist zeit- und rechenintensiv, erfordert einiges Wissen und funktioniert leider nicht immer problemlos. Deshalb setzten sich in den Neunziger Jahren vorgefertigte Binärpakete durch. Auf Suse- und Red-Hat-Systemen liegen die im RPM-Format [1] vor, was ehemals für Red Hat Paketmanagement stand, heute das rekursive Akronym “RPM Paketmanagement” abkürzt.
Pakete haben viele Vorteile: Sie sind optimal auf die Distribution abgestimmt und lassen sich jederzeit per Knopfdruck installieren, aktualisieren oder auch wieder restlos entfernen. Die Ersteller können Abhängigkeiten definieren, die Paketverwaltung löst diese auf und installiert alles Notwendige, auf Wunsch auch komfortabel über das Internet.
Trügerische Sicherheit
In der Praxis hat dieser Komfort dazu geführt, dass sich viele Nutzer nur mehr wenig Gedanken um die Sicherheit der von ihnen installierten Pakete machen. Angesichts der umfangreichen Features, die das Paketmanagement bietet, ist das jedoch fahrlässig: Die Paketverwaltung ermöglicht es dem Softwareentwickler, bei der Installation Skripte auszuführen, um beispielsweise einen Daemon vor der Deinstallation herunterzufahren oder ihn nach einer Aktualisierung automatisch neu zu starten.
Deshalb verlangen diese Skripte volle Rootrechte und lassen sich zu schädlichen Manipulationen missbrauchen. Immer wieder gab es Angriffe, wo Nutzer Pakete untergeschoben bekamen, die neben dem eigentlichen Inhalt auch böswillige Skripte enthielten.
Bei Dateien aus dem Internet ist also eine gewisse Skepsis angebracht, erst recht, wenn sie nicht über die Paketverwaltung aufs System kommen, sondern via Download und lokaler Installation. Mit dem Kommando »rpm -qp –scripts Paket.rpm« einen Blick auf die Skripte zu werfen, die das Paket bei Installation ausführen will, ist da Pflicht, aber auch nur eine erste.
Prüfsummen und Signaturen
Nahezu alle Distributionen haben die Gefahr erkannt und treffen entsprechende Schutzvorkehrungen. Dabei geht es nicht nur um absichtliche Manipulationen, sondern in erster Linie darum, ein viel häufiger auftretendes Problem zu verhindern: Die Installation beschädigter Pakete, beispielsweise durch einen fehlerhaften Download.
Per Definition enthalten RPM-Pakete in ihrem Header Prüfsummen in den Formaten SHA1 und MD5 sowie Informationen über die Dateigröße. Damit kann der Admin die Integrität eines heruntergeladenen Paketes wirkungsvoll überprüfen. Eine Authentifizierung des Herausgebers ist damit jedoch noch nicht möglich, die Quelle des Paketes bleibt vorerst im Unklaren. Die Integrität eines RPMs prüft das Kommando:
$ rpm --checksig beaver-0.4.1-1.x86_64.rpm beaver-0.4.1-1.x86_64.rpm: sha1 md5 OK
Wer sicherstellen will, dass ein Paket auch wirklich vom angegebenen Herausgeber stammt, greift auf dessen Signatur zurück – seriöse Entwickler signieren ihre Pakete. Genau wie bei E-Mail oder Dateien kommt hier das freie Kryptographiesystem GPG zum Einsatz. Der Entwickler erstellt als erstes ein GPG-Schlüsselpaar, mit dem er seine Pakete anschließend digital signiert. Das Erstellen des Schlüssels erledigt der Befehl »gpg –gen-key« (Listing 1).
Listing 1
gpg –gen-key
01 [...] 02 gpg: enabled debug flags: memstat 03 Bitte wählen Sie, welche Art von Schlüssel Sie möchten: 04 (1) RSA und RSA (voreingestellt) 05 (2) DSA und Elgamal 06 (3) DSA (nur signieren/beglaubigen) 07 (4) RSA (nur signieren/beglaubigen) 08 Ihre Auswahl? 2 09 DSA-Schlüssel können zwischen 1024 und 3072 Bit lang sein. 10 Welche Schlüssellänge wünschen Sie? (1024) 11 Die verlangte Schlüssellänge beträgt 1024 Bit 12 Bitte wählen Sie, wie lange der Schlüssel gültig bleiben soll. 13 0 = Schlüssel verfällt nie 14 <n> = Schlüssel verfällt nach n Tagen 15 <n>w = Schlüssel verfällt nach n Wochen 16 <n>m = Schlüssel verfällt nach n Monaten 17 <n>y = Schlüssel verfällt nach n Jahren 18 Wie lange bleibt der Schlüssel gültig? (0) 365 19 Key verfällt am Mi 18 Apr 2012 05:37:07 CEST 20 [...] 21 gpg: Schlüssel C6B8BAFB ist als uneingeschränkt vertrauenswürdig gekennzeichnet 22 Öffentlichen und geheimen Schlüssel erzeugt und signiert. 23 [...]
Der Anwender wählt Algorithmen, Länge und Verwendungszweck des Schlüssel aus und definiert ein Verfallsdatum. Das ist wichtig, damit Keys, die verloren gehen, automatisch inaktiv werden und nicht für immer als eigentlich nicht mehr gültige Authentifizierungsquelle durchs Web geistern. GPG erstellt anschließend eine User-ID, die den Schlüssel identifizierbar macht, wofür es einige Zufallsdaten benötigt. Dazu verlangt es zufälligen Input an der Konsole oder mit der Maus, bevor es abschließend die Kerndaten des Keys ausgibt.
Den erzeugten öffentlichen Schlüssel lädt der Entwickler mit »gpg –send-key« auf einen der offiziellen Schlüsselserver hoch (Listing 2). Welchen Server er hier verwendet, spielt keine Rolle, da die Server ihre Schlüssel untereinander austauschen.
Listing 2
gpg –send-key
01 $ gpg --keyserver hkp://pgp.mit.edu --send-key C6B8BAFB 02 gpg: enabled debug flags: memstat 03 gpg: sende Schlüssel C6B8BAFB auf den hkp-Server pgp.mit.edu 04 random usage: poolsize=600 mixed=0 polls=0/0 added=0/0 outmix=0 getlvl1=0/0 getlvl2=0/0 06 secmem usage: 0/32768 bytes in 0 blocks
Die Buildumgebung
Im nächsten Schritt geht es daran, eine passende Umgebung für den Bau der RPM-Pakete durch das Kommando Rpmbuild zu schaffen. Entwickler sollten dabei ihre Pakete niemals als Root bauen, denn ein Fehler in einer ».spec« -Datei oder einem Makefile könnten böse Folgen haben. Ganz Vorsichtige legen sogar einen dezidierten Benutzer zum Paketbau an, um ihre Dateien zu schützen.
Ein »mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}« erzeugt die passende Verzeichnisstruktur. Wer davon abweichen und beispielsweise ein anderes Directory als »~/rpmbuild« verwenden möchte, muss Rpmbuild dessen Pfad übergeben. Das erledigt am Einfachsten das Makro »%_topdir« in der Datei »~/.rpmmacros« (Listing 3). Wer will, erweitert bei dieser Gelegenheit seine Konfiguration gleich um nützliche und zum Signieren notwendige Makros [2].
Listing 3
~.rpmmacros
03 %_topdir %(echo $HOME)/linux/rpmbuild 04 %packager Max Mustermann <max.mustermann@example.net> 05 %vendor Max Mustermann 06 %_signature gpg 07 %_gpg_name C6B8BAFB 08 %_hkp_keyserver http://pgp.mit.edu
Ein Paket bauen
In der Build-Umgebung zimmert der Entwickler jetzt sein erstes Paket, beispielsweise eines für den Editor Beaver [3], der hier auch als Beispiel für ein einfaches Programm dient. Die Spec-Datei, die den Paketbau steuert, legt der Paketierer dazu in das Verzeichnis »SPECS« und das Quellarchiv, das er von der Homepage heruntergeladen hat, in das Verzeichnis »SOURCES« . Dann startet »rpm –ba SPECS/beaver.spec« den Bau des Paketes (Abbildung 1).

Abbildung 1: Rpmbuild baut ein Softwarepaket aus einer Spec-Datei. Die lässt sich einfach anpassen und bietet viele Möglichkeiten.
Das Paket lässt sich anschießend mit Rpm problemlos installieren. Leider warnt das Konsolentool nicht wegen der fehlenden Signatur, wohl aber die Paketverwaltungen moderner Distributionen wie Yum (Listing 4), Yast oder Packagekit (Abbildung 2). Während Packagekit immerhin den Anwender noch einmal um Eingabe des Rootpasswort bittet, verweigert Yum die Installation kategorisch, sofern die Überprüfung des GPG Schlüssels nicht mit der Option »–nogpgcheck« deaktiviert ist.
Listing 4
yum localinstall
01 # yum localinstall /home/chris/rpmbuild/RPMS/x86_64/beaver-0.4.1-1.x86_64.rpm 02 Geladene Plugins: langpacks, presto, refresh-packagekit, 03 Adding de_DE to language list 04 Einrichten der lokalen Paketverarbeitung 05 Untersuche /home/chris/rpmbuild/RPMS/x86_64/beaver-0.4.1-1.x86_64.rpm: beaver-0.4.1-1.x86_64 06 Markiere /home/chris/rpmbuild/RPMS/x86_64/beaver-0.4.1-1.x86_64.rpm zum Installieren 07 Löse Abhängigkeiten auf 08 --> Führe Transaktionsprüfung aus 09 ---> Paket beaver.x86_64 0:0.4.1-1 markiert, um installiert zu werden 10 --> Abhängigkeitsauflösung beendet 11 Abhängigkeiten aufgelöst 12 ===================================================================== 13 Paket Arch Version Repository Grösse 14 ===================================================================== 15 Installieren: 16 beaver x86_64 0.4.1-1 /beaver-0.4.1-1.x86_64 448 k 17 Vorgangsübersicht 18 ===================================================================== 19 Install 1 Package(s) 20 Gesamtgrösse: 448 k 21 Installed size: 448 k 22 Ist dies in Ordnung? [j/N] :j 23 Lade Pakete herunter: 24 Paket beaver-0.4.1-1.x86_64.rpm ist nicht unterschrieben

Abbildung 2: Package Kit warnt den Anwender eindringlich davor, unsignierte Pakete zu verwenden, und verlangt erneut das Admin-Passwort.
Um diese Warnung loszuwerden, sind zwei Schritte notwendig: Der Paketbaumeister muss das Paket signieren [4] und der Benutzer den öffentlichen Schlüssel in sein System integrieren. Als erstes signiert der Ersteller das Paket mit dem Befehl »rpm –addsign« .
$ rpm --addsign rpmbuild/RPMS/x86_64/beaver-0.4.1-1.x86_64.rpm Bitte das Passwort eingeben: Das Passwort ist richtig. rpmbuild/RPMS/x86_64/beaver-0.4.1-1.x86_64.rpm:
Der Befehl akzeptiert auch Wildcards, was es möglich macht, mehrere Pakete gleichzeitig kryptografisch zu unterzeichnen, ohne das Passwort erneut von Hand eingeben zu müssen.
Im Prinzip reicht ein einzelner Ordner mit signierten Paketen für Downloads aus, aber erst wenn dazu eine bestimmte Verzeichnisstruktur kommt, sprechen Entwickler von einem Repository. Vor dem Aufsetzen des “Repos” sollte sich der Admin Gedanken darüber machen, welche Distributionen und Versionen er bedienen will. Das folgende Beispiel bietet Pakete in 32 und 64 Bit für Fedora 14 an und veröffentlicht auch die passenden Quellpakete dazu. »mkdir -p repo/fedora/14/{i386,x86_64,sources}« erstellt die Ordnerstruktur, in der die fertigen Pakete landen.
Repository bauen
Damit die Paketverwaltung die Pakete erkennt, extrahiert »createrepo« einige Metadaten aus dem RPMs, zum Beispiel den Namen, die Version oder die Abhängigkeiten. Creatrepo ruft der Entwickler einfach im Verzeichnis mit den Paketen auf, es legt die Metadaten in dem Unterordner »repodata« ab:
$ cd ~/repo/fedora/14 $ for i in i386 x86_64 sources ; do > cd $i > createrepo . > cd .. > done
Createrepo darf der Admin aber erst nach dem Signieren des Paketes ausführen, weil sich das Paket durch die Signatur natürlich verändert, ebenso wie seine Metadaten. Im Wurzelverzeichnis des Repositorys hinterlegt er zusätzlich zwei Dateien, die Repository-Beschreibung und den Schlüssel.
Damit ein Benutzer das Lager in seine Paketverwaltung einbinden kann, braucht es eine ».repo« -Datei. Landet die auf dem Client, zum Beispiel mit Yum im Ordner »/etc/yum.repos.d/« , kann der Admin dort sofort Pakete aus dem Repository installieren. Listing 5 zeigt die ersten Zeilen einer solchen Datei. Dank des Eintrags »gpgkey« weiß die Paketverwaltung nun, welcher Schlüssel zum Signieren der Pakete diente und wo der passende Public Key zu finden ist. Der Paketersteller exportiert ihn mit dem Kommando:
Listing 5
/etc/yum.repos.d/mustermann.repo
01 [...] 05 [max-mustermann] 06 name=Max Mustermann's RPM packages 07 baseurl=http://example.net/repo/fedora/$releasever/$basearch/ 08 enabled=1 09 skip_if_unavailable=1 10 gpgcheck=1 11 gpgkey=http://example.net/repo/RPM-GPG-KEY-Mustermann 12 13 [max-mustermann-sources] 14 name=Max Mustermann's RPM packages - Sources 15 baseurl=http://example.net/repo/fedora/$releasever/sources/ 16 enabled=1 17 skip_if_unavailable=1 18 gpgcheck=1 19 gpgkey=http://example.net/repo/RPM-GPG-KEY-Mustermann 20 [...]
$ gpg --export --armor max.mustermann@example.net > RPM-GPG-KEY-Mustermann
Damit die in der Repo-Datei angegebene URL auch nach dem Upload auf den Server noch stimmt, landet die Schlüsseldatei »RPM-GPG-KEY-Mustermann« direkt im Wurzelverzeichnis des Repository.
Unsichere Schlüssel-Automatik für die Clients
Jetzt können Clients Pakete von hier installieren. Beim ersten Versuch lädt die Paketverwaltung automatisch den Schlüssel herunter und fordert den Benutzer auf, diesen zu prüfen und fragt, ob er ihn installieren will (Abbildung 3). Manuell geht das mit:

Abbildung 3: Die neue Paketquelle ist dem System noch nicht bekannt, beim ersten Zugriff muss der Admin deshalb die Echtheit der Signatur bestätigen.
$ rpm --import http://example.net/repo/RPM-GPG-KEY-Mustermann
Komfortabler, aber auch unsicherer, ist es, ein Paket zu bauen, das Konfiguration und Signaturschlüssel schon enthält. Das hat den Vorteil, dass Anwender den Schlüssel nicht mehr über das Internet herunterladen müssen. Zwar stellt das RPM-Paket jetzt die Datei-Integrität sicher, und der Benutzer ist gegen DNS-Manipulationen gewappnet, aber prinzipiell kann so auch jedermann ein signiertes Paket bauen und veröffentlichen, die Signatur ist praktisch wertlos.
Trotzdem kann das Vorgehen auch nützlich sein: Der Anwender bekommt automatisch Aktualisierungen für die Repository-Konfiguration, zum Beispiel wenn neue Softwareversionen auftauchen. Wer das einrichten will, ersetzt
gpgkey=http://example.net/repo/RPM-GPG-KEY-Max-Mustermann
in der Repository-Definition durch:
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Max-Mustermann
Auch das Spec-File für dieses Paket ist recht trivial, wie Listing 6 zeigt. Der Entwickler könnte sogar noch einen Schritt weiter gehen und den Schlüssel ebenfalls automatisch durch das Paket installieren lassen. Dazu fügt er folgenden Abschnitt zur Spec-Datei hinzu:
Listing 6
mustermnn-release.spec
01 Name: mustermann-release
02 Version: 1.0
03 Release: 1%{?dist}
04 Summary: Max Mustermann's RPM packages
05 Summary(de): Max Mustermanns RPM Pakete
06
07 Group: System Environment/Base
08 License: GPLv2
09 URL: http://example.net/repo
10 Source0: mustermann.repo
11 Source1: RPM-GPG-KEY-Mustermann.asc
12 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
13 BuildArch: noarch
14
15 %description
16 This package contains the repository definiton and signing keys for Max
17 Mustermann's RPM packages.
18
19 %description -l de
20 Dieses Pakete enthält die Informationen und GPG Schlüssel für Max Mustermanns
21 RPM Pakete.
22
23 %prep
24 # nichts zu entpacken
25
26 %build
27 # nichts zu bauen
28
29 %install
30 rm -rf %{buildroot}
31 install -Dpm 0644 %{SOURCE0} %{buildroot}%{_sysconfdir}/yum.repos.d/mustermann.repo
32 install -Dpm 0644 %{SOURCE1} %{buildroot}%{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-Mustermann
33
34 %clean
35 rm -rf %{buildroot}
36
37 %files
38 %defattr(-,root,root,-)
39 %doc
40 %{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-Mustermann
41 %{_sysconfdir}/yum.repos.d/mustermann.repo
42
43 %changelog
44 * Sun Apr 17 2011 Max Mustermann <max.mustermann@example.net> - 1.0-1
45 - Initial release
%post rpm --import etc/pki/rpm-gpg/RPM-GPG-KEY-Max-Mustermann || :
Der vollautomatische Import des Schlüssels ist aber nicht immer erwünscht, eigentlich sollte der Benutzer den Schlüssel ja prüfen und bestätigen, ansonsten hält sich der Sicherheitsgewinn in Grenzen.
Bitte bestätigen
Wenn jetzt der Entwickler noch die Authentizität des Paketes garantiert, das den Schlüssel enthält, ist der Anwender auf der sicheren Seite. Dafür gibt es leider nur den altmodischen Weg über eine Prüfsumme aus beispielsweise SHA1sum, wiederum per GPG-Schlüssel signiert:
$ sha1sum mustermann-release-1.0-1.fc14.noarch.rpm > SHA1SUM $ gpg --textmode --detach-sign --local-userC6B8BAFB SHA1SUMS
Auch die beiden Dateien »SHA1SUMS« und »SHA1SUMS.sig« muss der Admin jetzt zum Download bereitstellen.
Damit wäre theoretisch die Kette des Vertrauens komplett: Ein garantiert authentisches Paket installiert die Konfiguration, die es ermöglicht, weitere authentische Pakete zu installieren.
Fazit
Mit RPM sichere Pakete zu erzeugen ist keine Hexerei. Mit ein paar zusätzlichen Handgriffen lassen sich Pakete signieren und vor Manipulationen auf dem Weg zum Client schützen. Das Verfahren ist einfach und lässt sich mit wenig Aufwand automatisieren.
Enterprise-Distributionen wie SLES und RHEL mit ihren Signaturen bieten hier prinzipiell die gleiche Sicherheit wie die Community-Distributionen, weil die verwendeten Tools identisch sind. Jeder Admin, der selbst Pakete baut, kann – eine gewisse Umsicht vorausgesetzt – ebenso sichere Software bauen wie ein Distributor, solange er die Grundregeln beachtet. Die Qualität und Länge der Schlüssel liegt beim Eigenbau vollständig in der Hand des Admins, ebenso ist es sein Job, sichere und vertrauenswürdige Kanäle bereit zu stellen, über die die Dateien an den Anwender gelangen.
Genau da liegt aber der größte Vorteil, den die Enterprise-Distributionen für sich verbuchen können: Sie haben zertifizierte und signierte Pakete schon an Bord. Wem die nicht reichen, der muss das Risiko selbst schultern. (mfe)
Infos
- RPM Trac: http://www.rpm.org
- RPM Macros : http://fedoraproject.org/wiki/Packaging/RPMMacros
- Beaver: http://beaver-editor.sourceforge.net
- Pakete signieren: http://www.rpm.org/max-rpm/s1-rpm-pgp-signing-packages.html





