© peshkova - Fotolia.com

Linux Kernel kompilieren

Dieser Beitrag wurde am 14.Dezember 2014 auf die Linux-Kernel-Version 3.14.26 aktualisiert.

Einen eigenen Linux Kernel aus den Quellen zu kompilieren, ist Thema dieses IT Security Blog Beitrags. In einigen Tagen (Update: Beitrag erschienen) werde ich einen weiteren Beitrag veröffentlichen, bei denen ich Möglichkeiten nennen werde, einen "gehärteten" bzw. "gepatchten" Linux Kernel zu kompilieren. Bevor wir uns jedoch mit Themen wie PAX, grSecurtity & Co. auseinander setzen, müssen einige Grundlagen vorhanden sein. Die Quellcodebeispiele werde ich auf Basis eines Debian GNU/Linux Systems (Ubuntu 14.04 LTS) demonstrieren.

Vor- und Nachteile eines eigenen Kernels

Um es vorwegzunehmen - notwendig ist ein eigener Kernel in den meisten Fällen nicht. Mit Hilfe eines eigenen Kernels ist es beispielsweise möglich, diesen exakt an das System anzupassen, daher nur die Module bzw. Treiber zu kompilieren, die auch wirklich für das System benötigt werden. Ein Debian Paket des aktuellen Kernels lässt sich somit auf eine Größe von etwa 6-7 MB reduzieren. Notwendig ist ein selbst kompilierter Kernel beispielsweise auch dann, wenn dieser für mehr Sicherheit, zur Systemhärtung, gepatcht und angepasst werden soll.

Die Konfiguration erfordert etwas Erfahrung, der größte "Nachteil" ist die etwas schwerere Wartbarkeit. Ein Update des selbst kompilierten Kernels aus den Paketquellen der Distribution ist dann nicht mehr ohne weiteres möglich.

Eine Wiederherstellung bzw. Rückkehr zum originalen Kernel der Distribution ist zu jedem Zeitpunkt ohne Probleme möglich.

Erster Schritt: Quellcode des Vanilla Kernels downloaden

Der Vanilla Kernel ist der originale Kernel, also der Kernel, der nicht durch die Maintainer der Distributionen (z.B. Ubuntu) gepatched oder verändert wurde.

Der aktuelle Kernel kann von der Webseite https://www.kernel.org/ heruntergeladen werden.

Linux Kernel.org Webseite
Linux Kernel.org Webseite (abgerufen am 14.Dezember 2014)

Im Screenshot sind verschiedene Versionen sichtbar. Der letzte "stable" Kernel im Bild ist der 3.18. Für eine leichtere Wartbarkeit, insbesondere dann, wenn dieser gepatcht werden soll, ist ein Longterm Kernel sinnvoll. Ich werde die folgenden Schritte am Beispiel des Kernels 3.14.26 demonstrieren, da es für diesen ein stabilen grSecurity Patch gibt, was in einem weiteren BLOG Beitrag von mir relevant werden wird.

Heruntergeladen wird hierbei zum einen der Kernel im tar.xz gepackten Format, zum anderen die pgp Signatur. Die pgp Signatur verifiziert nicht die tar.xz, sondern nur die tar Datei, die mit xz komprimiert wurde.

Das Kompilieren aus dem Quellcode heraus bietet nebenbei das Sicherheitsplus, dass der Quellcode mit der kompilierte Version übereinstimmt. Der Download eines Open Source Programms bedeutet noch lange nicht, dass die kompilierte Version ohne Modifikationen aus dem öffentlichen Quellcode erzeugt worden ist!

Am Ende dieses Schrittes sollten auf dem System zwei Dateien vorhanden sein:

  • linux-3.14.26.tar.xz (Der Quellcodes des Kernels)
  • linux-3.14.26.tar.sign (Die Signatur der linux-3.14.26.tar)

Download validieren

Die Unversehrtheit und Integrität des Downloads kann mit folgendem Befehl überprüft werden:

# Download entpacken
unxz '/pfad/zur/datei/linux-3.14.26.tar.xz'

# Download verifizieren
gpg --verify '/pfad/zur/datei/linux-3.14.26.tar.sign'

Das Ergebnis sollte eine Ausgabe ähnlich dieser sein:

