zur Übersicht: Linux: meine Software und Konfigurationstipps

statische Grub-Konfiguration

21.06.2012

digitale Signatur dieser Webseite

  1. Motivation und Ziel

  2. Umsetzung

  3. Erweiterung: Fallback-Kernel

Motivation und Ziel

Bei manchen Systemen ist es sehr wichtig, dass sie nicht wegen vermeidbarer Probleme ausfallen; dazu gehört auch, dass ein Rechner nicht mehr automatisch durchbootet. Als Maßnahme in diesem Zusammenhang habe ich an anderer Stelle die Einrichtung einer eigenständigen Serviceinstallation (eigene Partition) empfohlen. Es besteht aber ein Restrisiko, dass beim Neuschreiben des Bootloaders irgendwas schiefgeht. Wenn der Bootloader (Grub) stehenbleibt, können – mit Glück: Wenn stage2 geladen werden kann und nur das Menü kaputt ist – Leute mit Sachkenntnis innerhalb von Grub die Bootparameter korrigieren. Spaß macht das nicht. Und auch in diesem Fall dauert das womöglich etwas; man ist ja normalerweise diesbezüglich nicht im Training.

Die Lösung besteht darin, Grub zweistufig zu benutzen: Die erste Grub-Installation wird "nie" verändert. Sie ruft entweder die Grub-Konfiguration des Standardsystems oder die der Serviceinstallation auf. Die Betriebssysteme werden so konfiguriert, dass sie Grub nur in ihrer /boot- oder Root-Partition (oder wo auch immer, jedenfalls nicht im MBR) konfigurieren. Wenn die Grub-Konfiguration des Hauptsystems zerstört wird, kann man immer noch problemlos das Rettungssystem starten und von dort aus das Problem lösen.

Umsetzung

Die folgende Beschreibung bezieht sich auf ein System mit openSUSE 12.1. Auf anderen Systemen mag es Abweichungen im Detail geben, aber auch dort sollte man diese Anleitung ohne große Probleme umsetzen können. Da diese Version von openSUSE standardmäßig noch die alte Version von Grub (0.97) verwendet, bezieht sich das folgende darauf. Die Vorgehensweise ist bei Grub2 sicherlich ähnlich.

Ich weiß nicht, ob das zwingende Voraussetzung ist, aber ich habe eine kleine Partition (wenige MiB) für die statische Grub-Installation reserviert. In diese Partition habe ich den Inhalt von /boot kopiert (nicht alles, nur den Symlink boot (der auf das Verzeichnis zeigt, in dem er sich befindet, also auf ".") und das Verzeichnis grub). Da Grub diese Partition lesen können muss, sollte sie mit ext2 oder ext3 formatiert sein.

Es geht vermutlich auch eleganter (mittels des Parameters --root-directory), aber das habe ich noch nicht ausprobiert. Wenn man eine eigene /boot-Partition hat, muss man diese aushängen. Wenn man auf Nummer sichergehen will, dass gerade kein Kernelupdate läuft, kann man für diese Aktion in Runlevel 1 oder 2 schalten und ggf. noch den cron beenden (und danach mit ps prüfen, dass kein Update läuft). Dann mountet man die für den statischen Grub reservierte Partition (in meinem Fall sda11) auf /boot. Es gibt dann ein Verzeichnis /boot/grub, allerdings nicht auf demselben Volume wie vorher. Mit folgendem Kommando lässt man den Grub auf dieser Partition aus dem MBR starten:

grub-install /dev/sda

Unter openSUSE muss man das Kommando allerdings als grub-install.unsupported aufrufen, aber das wird einem im obigen Fall auch mitgeteilt. grub-install /dev/sda kann man dort nur verwenden, wenn es dafür einen Eintrag in der Datei /boot/grub/device.map gibt, also etwa so:

(hd0) /dev/sda

openSUSE verwendet allerdings standardmäßig in dieser Datei die Geräte-IDs:

(hd0) /dev/disk/by-id/ata-WDC_WD5000BPVT-22HXZT1_WD-WXF1A90J2616

Man kann diesen Eintrag auf /dev/sda ändern oder den ID-Pfad verwenden. Ebenso kann man statt dessen das Ziel in der Grub-Notation angeben, also (hd0).

Für den Fall, dass grub-install scheitert (wie etwa bei mir in einer virtuellen Maschine (KVM; openSUSE 12.1; virtio-Plattentreiber, also /dev/vda statt /dev/sda), gibt es eine andere Möglichkeit der Installation, die Grub-Shell. Das ist mehr oder weniger der Code des Bootloaders (aus dem heraus man ja eine Shell starten kann), und irgendwie funktioniert das anders, so dass die Installation jedenfalls in meinem Fall auf diesem Weg gelungen ist. Gestartet wird die Shell über den Befehl grub (oder grub --device-map=/dev/null):

device (hd0) /dev/vda
root (hd0,0)
setup (hd0)
quit

Über einen anderen setup-Parameter kann der Bootloader statt in den MBR in eine Partition installiert werden, also etwa setup (hd0,6) für /dev/sda7.

Anschließend muss noch die Grub-Konfigurationsdatei (bei openSUSE ist das /boot/grub/menu.lst) auf dieser Partition angepasst werden. Es gibt zwei Möglichkeiten, den Grub der zweiten Ebene zu starten:

  1. Man lädt die Konfigurationsdatei (innerhalb der laufenden Grub-Instanz).

  2. Man startet den Bootloader von einer Partition. Das funktioniert ganz unabhängig davon, dass es sich wieder um Grub handelt. In diesem Fall beendet sich der Grub der ersten Ebene und startet den der zweiten komplett neu. Das scheint etwas länger zu dauern (warum auch immer, vielleicht eine absichtliche Verzögerung). Ob es gewichtige Argumente für die eine oder die andere Variante gibt, weiß ich nicht.

    Inzwischen habe ich gelesen, dass es sinnvoll sein kann, die Chainloader-Variante zu verwenden, wenn man unterschiedliche Linux-Distributionen im Einsatz hat, weil die angeblich gerne an Grub herumpatchen, so dass die einzelnen Grub-Installationen nicht zueinander kompatibel sein müssen. Ich gebe das mal ohne Wertung wider; ich habe so meine Schwierigkeiten zu glauben, dass Grub-Patches sich auf den Inhalt (bzw. desse Kompatibilität) der Konfigurationsdatei auswirken sollen. Anders sieht es natürlich aus, wenn unterschiedliche Grub-Versionen (Hauptversionen, nicht bloß Patchlevel) in den beiden Ebenen verwendet werden.

Das Ergebnis kann dann so aussehen:

default 0
timeout 2
password --md5 $1$bzRX90$xP52izb...xxxxxxxxxxxxxxxxxxxx

title Standardsystem (openSUSE 12.1 -- sda7, configfile)
    root (hd0,6)
    configfile /grub/menu.lst

title Standardsystem (openSUSE 12.1 -- sda7, Chainloader)
    root (hd0,6)
    chainloader +1

title Recovery (openSUSE 12.1 --  sda2, configfile)
    lock
    root (hd0,1)
    configfile /grub/menu.lst

title Recovery (openSUSE 12.1 --  sda2, Chainloader)
    lock
    root (hd0,1)
    chainloader +1

Auf diese Weise kann man beide installierte Systeme jeweils in beiden Varianten starten. Der Befehl lock sorgt dafür, dass diese Einträge nur nach Eingabe des Grub-Passworts aufgerufen werden können. Ob diese Einschränkung sinnvoll ist, hängt von den Umständen ab. Wichtiger ist, dass alle drei Grub-Installationen mit einem Passwort gesichert sind, so dass Unbefugte nicht in Software die Kernelparameter ändern können. Den Grub-Konfigurationen der beiden Linux-Installationen kann man dann noch so einen Eintrag hinzufügen, damit man ohne Verrenkungen zwischen den drei Menüs wechseln kann, wenn man sich mal bei der Auswahl vertippt hat:

title Grub Hauptmenue (MBR-sda11 static)
    rootnoverify (hd0,10)
    configfile /grub/menu.lst

Die Festlegung, dass der Bootloader nur in die /boot- oder Root-Partition geschrieben werden soll, erfolgt bei openSUSE in der Datei /etc/sysconfig/bootloader.

Erweiterungen

Fallback-Kernel

Eine weitere sinnvolle Absicherung des Bootvorgangs (die von der o.g. Grub-Konfiguration unabhängig ist) besteht darin, den Installationskernel (mit den Modulen) zu behalten, anstatt ihn beim ersten Kernelupdate löschen zu lassen, oder ihn später erneut zu installieren. Das hat zwei Vorteile:

  1. Wenn beim Kernelupdate (oder der anschließenden Grub-Konfiguration: openSUSE schreibt sie nach jedem Update auf die neuen Dateinamen um) irgend etwas schiefgeht, kann der Ersatzkernel aus einer dauerhaft unveränderten Konfiguration (im obigen Beispiel: vom Grub der ersten Ebene) gestartet werden (ggf. passwortgeschützt, um das Ausnutzen von Sicherheitslücken zu verhindern).

  2. Wenn man doch einmal von der Installations-DVD booten muss (oder vom Servicesystem per chroot in das Standardsystem wechselt), hat man alle Kernelmodule für den laufenden Kernel parat. Die könnte man theoretisch auch vorher laden, aber dafür muss man wissen, welche benötigt werden; außerdem dauert das eine Weile.

Der zugehörige Eintrag könnte so aussehen, unter der Annahme, dass vmlinuz.fallback und initrd.fallback sinnvollerweise Links auf die richtigen Dateinamen sind:

title Standardsystem (openSUSE 12.1 -- sda7, Fallback-Kernel)
    lock
    root (hd0,6)
    kernel /vmlinuz.fallback root=/dev/linux/rootfs splash=silent quiet showopts vga=0x317
    initrd /initrd.fallback