Linuxkernel selber bauen

Überblick, Komponenten

ZIEL: Erweiterte Funktionalitäten im Kernel bereitstellen oder z.B. für Sicherheitsanforderungen Funktionen entfernen

Linux ist ein monolithischer Kernel, d.h. Hardware-Treiber sind zu einem gewissen Teil fest integriert. Es besteht aber auch die Möglichkeit, Treiber auf modulare Art nachzuladen. Damit sie zur Laufzeit nachgeladen werden können, müssen allerdings Anschlusstellen vorgesehen werden. Hier drängt sich das Bild vom Schweizer Käse auf:

  • Käse = monolithisches Fertigprodukt

  • Löcher = Anschlusstellen für nachladbare Module

../_images/Kernelbau-01_Monolith-mit-Loechern.png

Wichtige Pfade für den Buildvorgang und für den späteren, neu übersetzten Kernel sind:

  • Kernelquellen: /usr/src/linux (ist i.d.R. ein Symlink auf das eigentliche Quellen-Verzeichnis)

  • Kernel und Initrd: /boot

  • Kernelmodule: /lib/modules

Kernel kompilieren

Ein gutes Grundlagendokument ist unter https://wiki.archlinux.org/index.php/Kernel/Traditional_compilation zu finden.

Zu den Release-Bezeichnungen:

Historisch gesehen, trug der letzte Entwicklerkernel die Nummer 2.5 (= letzte ungerade Minor-Nummer), ab Version 2.6 wurde anders verfahren.

Es gibt jetzt offiziell nur noch:

  • Prepatch (Prepatch or „RC“ kernels are mainline kernel pre-releases)

  • Mainline (Mainline tree is maintained by Linus Torvalds. It’s the tree where all new features…)

  • Stable (After each mainline kernel is released, it is considered „stable.“)

  • Longterm (There are usually several „longterm maintenance“ kernel releases provided for the purposes of backporting bugfixes for older kernel trees)

Siehe https://www.kernel.org/releases.html

Kernelbau mit Distributionspaket

Im einfachsten Fall benutzt man die vorgepatchten Kernelquellen der Distribution (Paket linux-source) und nicht die von https://kernel.org, womit eine bessere Kompatibilität zum vorliegenden System gegeben ist. Unter Debian 10 mit den älteren 4-er Kerneln benötigen wir dazu insgesamt die folgenden Pakete:

apt-get install linux-source build-essential libncurses-dev libelf-dev libssl-dev

Soll ein ganz neuer Kernel von https://kernel.org heruntergeladen und gebaut wenden (= Vanilla-Kernel, z.B. 5.10.11), sieht die Zeile etwas anderes aus:

apt-get install build-essential libncurses-dev libelf-dev libssl-dev flex bison

Nach dem Wechseln ins Quellverzeichnis entpacken wir den Tarball mit den Kernelquellen, erstellen einen Symlink namens „linux“ und wechseln weiterhin dort hinein:

$ cd /usr/src
$ tar xf linux-source-4.19.tar.xz
$ ln -s linux-source-4.19 linux
$ cd linux
$ make mrproper     ### Nur anfänglich auszuführen!

In diesem Verzeichnispfad finden nun künftig alle Arbeiten statt. Bevor es ans Auswählen der künftigen Kernelfeatures geht, sollte man sich überlegen, wie schlank und angepasst der Kernel werden soll:

a) Minimal angepasst: Die Konfiguration der Distribution ist Ausgangspunkt, mit make menuconfig braucht im Prinzip nur der CPU-Typ angepasst werden (Vorteil: weniger Konfigurationsaufwand; Nachteile: größerer Kernel, mehr Module, mehr Zeitaufwand beim Kompilieren)

b) Stark optimiert: Die .config-Datei wird mittels make localmodconfig generiert (Vorteile: schlanker Kernel mit wenigen Modulen, schnelleres Kompilieren; Nachteil: größerer Konfigurationsaufwand wegen nicht ausgewählten Systemfunktionen und Treibern)

Je nachdem, für welche Variante man sich entscheidet, sollte man folgendes beachten:

Zu a)

  • Mit cp /boot/config-`uname -r` .config kopiert man sich die Default-Config unter Debian

  • Mit zcat /proc/config.gz > .config tun wir dasselbe unter Arch-, Artix- und SuSE-Linux

  • Mittels make menuconfig stellen wir unter ‚Processor type and features‘ die ‚Processor family‘ ein. ACHTUNG: Unter Debian bitte unbedingt unter ‚Kernel hacking‘ -> ‚Compile-time checks and compiler options‘ die Option ‚Compile the kernel with debug info‘ deaktivieren! Ansonsten dauert der Übersetzungsprozess sehr lange; außerdem wird viel Festplattenplatz beansprucht!! Nach dem abschließenden <Save> wird von diesem Konfigrationsprogramm die Datei .config modifiziert. Achtung: Die Datei darf später nicht manuell editiert werden, weil dabei natürlich keine Abhängigkeitsprüfung möglich ist!

Zu b)

  • Hier kann gleich mit make localmodconfig begonnen werden, wobei nur die vorgefundenen und erkannten Geräte (USB-Devices vorher anstecken!) in die Konfigration aufgenommen werden

  • Mittels make menuconfig muss wiederum weiterkonfiguriert werden, wobei es jetzt deutlich mehr zu tun gibt, so muss neben der oben genannten ‚Processor family‘ z.B. auch folgendes aktiviert werden:

    • Dateisysteme (ISO, vfat, ntfs, …)

    • Blockgeräte (Loopback-Treiber, Multiple Disks, …)

    • Verschlüsselung (z.B. für cryptsetup/LUKS)

    • Netzwerksicherheit (iptables, nftables, VPN, …)

    • Device Driver…

Außerdem bitte wieder bei ‚Kernel hacking‘, … die Option ‚Compile the kernel with debug info‘ deaktivieren! Siehe auch weiter unten bei Wichtige config-Parameter.

Danach geht es endlich mit dem Übersetzungsprozess los, der bei einem ganz frischen Quelltextverzeichnis drei Schritte umfasst: das Bauen des Kernels, der Module und das Installieren der Module nach „/lib/modules/<KERNELVERSION>“:

$ make bzImage
$ make modules
$ make modules_install

Siehe dazu die folgende Abbildung, in der ein erfolgreicher Abschlusss der drei Kompilerschritte jeweils dargestellt ist:

../_images/Kernelbau-das_1.Mal.png

Falls der Kernel ein zweites Mal im selben Quelltextverzeichnis gebaut werden soll, ist vorher ein Cleaning erforderlich; wir haben dadurch einen Schritt mehr:

$ make clean
$ make bzImage
$ make modules
$ make modules_install

Anstelle von clean können auch die make-Targets mrproper und distclean eingesetzt werden. Aber ACHTUNG: die beiden letzteren löschen jeweils immer gründlicher - auch die .config-Datei wird mit entfernt!

Bis zur Kernelversion 2.4 gab es übrigens vor make clean noch einen weiteren Schritt: make dep Er ist mit Version 2.6 entfallen.

Warnung

Möchte man mit dem selben Quelltextverzeichnis weiterarbeiten und wiederholt Kernel mit unterschiedlichen Features bauen, ist es ratsam, die Datei Makefile zu editieren und bei EXTRAVERSION = einen eigenen Bezeichner wie etwa „-myKernel-Nr2“ mitzugeben. Das stellt sicher, dass nicht ein und derselbe Treiber doppelt eingebunden wird: als fest integriertes Builtin und zugleich auch als nachladbares Modul (was sich evl. immer noch im Verzeichnis /lib/modules/<KERNELVERSION> befindet).

Aus einer LPI-Braindump Fragensammlung:

„After unpacking the source code for a Linux kernel, what is the first make command that should be run which will delete any current configuration and all generated files? This command will ensure that no inappropriate files were left in the kernel archive by the maintainer.“

A) make clean      # sanft bereinigen (temp), '.config' bleibt erhalten
B) make mrproper   # hart bereinigen, '.config' wird gelöscht!
C) make distclean  # noch härter bereinigen, '.config' wird ebenfalls gelöscht!
D) make depend     # nur bis Kernel 2.4, außerdem 'make dep' und NICHT 'depend'!
E) make config     # Textbasierter Assistent mit tausend Fragen...

Correct Answer: C

Kernelbau mit Vanilla-Kernel

Distribution: Debian 8 (Jessie)

Vorbereitungen

Downloaden der ungepatchen Originalquellen von https://kernel.org (= Vanilla-Kernel):

$ mkdir /home/src
$ cd /home/src
$ wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.14.51.tar.xz

Installieren von Hilfsprogrammen:

$ apt-get install gcc make automake build-essential gcc-4.9-plugin-dev libncurses5-dev

Kernel im Verzeichnis /home/src entpacken:

$ tar xvJf linux-3.14.51.tar.xz

Symlink erzeugen:

$ cd /home/src
$ ln -s linux-3.14.51 /usr/src/linux

Kernel patchen

Patch von https://raw.githubusercontent.com/kdave/grsecurity-patches/master/grsecurity_patches/grsecurity-3.1-3.14.51-201508181951.patch herunterladen und unter ‚/home/src‘ speichern:

$ cd /home/src
$ wget https://raw.githubusercontent.com/kdave/grsecurity-patches/master/grsecurity_patches/grsecurity-3.1-3.14.51-201508181951.patch

Dann geht es so weiter:

root@deb8:/usr/src# rm linux
root@deb8:/usr/src#
root@deb8:/usr/src# ln -s /home/src/linux-3.14.51 linux
root@deb8:/usr/src#
root@deb8:/usr/src# ls -l
insgesamt 12
lrwxrwxrwx  1 root root   23 Jan 13 09:26 linux -> /home/src/linux-3.14.51
drwxr-xr-x  4 root root 4096 Jan 13 09:25 linux-headers-3.16.0-4-amd64
drwxr-xr-x  4 root root 4096 Okt 20 10:49 linux-headers-3.16.0-4-common
lrwxrwxrwx  1 root root   24 Dez 14  2015 linux-kbuild-3.16 -> ../lib/linux-kbuild-3.16
lrwxrwxrwx  1 root root   17 Okt  4 11:19 linux-OLDVERSION.1475658617 -> linux-source-3.16
drwxr-xr-x 23 root root 4096 Okt  4 13:05 linux-source-3.16
lrwxrwxrwx  1 root root   49 Okt  5 11:11 vboxguest-5.1.6 -> /opt/VBoxGuestAdditions-5.1.6/src/vboxguest-5.1.6
root@deb8:/usr/src#
root@deb8:/usr/src# cd linux
root@deb8:/usr/src/linux#
root@deb8:/usr/src/linux# ls -F
arch/    CREDITS         drivers/   include/  Kbuild   lib/         mm/     REPORTING-BUGS  security/  usr/
block/   crypto/         firmware/  init/     Kconfig  MAINTAINERS  net/    samples/        sound/     virt/
COPYING  Documentation/  fs/        ipc/      kernel/  Makefile     README  scripts/        tools/
root@deb8:/usr/src/linux#

Wir stehen im richtigen Verzeichnis und können den Patch nun anwenden:

$ patch -p1 < /home/src/grsecurity-3.1-3.14.51-201508181951.patch

   Alternativ:

$ cat /home/src/grsecurity-3.1-3.14.51-201508181951.patch | patch -p1

ACHTUNG:

  • Dem Kommando ‚patch‘ kann man den Dateinamen nicht direkt übergeben!

  • Dateien, die beim patchen rejectetd werden, erhalten das Suffix ‚.rej‘

  • Mit der Option ‚-R‘ können Patches rückgangig gemacht werden

Siehe auch http://www.thegeekstuff.com/2014/12/patch-command-examples

Kernel-Konfiguration

ZIEL: Einstellungen vornehmen, Konfigurationsdatei ‚.config‘ erzeugen

Zuerst müssen wir den Prozessortyp ermitteln, diesen dann im Konfig-Programm unter ‚Processor type and features‘, Untermenü ‚Processor family‘ auswählen, danach unter ‚Preemption Model‘ den Eintrag ‚No Forced Preemption (Server)‘ aktivieren und das Programm vorerst beenden, wobei die Einstellungen gespeichert werden müssen:

$ cat /proc/cpuinfo
$ make menuconfig

Konfiguration fortführen

Nachdem wir nun eine ‚.config‘ haben, geht es weiter im Verzeichnis der entpackten Kernelquelldateien. Mit dem neuen make-Ziel localmodconfig ist es viel schneller erledigt, die passenden Module für das aktuell laufende System auszuwählen. Die wichtigsten Module werden dabei automatisch erkannt, später ist nur wenig manuelle Nacharbeit erforderlich. Es werden allerdings nur bereits geladene Module (siehe ‚lsmod‘) berücksichtigt, was bedeutet, dass im Vorfeld alle benötigten USB-Geräte an den Rechner angeschlossen sein müssen!

Danach können manuell weitere gewünschte Einstellungen vorgenommen werden. Für grsecurity macht die „automatische Konfiguration“ auf alle Fälle erst einmal Sinn.

$ make localmodconfig

Taucht hierbei „NEW“ auf, übernehemn wir alles einfach mit ENTER. Das könnte z.B. so aussehen:

root@deb8:/usr/src/linux# make localmodconfig
using config: '.config'
vboxsf config not found!!
vboxguest config not found!!
*
* Restart config...
*
*
* GPIO Support
*
GPIO Support (GPIOLIB) [Y/n/?] y
Debug GPIO calls (DEBUG_GPIO) [N/y/?] n
/sys/class/gpio/... (sysfs interface) (GPIO_SYSFS) [N/y/?] n
*
* Memory mapped GPIO drivers:
*
Generic memory-mapped GPIO controller support (MMIO platform device) (GPIO_GENERIC_PLATFORM) [N/m/y/?] n
IT8761E GPIO support (GPIO_IT8761E) [N/m/y/?] n
F71882FG and F71889F GPIO support (GPIO_F7188X) [N/m/y/?] n
SMSC SCH311x SuperI/O GPIO (GPIO_SCH311X) [N/m/y/?] n
TS-5500 DIO blocks and compatibles (GPIO_TS5500) [N/m/y/?] n
Intel SCH/TunnelCreek/Centerton GPIO (GPIO_SCH) [N/m/y/?] n
Intel ICH GPIO (GPIO_ICH) [N/m/y/?] n
VIA VX855/VX875 GPIO (GPIO_VX855) [N/m/y/?] n
Intel Lynxpoint GPIO support (GPIO_LYNXPOINT) [N/m/y/?] n
*
* I2C GPIO expanders:
*
Maxim MAX7300 GPIO expander (GPIO_MAX7300) [N/m/?] n
MAX7319, MAX7320-7327 I2C Port Expanders (GPIO_MAX732X) [N/m/?] n
PCA953x, PCA955x, PCA957x, TCA64xx, and MAX7310 I/O ports (GPIO_PCA953X) [N/m/?] n
PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders (GPIO_PCF857X) [N/m/?] n
ADP5588 I2C GPIO expander (GPIO_ADP5588) [N/m/?] n
*
* PCI GPIO expanders:
*
BT8XX GPIO abuser (GPIO_BT8XX) [N/m/y/?] (NEW)
AMD 8111 GPIO driver (GPIO_AMD8111) [N/m/y/?] n
Intel Mid GPIO support (GPIO_INTEL_MID) [N/y/?] n
Intel EG20T PCH/LAPIS Semiconductor IOH(ML7223/ML7831) GPIO (GPIO_PCH) [N/m/y/?] n
OKI SEMICONDUCTOR ML7213 IOH GPIO support (GPIO_ML_IOH) [N/m/y/?] n
RDC R-321x GPIO support (GPIO_RDC321X) [N/m/y/?] n
*
* SPI GPIO expanders:
*
Maxim MAX7301 GPIO expander (GPIO_MAX7301) [N/m/y/?] n
Freescale MC33880 high-side/low-side switch (GPIO_MC33880) [N/m/y/?] n
*
* AC97 GPIO expanders:
*
*
* LPC GPIO expanders:
*
*
* MODULbus GPIO expanders:
*
*
* USB GPIO expanders:
*
#
# configuration written to .config
#

Jetzt können gern weitere Schalter de- und aktiviert werden:

$ make menuconfig

Alle für uns wichtigen Einstellungen finden sich im Untermenü ‚Security options‘. Da wir nur grsecurity-Patches wollen, können die anderen, auf die umstrittene LSM-Schnittstelle aufsetzenden Sicherheitserweiterungen in der angegebenen Reihenfolge deaktiviert werden, wobei der Eintrag ‚Socket and Networking Security Hooks‘ dann die LSM-Schnittstelle insgesamt deaktiviert.

Zu deaktivieren ist hierbei:

  • AppArmor support

  • TOMOYO Linux Support

  • NSA SELinux Support

  • Socket and Networking Security Hooks

Nun geht es in das Untermenü „Grsecurity ->“ hinein, wir schalten unser Feature mit der Leertaste ein (‚[*] Grsecurity‘) und wählen als ‚Configuration Method‘ „Automatic“ aus. Danach sollten weitere Anpassungen in den jeweiligen Untermenüs erfolgen.

Wichtige config-Parameter

Stand 03.06.2020, Debian-Sources 4.19.118

Die folgenden Angaben beziehen sich auf die Konfiguration der Kernelquellen per make menuconfig.

Allgemeine Optimierung

Anpassen der CPU-Familie (siehe cat /proc/cpuinfo), Verbesserung der Performance:

> Processor type and features  --->
    Processor family  (Generic-x86-64)  --->
    dort auf jeweilige CPU anpassen...

> General setup --->
    Preemption Model --->
    auswählen von entweder:
        "Preemptible Kernel (Low-Latency Desktop)"
    oder falls der RT-Patch installiert ist:
        "Fully Preemptible Kernel (RT)"

Debian-spezifisches