gpg: Unterschrift vom Fr 02 Aug 2013 23:07:57 CEST mittels RSA-Schlüssel ID 6092693E
gpg: Korrekte Unterschrift von »Greg Kroah-Hartman (Linux kernel stable release signing key) <greg[at]kroah.com>«
gpg: WARNUNG: Dieser Schlüssel trägt keine vertrauenswürdige Signatur!
gpg:          Es gibt keinen Hinweis, daß die Signatur wirklich dem vorgeblichen Besitzer gehört.
Haupt-Fingerabdruck  = 647F 2865 4894 E3BD 4571  99BE 38DB BDC8 6092 693E

Für eine Verifizierung sollte der Schlüssel des Inhabers importiert werden, weitere Informationen zu diesem Thema finden sich auf der Webseite Linux kernel releases PGP signatures.

System für die Kompilierung des Kernels vorbereiten

Ich verwende im Folgenden gcc in der Version 4.9, welches bei Ubuntu 14.04 standardmäßig nicht mitgeliefert wird. Die Version findet sich für Ubuntu im folgendem Respository: PPA for Ubuntu Toolchain Uploads (restricted)

Mit dem Befehl

sudo apt-get -y install patch bin86 kernel-package bc build-essential libncurses5-dev gcc-4.9-plugin-dev g++-4.9

werden alle notwendigen Pakete zum Erzeugen eines Kernels auf dem System installiert.

Download entpacken

Um uns im folgenden das ein oder andere "sudo" und "chown" sparen zu können, wechseln wir in der Konsole in eine Rootshell, bei der alle folgenden Befehle mit Rootrechten ausgeführt werden, bis diese mit "exit" verlassen wird:

sudo -i

Das Vorzeichen in der Kommandozeile wechselt hierbei von "$" auf "#".

Weitere Schritte:

# Arbeitsverzeichnis anlegen
mkdir /usr/src/linux/

# Ins Arbeitsverzeichnis wechseln
cd /usr/src/linux/

# tar Datei kopieren
cp /pfad/zur/datei/linux-3.14.26.tar ./linux-3.14.26.tar

# tar Datei entpacken
tar -xvf linux-3.14.26.tar

An dieser Stelle möchte ich einige Anmerkungen zum Format "tar.xz" schreiben, da ich häufig nach den Unterschieden gefragt werden. Tar bietet die Möglichkeit, Dateien sequenziell, also hintereinander, in eine einzige Datei zu schreiben bzw. Dateien aus dem TAR-Archiv wieder herzustellen. Im Falle von tar.gz bedeutet dies beispielsweise, dass die Dateien erst mit tar gepackt worden sind, anschließend wurde das tar Archiv mit gz komprimiert:

tar.gz erklaert

Linux Kernel anpassen

Eine gute Ausgangsbasis für den eigenen Kernel ist die aktuelle Kernelkonfiguration, diese befindet sich im Ordner /boot und hat meist einen Namen ähnlich der "config-x.yy.zz-generic", also zum Beispiel "config-3.2.0-51-generic". Diese wird in den aktuellen Ordner kopiert:

# Aktuelle Konfiguration übernehmen
cp /boot/config-$(uname -r) ./.config

Ist die Kernelversion der kopierten Konfiguration niedriger oder wird der Kernel aktualisiert, ist jetzt folgendes Kommando notwendig:

make oldconfig

An dieser Stelle kann der Kernel mit folgendem Befehl konfiguriert und angepasst werden:

make menuconfig

Es erscheint hierbei folgendes Konfigurationsmenü:

linux kernel kompilieren menuconfig

Einige wichtige Tastaturbefehle:

# Die Beschreibung oder Tipps zum aktuell ausgewählten Punkt anzeigen
?

# Durch die verschiedenen Punkte navigieren
Left/Right (⬌) Up/Down (⬍) PgUp/PgDn

# Menükonfiguration beenden oder das Kommando abbrechen (2x die Esc-Taste drücken)
Esc Esc

# Kommando auswählen / aktivieren oder Untermenü anzeigen
Enter (⏎)

# Kernelfeature in den Kernel kompilieren
y

# Kernelfeature als Modul kompilieren
m

# Kernelfeature nicht kompilieren
n

Um ausschließlich Module zu kompilieren, die aktuell (!) vom System benutzt werden, kann folgender Befehl genutzt werden:

make localmodconfig

