Technologie Présentation d'iPXE, un chargeur d'amorçage en PXE

54
23
juil.
2012
Technologie

Il est parfois nécessaire de faire démarrer une machine sur le réseau (par exemple, une machine sans disque dur) : la technologie la plus courante utilisée sur x86 s'appelle PXE et permet l'amorçage depuis un serveur TFTP. La plupart des cartes réseau modernes intègrent une implémentation de PXE, cependant la spécification ne décrit que le protocole susnommé dont les limitations se font vite sentir sur des configurations un peu complexes.

Heureusement pour nous, il existe depuis 1995 un projet libre qui a pour but le développement d'un firmware de carte réseau bien plus flexible que ce que l'on peut trouver actuellement, il s'agit d'iPXE.

Dans la suite de cette dépêche, vous trouverez une explicitation détaillée de l'étape d'amorçage d'une machine ainsi qu'une présentation des possibilités offertes par iPXE.

Merci à Nÿco, Sébastien Koechlin, ZeroHeure, Xavier Teyssier, Nils Ratusznik, tankey, Xavier Claude, oinkoink_daotter et Benoît pour avoir participé à la rédaction de cette dépêche.

Sommaire

Rappels historiques

Lors de l'apparition des machines sans disque, un certain nombre de protocoles, parfois propriétaires, ont dû être développés pour permettre l'assignation d’adresses à celles-ci. Le premier à être normalisé par l'IETF se nomme (RARP), pour Reverse-ARP, qui ressemble beaucoup, dans sa forme, au protocole ARP. Il n'offre cependant que la possibilité d'assigner une adresse à une machine et ne permet pas de communiquer d'autres informations essentielles comme la localisation d'une image à charger. Pour pallier ces limitations, le protocole BootP a ensuite été mis au point, il a finalement été rendu obsolète par l'apparition de DHCP, qui est beaucoup plus flexible.

En 1999, Intel et SystemSoft mettent au point Wired for Management (WFM), un système permettant la gestion de machines sans système d'exploitation, WFM décrit également une technique d'amorçage par le réseau, qui est alors nommée Preboot Execution Environment (PXE), et qui permet de télécharger un chargeur d'amorçage et d'offrir à celui-ci une API pour qu'il puisse charger facilement un noyau via le réseau. De nombreux constructeurs de cartes réseau se sont mis à embarquer des ROM d'extensions sur leurs cartes pour permettre l'amorçage par le réseau.

Bien que six architectures matérielles soient décrites par la spécification, à ce jour seule x86 possède une couverture complète. Cependant, les stations sans disque dur étant un concept assez ancien, on retrouve des solutions rendant un service similaire sur de nombreuses architectures et pour de nombreux systèmes. À noter aussi que l'Unix Libre NetBSD fournit et documente des solutions pour un grand nombre d'architectures. Enfin, SUN Solaris propose un service similaire dans son OpenROM pour son service Jumpstart.

L'amorçage d'une machine

L'amorçage d'une machine de type PC à base de processeur x86/x86-64 se fait généralement en plusieurs étapes, chacune ayant pour but de récupérer un exécutable de plus en plus évolué pour enfin arriver à charger le noyau d'un système d'exploitation.

Amorcer depuis un disque dur

Pour bien comprendre comment l'amorçage par le réseau fonctionne, il convient de savoir comment se passe l'amorçage « classique », c'est à dire depuis un disque dur.

Lors de l'initialisation de la machine, le BIOS détecte les périphériques amorçables (disque dur, disquette, CD-ROM…) et tente de les démarrer l'un après l'autre selon l'ordre configuré. Pour cela il charge en mémoire le premier secteur du périphérique (soit ses 512 premiers octets), et vérifie que les deux derniers octets correspondent bien aux valeurs hexadécimales AA 55. Si ce n'est pas le cas, il considère que les données chargées ne sont pas destinées à être exécutées et passe au périphérique suivant.
Une fois qu'un programme valide est chargé en mémoire, un certain nombre de valeurs sont placées dans les registres du processeur, comme le numéro du périphérique courant par exemple. Enfin, la première instruction du programme est exécutée.

Dans le cas d'un système Linux, le programme en question est généralement un stage1 de GRUB dont le but va être de lire le contenu de la partition /boot pour en extraire un stage2, qui est un programme bien plus évolué. Il ne faut pas oublier que le stage1 est limité à 510 octets (512 moins les deux derniers octets), ce qui est vraiment peu pour faire quoi que ce soit d'un tant soit peu avancé, d'où la nécessité d'aller chercher un stage2. Heureusement, pour faciliter la vie des concepteurs de chargeurs de démarrage, le BIOS fournit une fonction pour charger un ou plusieurs secteurs d'un périphérique en mémoire. Cette méthode est basée sur une interruption logicielle et le passage des paramètres se fait en utilisant les registres du processeur, la plupart des chargeurs d'amorçage étant écrit en assembleur, ce type d'opération est très facile à réaliser et permet de ne pas avoir à écrire un pilote pour disque-dur, ce qui est très appréciable quand on est limité en taille.

Le stage2 est alors lancé. Celui-ci peut configurer plus largement la machine (disposition de la mémoire, mode de fonctionnement du processeur, clavier, écran…). Il peut présenter un menu pour permettre de choisir certaines options; et finalement il va récupérer les éléments de l'étape suivante: le noyau d'un système d'exploitation et un éventuel initramfs pour leur passer la main. (Note: Dans le cas de Grub, celui-ci dispose d'une étape intermédiaire, le stage1.5 qui permet de charger des drivers pour les différents systèmes de fichiers et évite d'avoir à réinstaller GRUB à chaque modification de la configuration)

Amorcer depuis le réseau

Pour amorcer la machine depuis le réseau, le principe général reste le même, mais, le BIOS n'ayant pas été conçu pour démarrer depuis une carte réseau, il est un peu détourné. La carte va embarquer une ROM d’extension qui sera mise à disposition du BIOS à une certaine adresse. Lors de l'étape du Power-On Self-Test, le BIOS va reconnaitre une séquence d'octets spécifiques indiquant la présence de l'extension. Cette extension de la carte réseau va s'enregistrer comme périphérique amorçable auprès du BIOS qui sera alors capable de démarrer dessus.

Cette ROM contient un micrologiciel qui est divisé en plusieurs modules :

  • le premier est une pile PXE qui implémente une partie de la spécification PXE et dont le comportement sera détaillé plus bas;
  • le second est une série de fonctions respectant l'API UNDI pour Universal Network Device Interface permettant d'utiliser la carte réseau sans avoir à fournir un pilote. Ces fonctions pourront être utilisées par la pile PXE et par le programme que celle-ci chargera.

Reprenons le processus de démarrage avec l'exécution de la pile PXE. Son objectif est de récupérer sur le réseau un Network Bootstrap Program (NBP) pour l'exécuter. Pour cela, il faut obtenir une adresse IP afin de pouvoir communiquer sur le réseau, puis localiser, récupérer et exécuter ce NBP. La spécification PXE adresse ces questions en faisant d'une pierre deux coups : le protocole DHCP est utilisé avec deux options particulières : next-server et filename. DHCP est en effet un protocole flexible qui permet de spécifier de nombreux paramètres personnalisés, aussi bien dans la requête que dans la réponse. La norme PXE demande que dans la réponse, le serveur DHCP spécifie dans le paramètre next-server l'adresse IP d'un serveur TFTP et dans le paramètre filename le chemin du fichier NBP sur ce serveur. UNDI proposant dans son API un client TFTP, la pile PXE n'a plus grand-chose à faire, elle va charger le NBP en mémoire puis l'exécuter à son tour.

Ici les choses varient en fonction du NBP choisi par l'utilisateur. Très souvent il s'agit de PXELINUX, qui fait partie du projet SYSLINUX et qui est en quelque sorte l'équivalent du stage2 de GRUB. Il est capable de chercher à son tour sur le serveur TFTP un fichier de configuration, puis un noyau et un initramfs.

iPXE

Historique du projet

Etherboot

Le projet Etherboot vu le jour en 1995, avant l'apparition de la spécification PXE donc, il avait pour but développer un microgiciel embarquable dans une ROM d'extension pour BIOS afin de permettre l'amorçage par le réseau, le principe est alors sensiblement le même que celui qui sera retenu par PXE plus tard, la différence principale entre les deux approches étant qu'Etherboot récupérait directement un noyau tandis que PXE passe par un NBP.

gPXE

En 1999, Intel publie la spécification PXE, la question de savoir si Etherboot doit abandonner son approche actuelle pour implémenter une pile PXE divise alors les développeurs du projet. Trois développeurs, Marty Connor, H. Peter Anvin et Michael Brown prennent alors la décision de forker le projet, donnant naissance à gPXE. Marty Connor en profite pour créer rom-o-matic.net qui permet de générer des images gPXE facilement et qui connaît un succès rapide.

Entre temps, le mainteneur d'Etherboot n'ayant plus de temps à y consacrer abandonne le projet qui est donc réintégré dans gPXE.

En 2006, gPXE participe à la seconde édition du Google Summer of Code, sous l'égide de la fondation linux, à cette occasion, une pile TCP complète est implémentée par un étudiant indien. gPXE participera à tous les Google Summer of Code suivant jusqu'en 2011.

iPXE

En 2011, un différend oppose Marty Connor et Michael Brown conduisant à un fork du projet par ce dernier et donnant naissance à iPXE, gPXE ne donnant plus de signe de vie depuis presque un an, il est considéré comme mort à l'heure actuelle.

En 2012, iPXE participe à la septième édition du Google Summer of Code, prenant la place de gPXE.

Utiliser iPXE

Il est possible, à partir des sources d'iPXE, de générer un grand nombre d'images différentes, chacune répondant à une utilisation précise du programme. Cette partie recense les utilisations les plus courantes et les images qu'il faudra utiliser.

Chainloading

iPXE peut être chargé depuis une implémentation de PXE existante, il faudra donc utiliser une image de type NBP, c'est le cas d'undionly.kpxe par exemple. Il est également possible de charger iPXE depuis un autre chargeur d'amorçage comme GRUB ou LILO, dans ce cas de figure, une image de type bzImage est nécessaire, il faudra donc utiliser ipxe.lkrn.

Plus d'informations

Démarrer sur CD-ROM, disquette ou clé USB

Il existe également des images spécifiques chacun de ces supports, ipxe.iso, ipxe.dsk et ipxe.usb pour l'amorçage depuis, respectivement, un CD-ROM, une disquette ou une clé USB.

Flasher la ROM de sa carte réseau

Les plus téméraires d'entre vous pourront flasher la ROM de leur carte réseau afin de disposer d'iPXE de façon permanente. Ceci permet d'éviter le chainloading et économise donc une étape, sans parler du gain évident en sécurité si vous embarquez une clé privée dans iPXE : avoir une clé qui se balade en clair sur le réseau, ce n'est jamais une très bonne chose. Dans ce cas de figure, vous devez compiler une image de type .rom ou .mrom et il vous faudra connaître le pilote précis utilisé par votre carte réseau.

Plus d'informations

Fonctionnalités

iPXE, en plus du respect de la spécification PXE, propose un grand nombre de fonctionnalités qui ne sont pas décrites par celle-ci.

Protocoles

Là où la spécification PXE ne se limite qu'aux protocoles TFTP et Multicast TFTP, iPXE intègre un certain nombre d'autres protocoles. Les plus utilisés étant bien sûr HTTP et FTP, mais sont également disponibles :

  • Scalable Local Area Multicast protocol (SLAM) : un protocole conçu pour être plus efficace que Multicast TFTP et qui n'est à ma connaissance implémenté que dans iPXE.
  • HTTPS : HTTP sécurisé qui offre un certain nombre de possibilités combiné avec l'intégration de certificats dans iPXE, voir la partie Cryptographie.
  • iSCSI / AoE / FCoE : permet l'amorçage depuis un SAN, voir la partie Sanboot ci-dessous.
  • NFS : implémenté cette année dans le cadre d'un Google Summer of Code par l'auteur de cette dépêche. Devrait être intégré dans la branche principale sous peu.

Sanboot

iPXE permet l'amorçage sur un SAN, il va procéder exactement comme le BIOS pour un amorçage sur disque classique, sauf qu'il va intercepter les interruptions utilisées pour demander la lecture de secteurs afin d'émuler un disque physiquement présent dans la machine. Le chargeur d’amorçage de la cible distante sera donc tout à fait capable de faire son travail et de charger un noyau. Pour poursuivre l'amorçage, il faudra bien évidemment que le système d'exploitation ait des pilotes chargés pour le protocole utilisé (iSCSI, AoE ou FCoE). Cette technique permet d'installer et amorcer des stations Windows sans disque.

Cryptographie

iPXE possède une implémentation de TLS, qui est utilisée pour le support d'HTTPS, et permet lors de la compilation d'embarquer une ou plusieurs Autorités de certification. Il est donc possible d'amorcer une machine avec des images téléchargées depuis l'Internet en toute sécurité. Si une autre solution qu'HTTPS est choisie, il est également possible de signer l'image et de demander à iPXE de ne la charger que si la signature est valide. Pour permettre l'authentification des clients, iPXE permet aussi d'embarquer un certificat et la clé correspondante.

Plus d'informations

Scripting

L'une des fonctionnalités les plus appréciables d'iPXE est probablement la possibilité qui est offerte de le scripter. En effet, il embarque un petit interpréteur qui, bien que rudimentaire, offre des possibilités que ne fournissent pas les autres chargeurs d'amorçage. Ces scripts peuvent être soit embarqués dans l’exécutable à la compilation, soit exécutés comme des images classiques.

La syntaxe ressemble beaucoup à du shell édulcoré, les boucles y sont absentes mais peuvent être construite à partir de goto, de même pas d'instruction if mais une alternative un tout petit peu moins intuitive. Ces choix s'expliquent par le fait que l’interpréteur doit rester le plus petit possible, faisant partie du cœur du programme, il n'est pas possible de le désactiver à la compilation contrairement à la plupart des autres fonctionnalités.

Le classique hello world :

#!ipxe

echo Hello World!

Un script qui demande à l'utilisateur d'entrer une valeur et envoie cette valeur ainsi que son adresse MAC à un serveur HTTP, ce script a été utilisé par l'auteur de cette dépêche à plusieurs reprises pour s’épargner le relevé fastidieux des adresses MAC d'une centaine de machines :

#!ipxe

# Effectue une requête dhcp
dhcp

# Affiche "Nom de la machine : " et attends une entrée de l'utilisateur pour continuer, la valeur saisie est placée dans la variable 'name'.
echo -n Nom de la machine : && read name

# Exécute l'image renvoyée par le serveur HTTP tout en lui transmettant la saisie de l'utilisateur et l'adresse MAC de la machine. 
chain http://${next-server}/?name=${name}&mac=${mac}

Une boucle qui effectue une requête dhcp tant qu'une réponse n'a pas été reçue :

#!ipxe

# Déclare le label 'retry_dhcp'
:retry_dhcp

# Effectue une requête DHCP puis 'isset' teste si la variable 'filename' contient une valeur, ce qui est le cas quand une réponse DHCP valide à été reçue.
# Si 'isset' renvoie faux, le goto nous renvoie au début du script.
dhcp && isset ${filename} || goto retry_dhcp

# La commande 'chain' télécharge l'image dont l'URI est passé en paramètre et l’exécute.
chain ${filename}

iPXE embarque aussi un shell où toutes ces commandes, à l'exception de goto, sont disponibles.

Plus d'informations

Menu graphique

Si PXELINUX et GRUB offrent la possibilité d'afficher des menus depuis longtemps, l'implémentation de cette fonctionnalité dans iPXE date seulement de quelques mois. Contrairement à ce que l'on peut trouver ailleurs, il n'y a pas de fichier de configuration pour décrire le menu, il faut utiliser le langage de script intégré à iPXE et en particulier les commandes menu, item et choose.

Un exemple simple :

#!ipxe

menu
item linux    Boot Linux
item shell    Enter iPXE shell
item exit     Exit to BIOS

# Affiche le menu à l'utilisateur et place le nom de l'élément choisi dans la variable 'target'
choose --default exit --timeout 3000 target && goto ${target}

:linux
# Amorce sur Linux
chain ftp://${server}/bzImage

:shell
# Propose un shell iPXE à l'utilisateur
shell

:exit
# Termine la session iPXE
exit

L'affichage d'un menu plus complet, avec des séparateurs :

menu

DNS

Contrairement à une implémentation classique de PXE, iPXE est capable de comprendre des URI passé par l'option DHCP filename, dans cet URI, la cible peut être un nom de domaine. Afin de pouvoir les résoudre, iPXE intègre un petit client DNS. Ceci ouvre un certain nombre de possibilités, comme la répartition de charge par round-robin DNS qui est particulièrement utile quand on veut amorcer un très grand nombre de machines.

VLAN

Enfin, iPXE est capable de configurer une interface comme un VLAN trunk c'est à dire qu'il peut générer des interfaces virtuelles, une pour chaque VLAN, qui se retrouveront sur des segments Ethernet différents. Pour que ceci fonctionne, il faut bien sûr que la machine soit reliée à un commutateur capable de comprendre le protocole IEEE 802.1Q.

Plus d'informations

Alternatives à iPXE

Je ne connais pas d'alternative libre à iPXE en tant que microgiciel embarquable, en revanche il existe d'autres chargeurs d'amorçage qui peuvent être utilisés comme NBP :

  • PXELINUX du projet SYSLINUX
  • pxegrub du projet GRUB
  • # MBR

    Posté par . Évalué à  10 .

    le stage1 est limité à 510 octets

    En fait, c'est même (beaucoup) moins : 440 octets (voire 446), car le secteur d'amorçage contient aussi la table des partitions, à la fin.

    Hop, moi.

    • [^] # Re: MBR

      Posté par . Évalué à  7 . Dernière modification : le 23/07/12 à 13:35

      J'ai préféré passer sur ce détail pour ne pas compliquer inutilement cette partie qui reste quand même une petite digression.
      Cependant, si on sort du cas particulier de GRUB, il est tout à fait possible de ne pas inclure de MBR sur le disque et la limite artificielle des 44(0|6) octets disparaît. (Même si c'est l'exception plutôt que la norme, un disque peut être utilisé sans être partitionné).

      Merci pour la précision.

  • # j utilise depuis tres longtemps

    Posté par (page perso) . Évalué à  1 .

    Yes,

    j avais vu la conf de gpxe, et j ai adopté immédiatement, un bon remède à netgrub.
    ensuite plus de dev ou quasi plus de gpxe, et ipxe est né. l utiliser c est l adopter. tout si install, de windows a linux a solaris, juste les osX qui ne marchent pas pareil.

    jusqu à il n y a pas si longtemps il fallait faire des menu dans le model syslinux, mais depuis la suppression du support COM32 par défaut, il support son propre model de menu. mais il est quand meme souhaitable de le rajouter, et aussi le port console (RS232 pour les vieux comme moi) (pour ceux qui font des install system en masse par console).

    et tout le paramétrage se fait dans $SRC/src/config
    * general.h pour le COM32 à activer, permet de lancer syslinux par exemple ou bien de lancer un hdt (ça peut être utile)
    * console.h pour activer le port console

    ou mieux encore de rajouter dans la partie locale config/local/console.h (#define CONSOLE_SERIAL) et config/local/general.h (#define IMAGE_COMBOOT)

    il y a quelque threads et polémique de pourquoi pas toujours le mettre et/ou de l activer au besoin par des swtich, c est un long débat qui a pour contrainte la taille du binaire et des nombreux chois et possibilité (en résumé très succin)

  • # Amorçage d'une machine

    Posté par (page perso) . Évalué à  9 .

    La partie "Amorçage d'une machine" devrait s'intituler "Amorçage d'une machine de type PC à base de processeur x86/x86-64". En effet, la très grande majorité des machines à base de processeur ARM, PowerPC, MIPS, AVR32, Blackfin ou autre ne s'amorcent pas du tout de la façon décrite ici, et même certaines machines x86 ne s'amorcent pas non plus de la manière décrite. Ce qui est décrit est bien le cas particulier de la plateforme PC (qui certes est omniprésent dans le monde du serveur et desktop, mais ne reste pas le seul cas possible).

  • # Préhistoire

    Posté par . Évalué à  5 .

    Rappels historiques

    Lors de l'apparition des machines sans disque, …

    Ère qui, rappelons-le, succède à celle où sont apparues les machines avec disques (ah l'amstrad PC 1512, pas de disques durs mais deux (!) lecteurs de disquettes).

    • [^] # Re: Préhistoire

      Posté par (page perso) . Évalué à  3 .

      Qui elle-même succède à celle où sont apparues les machines avec lecteurs de disquette :) (ah, la joie d'attendre que la cassette ait fini de tourner avant de pouvoir jouer au pendu…) Si on faisait comme sur les bons vieux CPC où le système de base était en ROM, et donc le boot en même pas 1s, ça serait bien plus simple ;)

  • # petitboot

    Posté par (page perso) . Évalué à  4 .

    À noter l'existence du projet petitboot, qui est simplement une commande Linux qui sait afficher un menu de boot, et utiliser kexec pour exécuter le noyau choisit. La doc ne parle pas de PXE, c'est dommage, ça ne devrait pas être dur à y ajouter.

    Oui, je sais, ça ne règle pas le boot du linux qui lance petitboot, c'est juste pour compléter le tableau :)

  • # Remerciements

    Posté par (page perso) . Évalué à  2 .

    C'est vraiment un bel article, je suis fier d'y avoir contribué en semant des idées pourris et en vrac avant de lâchement m'éclipser en vacances loin de tout. :-)
    M. Mareo c'était une très très bonne idée cet article, je sens que ça va devenir mon pense-bête sur iPXE.

    "La liberté est à l'homme ce que les ailes sont à l'oiseau" Jean-Pierre Rosnay

    • [^] # Re: Remerciements

      Posté par . Évalué à  3 .

      Merci pour ces encouragements !
      Je suis cependant un tout petit peu triste du manque de retour, je m'attendais à plus de questions.

  • # GRUB 2

    Posté par (page perso) . Évalué à  2 .

    Vous parlez beaucoup de GRUB 1, mais la dernière version GRUB 2 ne fonctionne pas tout à fait pareil. Quoi qu'il en soit, GRUB 2 est utilisable sur PXE sans adaptation. Il faut évidemment construire une image au format PXE, mais je veux dire que c'est une fonctionnalité intégrée.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.