Kernel ohne Debug-Infos bauen (=> https://www.olimex.com/forum/index.php?topic=5946.0):

> Kernel hacking
    Compile-time checks and compiler options --->
    unbedingt "Compile the kernel with debug info" deaktivieren, ansonsten dauert der
    Übersetzungsvorgang sehr lang und benöigt viel Plattenplatz!
    (Symbol: DEBUG_INFO [=y])

Beim Fehler „System keyring enabled but keys „debian/certs/debian-uefi-certs.pem“ not found“:

> Cryptographic API --->
    Certificates for signature checking --->
    den Dateipfad in
        "Additional X.509 keys for default system keyring" entfernen...

Bei Verwendung von ‚make localmodconfig‘

Für’s mounten von ISO-Dateien, LUKS/cryptsetup und andere Dinge:

> Device Drivers  --->
    Block devices  --->
        "Loopback device support"
        "Cryptoloop Support"
    Multiple devices driver support (RAID and LVM)  --->
        auswählen von:
        "Device mapper support"
        "Crypt target support"
    Multimedia support --->
        Media USB Adapters --->
        für TechnoTrend S2400 auswählen:
            "Support for various USB DVB devices"
            "Pinnacle 400e DVB-S USB2.0 support"

Verschiedenste Dateisysteme aktivieren:

> File systems --->
auswählen von:
    "Inotify support for userspace"
    "FUSE (Filesystem in Userspace) support"
    "Overlay filesystem support"
        CD-ROM/DVD Filesystems --->
            auswählen aller gewünschten/möglichen Optionen...
        DOS/FAT/NT Filesystems  --->
            auswählen aller gewünschten/möglichen Optionen...
        Network File Systems  --->
            auswählen aller gewünschten/möglichen Optionen...

Für LUKS/cryptsetup (=> https://wiki.gentoo.org/wiki/Dm-crypt/de):

> Cryptographic API  --->
    auswählen von:
    "XTS support"
    "SHA224 and SHA256 digest algorithm"
    "AES cipher algorithms"
    "AES cipher algorithms (x86_64)"
    "User-space interface for hash algorithms"
    "User-space interface for symmetric key cipher algorithms"

Für iptables-Firewall:

> Networking support --->
    Networking options --->
        Network packet filtering framework (Netfilter) --->
            Core Netfilter Configuration --->
                    (Hier nun alle gewünschten/erforderlichen Optionen auswählen.)

Sicherheit

Die wichtigen Einstellungen befinden sich im Untermenü „Security options“.

Wer nicht explizit AppArmor, SELinux, SMAC, oder TOMOYO benötigt, kann die umstrittene Schnittstelle für die Linux Security Modules (LSM) namens „Socket and Networking Security Hooks“ insgesamt deaktivieren. Von der Reihenfolge her müssen aber zuerst die darauf aufsetzenden Mandatory Access Control (MAC) modules abgeschaltet werden, zum Schluss erst LSM. Beim Kernel 5.7.0 gestaltet sich dies so:

  1. NSA SELinux Support

  2. Simplified Mandatory Access Control Kernel Support

  3. TOMOYO Linux Support

  4. AppArmor support

  5. Socket and Networking Security Hooks

Zu den Linux Security Modules siehe:

Kernel und Module bauen

$ make bzImage && make modules

Module installieren und Verzeichnisgröße kontrollieren

$ make modules_install

$ du -sh /lib/modules/*
105M    /lib/modules/3.14.51-grsec
101M    /lib/modules/3.2.0-4-amd64

Kernel kopieren und umbenennen, Initrd erzeugen

Wichtig ist dabei, sich den Namen des eben kontrollierten, 105 MByte großen Verzeichnisses, nämlich „3.14.51-grsec“ zu notieren, denn diesen Namen benötigen wir gleich als Suffix zur richtigen Bennenung von Kernel und Initrd, so dass das Kommando ‚update-grub‘ im Anschluss gültige Booteinträge in der ‚/boot/grub/grub.cfg‘ erstellen kann.

Der Name der Kerneldatei muss mit „vmlinuz-“ beginnen, insgesamt dann also „/boot/vmlinuz-3.14.51-grsec“ heißen, die initiale RAM-Disk entsprechend „/boot/initrd.img-3.14.51-grsec“. Die beiden Dateien bekommen wir auf folgende Art an Ort und Stelle:

$ cp arch/x86/boot/bzImage /boot/vmlinuz-3.14.51-grsec
$ update-initramfs -c -k 3.14.51-grsec

Alternativ kann bei vielen Distributionen dieses Kommando verwendet werden: mkinitramfs -k 3.14.51-grsec -o /boot/initrd.img-3.14.51-grsec

Bei Arch/Artix-Linux sieht das etwas anders aus, z.B. so: mkinitcpio -k 5.6.15 -g /boot/initramfs-5.6.15.img

Bootloader automatisch um neuen Kernel erweitern:

Das folgende Debian-Kommando stellt einen Wrapper für grub-mkconfig -o /boot/grub/grub.cfg dar:

$ update-grub

Nun können wir den neuen Kernel testen und das System neu starten, wobei neuere Distributionen automatisch den neuesten Kernel auswählen