Dieser Befehl ist auch die Ausgangsbasis, für einen schlanken, an das System angepassten Kernel. Wichtig ist hierbei, dass alle USB-Geräte, etc. zu dem Zeitpunkt eingesteckt sind, da sonst die Module für diese nicht mitkompiliert werden. Insbesondere bei Servern ein interessanter Befehl. Eine Liste aller aktuell geladenen Module kann mit dem Befehl

lsmod

aufgerufen werden. Um die Module nicht als Kernel-Modul, sondern direkt in den Kernel zu kompilieren, kann folgender Befehl genutzt werden:

make localyesconfig

Vorteile dieser Möglichkeit: Das dynamische Nachladen von Kernelmodulen kann in der menuconfig deaktivieren werden, was einen guten Grundschutz gegen Rootkits bietet. Etwa 90% der bekannten Rootkits versuchen, dynamisch Kernelmodule nachzuladen!

[ ] Enable loadable module support  --->

Für weitere Optimierungen können wir nun noch einen Blick in die Make-Datei werfen:

nano Makefile

In der Datei gibt es folgenden Abschnitt

# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
# But warn user when we do so
warn-assign = \
$(warning "WARNING: Appending $(1)")

ifneq ($(KCPPFLAGS),)
        $(call warn-assign,CPPFLAGS)
        KBUILD_CPPFLAGS += $(KCPPFLAGS)
endif
ifneq ($(KAFLAGS),)
        $(call warn-assign,AFLAGS)
        KBUILD_AFLAGS += $(KAFLAGS)

Weitere Optimierungen sind hierbei in der Zeile KBUILD_CPPFLAGS möglich:

# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
# But warn user when we do so
warn-assign = \
$(warning "WARNING: Appending $(1)")

ifneq ($(KCPPFLAGS),)
        $(call warn-assign,CPPFLAGS)
        KBUILD_CPPFLAGS += $(KCPPFLAGS) -march=native -pipe -mtune=native
endif
ifneq ($(KAFLAGS),)
        $(call warn-assign,AFLAGS)
        KBUILD_AFLAGS += $(KAFLAGS)

Ergänzt habe ich 3 Parameter:

  • -pipe: Verringert die Zeit, die zum Kompilieren benötigt wird, auf Systemen mit viel Arbeitsspeichert signifikant
  • -march=native: Optimiert den Kernel auf die aktuelle CPU
  • -mtune=native: Passt den Kernel an das System an, ist nur dann sinnvoll, wenn der Kernel ausschließlich auf dem gleichen System genutzt wird.

Den Kernel kompilieren

Im letzten Schritt wird der Kernel kompiliert:

make-kpkg clean

# Mit mehreren Threads gleichzeitig kompilieren (Anzahl der CPU Kerne)
export CONCURRENCY_LEVEL=4

# Kernel im "Debian Style" übersetzen
make-kpkg --initrd --append-to-version "Bezeichnung_eurer_Wahl" kernel_image

Nach dem Abschluss findet sich im übergeordneten Verzeichnis ein deb-Paket, was mit dem Befehl

sudo dpkg -i Name_der_datei.deb

installiert werden kann.

Kompilierungsfehler bei deaktiviertem "Loadable Module Support"

Bei deaktiviertem "Loadable Module Support" bricht die Kompilierung unter Ubuntu 14.04 LTS mit folgendem Fehler ab:

The present kernel configuration has modules disabled.
Type 'make config' and enable loadable module support.
Then build a kernel with module support enabled.

make[1]: *** [modules] Fehler 1
make[1]: Verlasse Verzeichnis '/usr/src/linux/linux-3.14.26'
make: *** [debian/stamp/build/kernel] Fehler 2

Zur Lösung des Problems mit dem Befehl

grep -R "*CONFIG_MODULES $(CONFIG_FILE)"

die Kernel-Rulefiles durchsuchen und prüfen, wo dieser Ausdruck verwendet wird.

Anschließend in den Fundstellen die folgenden Ausdrücke ersetzen:

Den Text

grep -E ^[^\#]*CONFIG_MODULES $(CONFIG_FILE)

ersetzen mit

grep -E ^[^\#]*CONFIG_MODULES[^_] $(CONFIG_FILE)

Anschließend sollte sich der Kernel fehlerfrei kompilieren lassen.

Bildnachweis:

  • Beitragsbild: © peshkova - Fotolia.com

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert