08/05/2012

Comment faire un Initram minimal avec mode rescue

Aujourd’hui Geekfault vous propose d’apprendre à construire vous même votre initram, comme un grand.
On ne parlera pas de splash ou autre eyecandy pour newbies, juste de choses utiles pour nous autres les geeks.
Et on vous expliquera surtout comment ça marche, afin que vous puissiez devenir un Guru de la fabrication d’initram.

Mais au fait, c’est quoi un initram ?

Un initram est un fichier contenant un mini système sur lequel boot le kernel.
Son utilisation la plus commune est de servir d’étape pour lancer le vrai système, c’est ce cas que nous verrons.

Étapes d’un boot typique avec initram

  • La machine démarre.
  • Le BIOS se lance, lit sa conf, et passe la main au boot-loader Grub présent sur le MBR.
  • Grub lit sa conf, copie en RAM le kernel et l’initram puis passe la main au kernel.
  • Le kernel s’initialise, détecte le hardware puis passe la main à l’initram.
  • L’initram fait son boulot et passe la main au vrai système, par exemple Gentoo.
  • Gentoo s’initialise et vous demande de vous logger.

Dans quel cas l’utiliser ?

Si le système est directement accessible par le kernel, un initram ne sert à rien, le kernel y arrivera tout seul.
En revanche si le système n’est pas directement accessible (raid, cryptage, lvm, etc), alors il faut un initram pour y accéder.

Autopsie d’un initram

Un fichier d’initram n’est en fait qu’une archive cpio. cpio étant un format similaire à tar, mais plus basique.
Dans cette archive on retrouve une arborescence de fichier similaire à celle d’un système classique (/dev, /etc, /bin, /lib, etc)
Il doit aussi y avoir le fichier init à la racine de l’arborescence, c’est à ce fichier que le kernel passe la main et c’est presque toujours un script shell.
On ne laisse évidemment dans cette arborescence que le strict nécessaire si bien qu’il ne reste souvent que /bin, /init et un ou deux autres dossiers.
On peux également compresser ce fichier avec gzip, bzip2 ou xz si le kernel le supporte.

Voila pour un rappel de la technologie.

Initram vs initrd

On confond souvent les deux car ils servent à la même chose mais l’initrd est en fait le grand-père de l’initram.
L’initrd est un fichier de taille fixe, formaté en ext2, copié en RAM et monté comme un disque dur.
Alors que l’initram est une archive cpio dont le contenu est copié dans du tmpfs, (taille dynamique, pas besoin de formater).

Bref l’initrd est à l’initram ce que le Tam-tam est à Internet: un ancêtre archaïque.

Construire son initram

Pour éviter de rester vague, prenons un exemple concret: une Gentoo sur une partition cryptée avec dm-crypt, le tout sur Raid 1 software.
En bonus, et pour le même prix, on rajoutera un mode rescue.

Il va nous falloir les ingrédients suivant:

  • Un shell pour exécuter le script /init
  • Un moyen de remplir /dev pour avoir /dev/sda1, etc
  • L’exécutable mdadm pour monter le Raid
  • L’exécutable cryptsetup pour décrypter
  • Un moyen de passer le clavier en français pour taper le mot de passe
  • Un moyen de passer en mode rescue
  • Un script /init pour orchestrer tout ça

On mettra tout ces ingrédients dans un dossier appelé DOSSIER et on verra plus tard comment transformer ce dossier en initram.

Shell: Busybox

Busybox est une sorte de couteau suisse qui fournit les commandes de base (sh, cd, ls, rm, mount, …), tout cela contenu dans un seul petit exécutable. Il contient bien évidement un shell.

Il existe deux méthodes pour accéder aux commandes de Busybox:

  • l’appel direct
    $ busybox ls -la /etc
  • le lien symbolique
    $ ln -s busybox /bin/ls
    $ ls -la /etc

On ajoute Busybox à notre initram:

$ cd DOSSIER
$ mkdir bin
$ cp `which busybox` bin/
$ ln -s busybox bin/sh

Busybox est disponible sur toutes les distribution et, sauf exception, est toujours compilé en static.
Vous pouvez vérifier avec la commande:

$ file `which busybox`
/bin/busybox: ELF 64-bit LSB executable, x86-64, statically linked

Si il est dynamically linked alors il faut soit se débrouiller pour avoir une version statically linked, soit inclure dans l’initram (dans /lib) les librairies dont il a besoin (utiliser la commande ldd pour avoir la liste).
Cela est valable pour tout exécutables que vous ajoutez à votre initram.

/dev: Busybox

La encore, busybox a ce qu’il faut: mdev, un mini udev.
mdev marche différemment de udev qui pour rappel est un service qui tourne en permanence.
mdev à l’inverse est à lancer quand on en a besoin, et on en a besoin de deux façon:

  • pour remplir /dev avec tout ce qu’a trouvé le kernel jusque là
    $ mdev -s
  • pour que /dev soit mis à jour chaque fois que le kernel trouve un nouveau périphérique
    ln -s busybox /bin/mdev
    echo '/bin/mdev' > /proc/sys/kernel/hotplug

On utilisera mdev dans le script /init.

mdadm et cryptsetup

Pour vous faciliter la tache il vaut mieux les avoir en static sinon il faut inclure les librairies avec.

On ajoute mdadm et cryptsetup à notre initram:

$ cp `which mdadm` bin/
$ cp `which cryptsetup` bin/

On les utilisera dans le script /init

Clavier Français: Busybox

Décidément ce Busybox fait tout !
En fait il ne sait pas vraiment configurer le clavier en français mais il sait sauver la configuration actuelle dans un fichier.

On crée le fichier avec:

$ cd DOSSIER
$ mkdir etc
$ busybox dumpkmap > etc/kmap-fr

Évidement il faut que le clavier soit déjà correctement configuré pour que cette opération ait un sens.
On peux ensuite charger le fichier avec loadkmap, mais nous verrons cela dans le script /init.

Rescue

On a donc maintenant un shell, le clavier peut etre configuré, on a mdadm et cryptsetup, en plus de ça busybox est plein d’outils divers (ip, lspci, wget, ps, etc, …). tapez busybox pour voir la liste.
Il y a donc tout ce qu’il faut pour un rescue, autant en profiter.
Le mode rescue sera tout simplement de lancer un shell pour donner la main à l’utilisateur.

On pourra y arrivera de deux manières:

  • le script /init rencontre une erreur et lance un shell.
  • On rajoute “rescue” aux paramètres du kernel et on configure le script /init pour qu’il lance un shell dans ce cas.

On vera tout ca dans le script /init.

Pour info, rajouter un paramètre au kernel peux se faire directement depuis Grub:

  • attendez de voir grub
  • appuyez sur une touche pour arrêter le compte à rebour
  • appuyez sur e, comme edit, vous verez alors plusieurs lignes, dont celle commencant par kernel
  • appuyez a nouveau sur e et ajoutez rescue à la fin de cette ligne
  • appuyez sur escape pour sortir du mode edition
  • appuyez sur b, comme boot, pour booter

La modification n’est pas permanente.

Script /init

Le gros morceau, c’est lui qui fait le boulot.

#!/bin/sh

# on monte /sys et /proc
# certains programmes en ont besoin et ça coûte rien
mkdir /proc /sys
mount -t proc proc /proc
mount -t sysfs sysfs /sys

# On crée /dev et on le remplit avec mdev
mkdir -p /dev
mount -t tmpfs none /dev
ln -s busybox /bin/mdev
echo '/bin/mdev' > /proc/sys/kernel/hotplug
mdev -s

# On charge la config clavier pour pouvoir taper le mdp correctement
loadkmap < /etc/kmap-fr

# on lance le shell si il y a "rescue" dans les paramètres du kernel
grep -w rescue /proc/cmdline && exec sh

# on bloque l'output du kernel le temps de taper le mot de passe
PRINTK=`cat /proc/sys/kernel/printk`
echo 0 > /proc/sys/kernel/printk

# On laisse mdadm se débrouiller pour monter le RAID
# On lui donne juste les dev possible et l'UUID du RAID
# L'UUID peut être obtenu avec la commande "mdadm -D /dev/mdX"
echo 'DEVICE /dev/sd[abcd][2]' > /etc/mdadm.conf
echo 'ARRAY /dev/md2 UUID=62121:a6a45:34d17:37d8e' >> /etc/mdadm.conf
mdadm --assemble --scan || exec sh

# on décrypte (il demande donc le mdp)
cryptsetup luksOpen /dev/md2 vault || exec sh

# on monte la partition décryptée
mkdir /newroot
mount -r /dev/mapper/vault /newroot || exec sh

# on sauve les paramètres du kernel pour les donner au vrai init
CMDLINE=`cat /proc/cmdline`

# on nettoie le bordel qu'on a mis
echo "${PRINTK}" > /proc/sys/kernel/printk
echo '' > /proc/sys/kernel/hotplug
umount /dev
umount /sys
umount /proc

# et on passe la main au vrai système
exec switch_root /newroot /sbin/init ${CMDLINE}

Générer l’initam

Ultra simple:

$ cd DOSSIER
$ find . | cpio --quiet -o -H newc | gzip --best > /boot/mon_initram

Vous pouvez évidement utiliser bzip2 ou xz pour compresser, assurez vous juste que votre kernel le supporte.

Et voila, votre initram est terminé.
Il n’y a plus qu’à booter dessus, à vous les kernel panic !

  1. roidelapluie
    | #1

    Personnellement j’utilise dracut, parce que ma version de udev nécéssitait de se trouver intégrée à l’initramfs.

  2. | #2

    Oué oué.
    Tu sais bien qu’on dit déchiffrer pourtant.
    T’en fait n’exprêt qu’a m’énerver.

  3. dacrovinunghi
    | #3

    Excellent bien expliqué et bien synthétisé, bravo.

  4. | #4

    Bien écrit, intéressant et très bien expliqué.

  1. | #1
  2. | #2