Installation de Debian-GNU-Linux et Windows 7 par le réseau/Internet
par , le mercredi 15 août 2018 à 22:50

Catégorie : Général
Mots clés : Debian Windows Boot PXE iPXE

J'ai parfois besoin de réinstaller mon serveur ou mes PC & portables sous Debian-GNU-Linux et j'utilise pour ça l'installeur Debian en boot PXE sur mon serveur maison, mais régulièrement il me dit que le noyau n'est pas le bon car la version des dépôts officiels a évolué (pour une même version majeure). Je me vois donc contraint de mettre à jour les images netinst de démarrage sur mon serveur TFTP. D'autre part, il m'arrive, encore plus rarement, de devoir réinstaller un portable sous Windows 7, et je souhaiterais ne plus avoir à ressortir mon DVD d'installation qui traîne quelque part d'une boite. iPXE, un remplaçant du boot PXE de SYSLINUX me semble offrir tout ce qu'il faut pour répondre à ces 2 besoins


Projet

Mon premier besoin et de pouvoir lancer l'installation de la version stable de Debian-GNU-Linux en se basant sur l'installeur en phase avec le miroir Debian que j'utilise (Proxad/Free), et en y intégrant les firmware non-libre pour du matériel Dell.

Mon second besoin est de pouvoir booter le CD d'installation de Windows 7 depuis le réseau.

iPXE répond parfaitement, et même plus, à ces 2 besoins.

Boot iPXE

iPXE est un remplaçant du boot PXE fourni par SYSLINUX. Il en décuple les capacités en permettant de booter à travers Internet, peut charger les noyaux et images de disquettes/ISO en HTTP, iSCSI (internet SCSI), AoE (ATA over Ethernet) ... Son système de script est suffisant pour offrir des menus, et de plus permet de chaîner le boot vers d'autres installations iPXE sur Internet.

L'ouverture de iPXE permet même de déporter la logique des menus et du chargement des noyaux/disquettes sur un serveur web, c'est ce que nous allons faire ici.

iPXE peut être lancé de 3 manières différentes :

  • en bootant sur le réseau local par le BIOS avec redirection, provoqué par le serveur DHCP du LAN, vers iPXE .
  • en bottant sur un CD ou une clé USB contenant le bootloader iPXE, si possible pré-configuré pour notre menu.
  • en utilisant une ROM de carte réseau flashée avec iPXE, ce qui est le cas des versions récentes des NIC virtuelles QEM/KVM sous Debian. dhcp

Schéma de fonctionnement

Le schéma habituel d'un boot PXE est de fournir le boot loader PXE depuis un serveur TFTP puis de laisser ce loader vernir chercher sa configuration et donc son menu sur le même serveur tftp, et en général les noyau et autres images de disquettes virtuelles ou CDROM ISO sont récupérés aussi sur ce même serveur TFTP.

Pour commencer nous allons mettre en place un serveur TFTP pour fournir le boot loader iPXE, et nous allons rapidement renvoyer la suite de la configuration sur un serveur web HTTP, qui sera un serveur publique, ce qui permettra à toute personne intéressée de le chaîner dans son propre menu iPXE.

Le schéma de fonctionnement de base est :

Serveur/PC/portableServeur/PC/portableserveur DHCPserveur DHCPserveur TFTPserveur TFTPserveur HTTPserveur HTTP??BiosDHCP requestIP/masque +serveur DNS +default routealt[Loader NON iPXE (boot network BIOS classique)]adresse serveur TFTP +nom du fichier binaire iPXEdemande du fichier binaire iPXEchargement du fichier binaire iPXE,qui prend le relai sur le BIOSrestart du boot loader avec iPXE,on reprend plus haut ("DHCP request")mais en suivant la variante [Loader iPXE][Loader iPXE]URL HTTP du script iPXEiPXEdemande fichier script iPXEchargement du script iPXEexecution du script iPXE

La première petite astuce est de configurer ldhcpe serveur DHCP pour fournir le binaire du loader iPXE pour les BIOS classiques, et sinon renvoyé l'URL HTTP du script iPXE s'il s'agit d'un loader iPXE. Donc dans le cas d'un BIOS standard, il y aura 2 requêtes DHCP, une par le BIOS classique et une seconde par iPXE une fois chargé dans le client. La détection du type de loader est effectué par DHCP qui reçoit une information user-class = "iPXE" de la part du client.

Si le BIOS/loader du PC est déjà iPXE il n'y aura qu'une requête DHCP. C'est le cas avec les BIOS des VM QEMU/KVM sur Debian Strech. C'est aussi le cas si l'on utilise iPXE sur clé USB ou CDROM (voir un peu plus long sur ce sujet).

Configuration serveurs DHCP, TFTP et HTTP

DHCP

Concernant DHCP, il y a de fortes chances pour qu'il soit déjà installé sur votre serveur, si ce n'est pas le cas et s'il n'y a pas d'autre serveur DHCP sur votre réseau local vous pouvez l'installer :

root@server: apt-get install isc-dhcp-server

Si c'est une première installation du serveur DHCP, je vous invite a commencé par le faire fonctionner sans la partie iPXE. Votre serveur DHCP doit fournir une adresse IP/masque, une passerelle par défaut (pas indispensable dans notre cas de figure) et un serveur DNS (ça aide à résoudre les hostname pour la partie HTTP).

Si vous avez déjà un serveur DHCP, par exemple sur un NAS (Synologic...) ou un routeur interne (box ADSL...), vous allez devoir essayer d'adapter la configuration de votre DHCP. Je ne peux vous fournir la procédure dans ce cas, et il se peut que ce ne soit pas facile ni possible. A voir selon la plateforme.

L'ajout à votre configuration DHCP est la suivante, à mettre en global ou dans la section subnet du réseau local :

next-server 192.168.100.1;
if exists user-class and option user-class = "iPXE" {
   filename "http://boot.toto.com/boot.php";
} else {
   #filename "pxelinux.0";
   filename "undionly.kpxe";
}

Ce bout de configuration charge le loader iPXE depuis le serveur TFTP pour les bios classiques, et renvoi l'URL du script pour le bootloader iPXE :

  • l'IP 192.168.100.1 doit être celle de votre serveur TFTP, qui peut-être le même que le serveur DHCP (notre cas de figure) ou un autre.
  • l'URL HTTP dans la ligne filename "http://boot.toto.com/boot.php"; doit bien sûr être adaptée au serveur-virtualhost HTTP (voir plus loin la configuration de ce dernier).
  • la ligne commentée #filename "pxelinux.0"; corresponds à l'ancien boot PXE provenant de SYSLINUX.
  • le fichier undionly.kpxe provient du package Debian ipxe (installation ci-dessous) ou compilé à partir des sources présents sur le site du projet iPXE.

Note: j'ai eu un soucis de configuration DHCP depuis le loader undionly.kpxe chargé sur un portable Dell Latitude E4300. Un problème de compatibilité BIOS-PXE avec iPXE oblige à utiliser le loader undionly.kkpxe (2 'k') qu'il faut compiler à partir des sourses du projet iPXE.

TFTP et bootloader iPXE

Sous Debian Stretch, nous allons utiliser le serveur TFTPD HPA, nous allons aussi avoir besoin d'un des bootloader iPXE :

root@server: apt-get install tftpd-hpa ipxe

Le fichier de configuration de tftpd-hpa est :

root@server:/etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure"

ce qui veut dire que la racine du serveur est /srv/tftp et que l'on ne voit pas les fichiers plus haut (principe du chroot).

Ce serveur TFTP ne sert que sur la LAN, inutile de l'ouvrir en accès que l'extérieur.

Nous pouvons maintenant copier le boorloader iPXE, dit universel :

root@server: cp /usr/lib/ipxe/undionly.kpxe /srv/tftp/

Ce loader fonctionne avec le driver réseau UNDI présent dans les BIOS des cartes réseaux compatibles avec la norme PXE. Ceci permet d'avoir un load iPXE qui marche sans se préoccuper du type de carte réseau. Ce qui va lui permettre de se connecter au serveur HTTP et de charger le script iPXE. Attention, il faut bien comprendre que ce côté driver universel n'est utilisable que pendant la phase BIOS, une fois l'OS (noyau Linux ou Windows) chargé ce driver est déchargé et c'est l'OS qui prend la main avec tous les problèmes de compatibilité habituelle.

Serveur HTTP

L'URL HTTP, fournie par DHCP, au loader iPXE doit renvoyer un fichier texte contenant des instructions iPXE. Nous pouvons tout à fait utiliser un fichier statique mais aussi un script PHP pour effectuer des post-traitements. Dans notre cas de figure, pour Debian, nous allons faire quelques post-traitements.

Nous installons donc le serveur Apache2 et PHP (7). Vous pouvez bien sûr utiliser un autre serveur Web (nginx...) et le placer sur un autre serveur physique (NAS, VM...) :

root@server: apt-get install apache2 libapache2-mod-php

Nous allons ensuite créer un virtualhost qui doit correspondre à l'URL indiquée dans la configuration du serveur DHCP :

root@server:/etc/apache2/sites-available/boot.toto.com.conf

#-----------------------------------------------------------------
# Host virtuel
#-----------------------------------------------------------------
<VirtualHost *:80>
    ServerName boot.toto.com
    ServerAdmin webmaster@kozodo.com

    DocumentRoot /home/public/www/boot.toto.com/htdocs

    <Directory /home/public/www/boot.toto.com/htdocs>
        Require all granted
    </Directory>

    ErrorLog /var/log/apache2/boot.toto.com-error.log
    CustomLog /var/log/apache2/boot.toto.com-access.log vhost_combined

    DirectoryIndex index.php index.html index.txt
</VirtualHost>

Ne pas oublier d'activer ce virtualhost a2ensite boot.toto.com et de recharger Apache systemctl reload apache2.

  • Il serrait plus sage de passer en HTTPS pour éviter les modifications de fichier 'au vol' (attaque man-in-the-middle).
  • ServerName doit bien correspondre au hostname de l'URL fournie par DHCP.
  • le dossier /home/public/www/boot.toto.com/htdocs, racine de votre site, doit être adapté à votre environnement.

Pour tester mettons un fichier de script iPXE assez simple (pas d'utilisation de PHP dans celui-ci, mais le fichier s'appelle bien boot.php conformément à l'URL indiquée dans la configuration DHCP) :

root@server:/home/public/www/boot.toto.com/htdocs/boot.php

#!ipxe

#dhcp

:start
menu Install OS
item --gap -- ----- OS Boot -----------------------------------------
item local Local disk
item --gap -- ----- External public menus ------------------------------
item salstar => Salstar (salstzar.sk)
item netbootxyz => NetBoot (netboot.xyz)
item --gap -- ----- iPXE -----------------------------------------------
item shellipxe Shell
choose --default local target && goto ${target}

:local
sanboot --no-describe --drive 0x80

:shellipxe
shell

:salstar
chain http://boot.salstar.sk

:netbootxyz
chain http://boot.netboot.xyz/

Vous pouvez noter que la commande dhcp est en commentaire, car ce pool/non-free/f/firmware-nonfree/(firmware-.*.deb) phase a normalement été effectuée par le loader iPXE juste avant de charger ce script.

Maintenant nous avons une séquence de chargement iPXE complète. Faisons un test avec une VM sous KVM (ou sous VirtualBox si vous l'avez installée).

Votre VM doit booter sur le réseau, et dans le cas de DHCP il faut avoir une carte réseau en mode Bridge (=Pont) car en NAT il y a un réseau intermédiaire avec son propre DHCP (certainement modifiable pour notre besoin, mais c'est une autre histoire) :

en démarrant la VM on obtient :

et si l'on choisit de partir vers le script/menu publique de Salstar (qui n'est pas hébergé chez nous donc) :

et nous voilà dans le monde merveilleux des iPXE serveurs publiques.

Script iPXE pour Debian

Si vous faites un tour de la documentation du loader iPXE, vous vous rendrez compte de ses nombreuses possibilités. Il est notamment possible de charger un noyau et des disquettes/cdrom virtuel depuis n'importe quel serveur HTTP/FTP/NFS...

Nous allons donc en profiter pour charger les images Debian NetInst directement depuis le miroir local de notre fournisseur (Free dans mon cas) (note: je n'ai pas testé avec le redirecteur Debian deb.debian.org.). A cela nous allons ajouter l'image des firmwares non libre, très utile sur du matériel Dell (driver réseau BNX II).

Scrip iPXE statique

Serveur/PC/portableServeur/PC/portableserveur DHCPserveur DHCPserveur TFTPserveur TFTPserveur HTTPserveur HTTPDebian local mirrorDebian local mirrorBiosDHCP requestIP/masque +serveur DNS +default routealt[Loader NON iPXE (boot network BIOS classique)]adresse serveur TFTP +nom du fichier binaire iPXEdemande du fichier binaire iPXEchargement du fichier binaire iPXE,qui prend le relai sur le BIOSrestart du boot loader avec iPXE,on reprend plus haut ("DHCP request")mais en suivant la variante [Loader iPXE][Loader iPXE]URL HTTP du script iPXEiPXEdemande fichier script iPXEchargement du script iPXEChargement Debiandemande du noyau +ramdisk +archive firmware non librelinuxinitrd.gzfirmware.cpio.gzInstallation Debian de type NetInst

La première version du script iPXE est :

root@server:/home/public/www/boot.toto.com/htdocs/boot.php

#!ipxe

#dhcp

:start
menu Install OS
item --gap -- ----- No OS Boot -----------------------------------------
item local Local disk
item --gap -- ----- Debian ---;------------------------------------------
item debian-stable-amd64-text   Install Debian stable/amd64 from Debian repository
item debian-stable-i386-text    Install Debian stable/i386  from Debian repository
item debian-stable-amd64-auto   Install Debian stable/amd64 from Debian repository with Cinnamon desktop
item debian-stable-i386-auto    Install Debian stable/i386  from Debian repository with Cinnamon desktop
item debian-stable-amd64-rescue Rescue  Debian stable/amd64 from Debian repository
item debian-stable-i386-rescue  Rescue  Debian stable/i386  from Debian repository
choose --default local target && goto ${target}

:local
sanboot --no-describe --drive 0x80

:debian-stable-amd64-text
kernel http://debian.proxad.net/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux
initrd http://debian.proxad.net/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/stable/current/firmware.cpio.gz
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
prompt Press any key
booté

:debian-stable-i386-text
kernel http://debian.proxad.net/debian/dists/stable/main/installer-i386/current/images/netboot/debian-installer/i386/linux
initrd http://debian.proxad.net/debian/dists/stable/main/installer-i386/current/images/netboot/debian-installer/i386/initrd.gz
initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/stable/current/firmware.cpio.gz
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
prompt Press any key
boot

:debian-stable-amd64-auto
kernel http://debian.proxad.net/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux
initrd http://debian.proxad.net/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/stable/current/firmware.cpio.gz
initrd http://boot.toto.com/standard.cfg preseed.cfg
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
prompt Press any key
boot

:debian-stable-i386-auto
kernel http://debian.proxad.net/debian/dists/stable/main/installer-i386/current/images/netboot/debian-installer/i386/linux
initrd http://debian.proxad.net/debian/dists/stable/main/instalinstallationler-i386/current/images/netboot/debian-installer/i386/initrd.gz
initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/stable/current/firmware.cpio.gz\n";
initrd http://boot.toto.com/standard.cfg preseed.cfg
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
prompt Press any key
boot

:debian-stable-amd64-rescue
kernel http://debian.proxad.net/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux
initrd http://debian.proxad.net/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/stable/current/firmware.cpio.gz
imgargs linux language=fr country=FR locale=fr_FR.UTF-8 rescue/enable=true
prompt Press any key
boot

:debian-stable-i386-rescue
kernel http://debian.proxad.net/debian/dists/stable/main/installer-i386/current/images/netboot/debian-installer/i386/linux
initrd http://debian.proxad.net/debian/dists/stable/main/installer-i386/current/images/netboot/debian-installer/i386/initrd.gz
initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/stable/current/firmware.cpio.gz
imgargs linux language=fr country=FR locale=fr_FR.UTF-8 rescue/enable=true
prompt Press any key
boot

et le fichier standard.cfg pour l'installation semi-automatique du Cinnamon desktop, à placer sur le serveur HTTP au même endroit que le fichier boot.php, est :

root@server:/home/public/www/boot.toto.com/htdocs/standard.cfg

d-i debian-installer/locale string fr_FR.UTF-8
d-i keyboard-configuration/xkb-keymap select fr(latin9)

d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_domain string unassigned-domain
d-i netcfg/wireless_wep string

d-i hw-detect/load_firmware boolean true

d-i mirror/protocol string http
d-i mirror/country string France
d-i mirror/http/hostname string ftp.fr.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string

d-i clock-setup/utc boolean true
d-i time/zone string Europe/Paris
d-i clock-setup/ntp boolean true

d-i apt-setup/non-free boolean true
d-i apt-setup/contrib boolean true
d-i apt-setup/services-select multiselect security, updates
d-i apt-setup/security_host string security.debian.org

tasksel tasksel/first multiselect standard, cinnamon-desktop
d-i pkgsel/include string joe mc less htop glances rsync ncdu firefox-esr thunderbird pidgin vlc screen ark unrar unzip terminator openssh-server mosh wget curl qemu-kvm virt-manager postfix
popularity-contest popularity-contest/participate boolean false

d-i grub-installer/only_debian boolean trueinstallation
d-i grub-installer/with_other_os boolean true

d-i debian-installer/add-kernel-opts string nousb consoleblank=0

d-i finish-install/reboot_in_progress note

Quelques remarques sur le script iPXE et le fichier preseed :

  • on a ajouté un timeout de 5 secondes : si aucun choix n'est fait on boot sur le disque local.
  • les URL des noyaux Linux et ramdisk de l'installeur doivent être adaptées à votre miroir (hostname et chemin racine différents).
  • j'ai répété les lignes kernel et initrd mais il est tout à fait possible d'utiliser des variables (ex: arch, dist) pour regrouper/simplifier la*e script iPXE, mais nous allons le faire en PHP (ci-dessous).
  • les firmwares non libre sont pris directement sous formes d'une archive cpio et c'est justement directement ce qu'attend le noyau, à la suite du ramdisk de l'installeur. Il es tout à fait possible d'ajouter d'autres archive cpio pour d'autre firmware.
  • le fichier preseed doit se nommer preseed.cfg et être directement posé à la racine du ramdisk. Initrd va le renommer et le créer une archive CPIO le contenant, du coup le noyau Linux va le décompresser tel quel et l'installeur va automatiquement le prendre en compte (pas la peine d'ajouter 'auto' ou le nom du fichier dans les paramètres du noyau).
  • la ligne prompt Press any key permet de voir ci qui a été chargé par le loader avant de lancer l'installation.

Le menu qui apparaît est maintenant :

Voilà, c'est déjà un grans pas, mais on peurt faire légèrement mieux; prendre les firmwares non libre directement dans le dépôt Debian non-free donc sans se baser sur le dépôt non officiel d'URL http://cdimage.debian.org/cdimage/unofficial/ qui pourrait ne pas être à jour en même temps que le dépôt Debian (ce qui n'est pas un grand risque puisqu'il ne s'agit que de firmware, pas de module noyau Linux).

Script iPXE dynamique

Pour cela, à la création du menu iPXE, nous allons rechercher dans le fichier Packages.gz du dépôt officiel l'ensemble des paquets Debian firmware nonfree, correspondant à la distribution et architecture souhaitée. Cette opération effectue une requête HTTP distante de notre serveur HTTP vers le miroir Debian, c'est là que PHP est utile (un script shell ou Python aurait aussi pu faire l'affaire).

Serveur/PC/portableServeur/PC/portableserveur DHCPserveur DHCPserveur TFTPserveur TFTPserveur HTTPserveur HTTPDebian local mirrorDebian local mirrorBiosDHCP requestIP/masque +serveur DNS +default routealt[Loader NON iPXE (boot network BIOS classique)]adresse serveur TFTP +nom du fichier binaire iPXEdemande du fichier binaire iPXEchargement du fichier binaire iPXE,qui prend le relai sur le BIOSrestart du boot loader avec iPXE,on reprend plus haut ("DHCP request")mais en suivant la variante [Loader iPXE][Loader iPXE]URL HTTP du script iPXEiPXEdemande fichier script iPXEscript PHP pour construire la liste des packages firmware nonfreeget /debian/dists/\<dist\>/non-free/binary-\<arch\>/Packages.gzPackages.gzextraction de la liste des paquets pool/non-free/f/firmware-nonfree/firmware-.*.debchargement du script iPXEChargement Debiandemande du noyau +ramdisk +list des packages pour les firmwares non libreslinuxinitrd.gzfirmware-nonfree-*.debplusieurs packagesfirmware-nonfree-*.debInstallation Debian de type NetInst

Notre seconde version du script boot.php ressemble maintenant à ceci :

root@server:/home/public/www/boot.toto.com/htdocs/boot.php

#!ipxe

#dhcp

:start
menu Install OS
item --gap -- ----- No OS Boot -----------------------------------------
item local Local disk
item --gap -- ----- Debian ---------------------------------------------
item debian-stable-amd64-text   Install Debian stable/amd64 from Debian repository
item debian-stable-i386-text    Install Debian stable/i386  from Debian repository
item debian-stable-amd64-auto   Install Debian stable/amd64 from Debian repository with Cinnamon desktop
item debian-stable-i386-auto    Install Debian stable/i386  from Debian repository with Cinnamon desktop
item debian-stable-amd64-rescue Rescue  Debian stable/amd64 from Debian repository
item debian-stable-i386-rescue  Rescue  Debian stable/i386  from Debian repository
choose --default local --timeout 5000 target && goto ${target}

:local
sanboot --no-describe --drive 0x80

:debian-stable-amd64-text
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
imgargs linux language=fr country=FR lbien ocale=fr_FR.UTF-8
prompt Press any key
boot

:debian-stable-i386-text
<?php debian_with_firmware_nonfree('stable', 'i386'); ?>
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
prompt Press any key
boot

:debian-stable-amd64-auto
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
initrd http://boot.toto.com/preseed/standard.cfg preseed.cfg
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
prompt Press any key
boot

:debian-stable-i386-auto
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
initrd http://boot.toto.com/preseed/standard.cfg preseed.cfg
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
prompt Press any key
boot

:debian-stable-amd64-rescue
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
imgargs linux language=fr country=FR locale=fr_FR.UTF-8 rescue/enable=true
prompt Press any key
boot

:debian-stable-i386-rescue
<?php debian_with_firmware_nonfree('stable', 'i386'); ?>
imgargs linux language=fr country=FR locale=fr_FR.UTF-8 rescue/enable=true
prompt Press any key
boot

<?php

function debian_with_firmware_nonfree($dist, $arch) {
    $debian_repo   = 'debian.pro;xad.net';
    $root_dir      = 'debian';
    $packages_list = gzdecode(file_get_contents("http://$debian_repo/$root_dir/dists/$dist/non-free/binary-$arch/Packages.gz"));

    print "kernel http://$debian_repo/$root_dir/dists/$dist/main/installer-$arch/current/images/netboot/debian-installer/$arch/linux\n";
    print "initrd http://$debian_repo/$root_dir/dists/$dist/main/installer-$arch/current/images/netboot/debian-installer/$arch/initrd.gz\n";
    print "#initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/$dist/current/firmware.cpio.gz\n";
    print "initrd http://boot.toto.com/empty_firmware_folder.cpio.gz\n";

    // Search all matching nonfree firmware
    preg_match_all('#Filename: (pool/non-free/f/firmware-nonfree/(firmware-.*.deb))#', $packages_list, $matches);    

    foreach ($matches[1] as $id => $file)
        print "initrd http://$debian_repo/$root_dir/$file /firmware/" . $matches[2][$id] . "\n";
}
?>

C'est un peu plus compact et surtout assez magique (s'il n'y pas d'erreur PHP ni de problème de connexion du serveur web vers l'extérieur, voir les log Apache/PHP sur votre serveur HTTP) :

  • il faut éventuellement adapter le hostname et le chemin racine de votre miroir Debian, dans les variables PHP $debian_repo et $root_dir.
  • si besoin, vous pouvez scanner d'autres firmware (ex: firmware non libre vidéo pour une installation graphique) en adaptant l'expression régulière utilisée dans la fonction PHP preg_match_all.

Finalement le boot donne ceci, mais on a pas le temps de voir tous les fichiers chargés tellement ça défile vite :

Pour vérifier le script vous pouver l'appeler depuis un navigateur web en saisissant l'URL que fournie dans la configuration DHCP, vous devriez avoir le texte du scrip iPXE visible. Si la page est blanche, c'est qu'il y a une erreur cotée PHP (voir les log Apache/php sur votre serveur web.

Script iPXE pour Windows 7

Pour Windows c'est un peu moins évident, car le boot PXE n'est pas quelque chose de naturel, Windows disposant son propre système de déploiement à base d'image (WDS); mais après pas mal de lecture de documentation, et beaucoup de tests, j'ai trouvé 2 solutions relativement simple.

Tout d'abord, le projet iPXE fournis une aide précieuse. Nous allons utiliser le binaire WimBoot pour charger le noyau WINPE contenu dans le CD d'installation de Windows, sans avoir besoin de créer notre propre WINPE.

L'installeur Windows aura ensuite besoin d'accéder au contenu du CD/DVD d'installation. Nous allons mettre en place 2 méthodes :

  • partage du CD par SMB/CIFS sous Linux (s serveur Samba), facile et utilisable sur le LAN; mais pas recommandé sur internet (ou alors en filtrant par IP source dans Samba).
  • montage d'un CD/DVD-ROM par iSCSI sur un target dédié, utilisable sur Internet (à filter par IP, initiator name ou mot de passe CHAP).

Chargement du noyau de l'installeur

Sur le serveur web il faut copier quelques fichiers du CD/DVD d'installation de Windows, nous allons donc monter l'image ISO de Windows 7 dans un dossier et copier les fichiers dans un sous-dossier de la racine web (win7install):

root@server:/home/public/www/boot.toto.com/htdocs# mkdir -p /home/public/windows7dvd
root@server:/home/public/www/boot.toto.com/htdocs# mount -o loop /home/public/soft/Sys/Windows7/Win7AIO.iso /home/public/windows7dvd
root@server:/home/public/www/boot.toto.com/htdocs# mkdir win7install
root@server:/home/public/www/boot.toto.com/htdocs# cp /home/public/windows7dvd/boot/boot.sdi win7install/
root@server:/home/public/www/boot.toto.com/htdocs# cp /home/public/windows7dvd/boot/boot.wim win7install/
root@server:/home/public/www/boot.toto.com/htdocs# cp /home/public/windows7dvd/boot/bcd      win7install/

Il faut aussi copier le fichier binaire wimboot, à extraire de l'archive indiquée sur le site iPXE, dans le dossier win7install.

Ceci dit, ces fichiers vont nous permettre uniquement de lancer l'environnement Windows WINPE, il faut y ajouter un script qui va nous permettre d'accéder aux fichiers du CD/DVD d'installation (configuration ci-dessous), et lancer le setup.exe de Windows. Nous allons donc créer 2 fichiers, à placer dans le dossier win7install du serveur web :

root@server:/home/public/www/boot.toto.com/htdocs/win7install/winpeshl.ini

[LaunchApps]
"install.cmd"

root@server:/home/public/www/boot.toto.com/htdocs/win7install/install.cmd

wpeinit

rem Se connecter ou recherche le CD/DVD d'installation Windows (voir plus loin selon la méthode choisie)

setup.exe

Installeur Windows sur un partage Samba

Serveur/PC/portableServeur/PC/portableserveur DHCPserveur DHCPserveur TFTPserveur TFTPserveur HTTPserveur HTTPserveur Samba/Windowsserveur Samba/WindowsBiosDHCP requestIP/masque +serveur DNS +default routealt[Loader NON iPXE (boot network BIOS classique)]adresse serveur TFTP +nom du fichier binaire iPXEdemande du fichier binaire iPXEchargement du fichier binaire iPXE,qui prend le relai sur le BIOSrestart du boot loader avec iPXE,on reprend plus haut ("DHCP request")mais en suivant la variante [Loader iPXE][Loader iPXE]URL HTTP du script iPXEiPXEdemande fichier script iPXEchargement du script iPXEChargement Windowsdemande de l'environnement WINPEwimbootinstall.cmdwinpeshl.iniBCDboot.sdidesboot.wimconstruction du ramdisk WINPE par wimbootet lancement de WINPEinstall.bat executionWINPE initialisationpartage fichiers du DVD d'installation de Windows 7setup.exe et autres fichier de l'installeur WindowsInstallation Windows

Dans le cas de l'utilisation du partage Samba pour accéder à l'installeur Windows, le fichier de commande Windows install.cmd ressemble à :

root@server:/home/public/www/boot.toto.com/htdocs/win7install/install.cmd

rem Initialise WINPE
wpeinit

rem Attends 5 secondes, que le réseau soit bien monté
ping -n 5 127.0.0.1 > NUL

rem Se connecte au partage contenant les fichiers du DVD Windows
net use z: \\boot.toto.com\INSTWIN7
z:

rem Lance le setup Windows
setup.exe

rem Evite le redémarrage immédiat de WINPE en cas de problème
cmd
  • le nom du fichier winpeshl.ini est imposé par WINPE qui va le prendre en compte automatiquement, si présent.
  • le nom du fichier install.cmd doit correspondre au paramètre de la section [LaunchApps] de winpeshl.ini.
  • le choix de l'unité Z: pour le partage est arbitraire, mais il faut savoir que C: est réservé par le noyau WINPE, D: sera pris par le disque physique si son système de fichier est pris en compte par Windows (NTFS, FAT...), et X: contiens les fichiers installés par WimBoot.
  • le nom du partage INSTWIN7 est libre, mais doit correspondre au partage Samba (sur Linux ou Windows).

Il faut, comme indiqué plus haut, partager le contenu du CD/DVD d'installation de Windows par Samba sous Linux. Notez que le partage du CD/DVD pourrait être sur un autre serveur, y compris d'une plateforme différente (ex: Windows Server).

Voici le bout de configuration de Samba pour ce partage, en se rappelant que nous avons monté l'image ISO du CD/DVD dans /home/public/windows7dvd (à l'aide de la commande mount -o loop ... plus haut). Ce montage devra être pérennisé dans /etc/fstab ou alors il faudra copier tous les fichiers dans le dossier partagé par Samba :

root@server:/etc/samba/smb.conf (extrait du fichier)

[INSTWIN7]
   browsable = yes
   read only = yes
   writable = no
   path = /home/public/windows7dvd
   guest ok = yes
   acl allow execute always = true
  • le nom du partage INSTWIN7 doit être celui utilisé dans le fichier install.cmd.
  • partage en lecture seule, c'est toujours mieux.
  • accès sanns mot de passe, à tester à la main depuis un poste client pour être sûr.
  • le path doit être cohérent avec le point de montage du fichier ISO DVD de Windows (fait avec le fameux mount -o loop ...).
  • acl allow execute always = true est essentiel pour rendre le fichier setup.exe exécutable.

Du coup le script iPXE pour lancer l'installation de Windows doit être :

root@server:/home/public/www/boot.toto.com/htdocs/boot.php

#!ipxe

#dhcp

:start
menu Install OS
item --gap -- ----- No OS Boot -----------------------------------------
item local Local disk
item --gap -- ----- Windows - ------------------------------------------
item windows7      Install Windows 7 (by SMB/CIF share)
choose --default local --timeout 5000 target && goto ${target}

:local
sanboot --no-describe --drive 0x80

:windows7
kernel win7install/wimboot
initrd win7install/install.cmd             install.cmd
initrd win7install/winpeshl.ini            winpeshl.ini
initrd win7install/bcd                     BCD
initrd win7install/boot.sdi                boot.sdi
initrd win7install/boot.wim                boot.wim
prompt Press a key
boot
  • je n'ai mis que la section Windows ici, le script complet est en fin d'article.
  • le prompt permet de vérifier que tout s'est bien passé; à retirer une fois au point.
  • ATTENTION, l'ordre des fichiers est important; si le fichier winpeshl.ini n'est pas mis avant le noyau ça ne marche pas.

NOTE : le type de carte de la carte réseau doit être supportée par l'installeur Windows (rtl8139 au lieu de virtio) :

Le boot iPXE de Windows donne :

le script install.cmd en arrière plan :

Voilà, pour une installation sur un LAN c'est suffisant. Mais pas question de mettre le partage Samba/Windows du contenu du CD sur Internet. D'oû la seconde solution.

Installeur Windows avec un montage iSCSI

On va partir du même chargement de l'environnement WINPE mais en modifiant le script install.cmd pour qu'il connecte le CD/DVD d'installation de Windows depuis un 'partage' iSCSI (on dit target iSCSI dans ce cas). Ceci est possible car iPXE peut déclarer un tel montage au noyau Windows. Note: iPXE ne bootera pas sur ce montage, car même si c'est possible la suite de l'installeur ne passera pas.

Après pas mal de tests j'ai pu détecter les contraintes suivantes :

  • iPXE peut booter sur un 'partage' iSCSI du CD/DVD Windows mais uniquement si le montage est fait sur le drive id 0X80.
  • si iPXE 'émule' le drive 0x80, le noyau Windows ne voit plus le disque local (le premier utilisable) et dit donc qu'aucun disque physique n'est présent pour l'installation (il demande un éventuel driver de disque dur).
  • si le montage iSCSI du CD/DVD Windows est fait sur un device id supérieur à 0x80, Windows peut présenter le CD/DVD en d: ou e: ou f: ... en fonction de la détection des disques locaux et selon qu'ils sont formatés ou non avec un système de fichiers connu par Windows (NTFS...).
  • de toute façon si le montage iSCSI du CD/DVD Windows est fait sur un device id supérieur à 0x80, l'installeur Windows ne trouve pas le CD d'installation et demande un driver de CD-ROM; si on 'browse' sur le CD/DVD monté automatiquement il le refuse.
  • si on fournit un fichier winpeshl.ini à WINPE (par l'intermédiaire de iPXE), WINPE ne détecte plus le partage iSCSI; il semble que le driver nécessaire ne soit pas monter automatiquement dans ce cas; en revanche sans la présence du fichier winpeshl.ini, on retrouve notre CD/DVD (quelque part entre D: et W:); mais comme on veut lancer des commandes avec un install.cmd il nous faut le winpeshl.ini.
  • il y a bien le driver iSCSI dans le WINPE de l'installeur Windows 7 mais pas les commandes utilitaires (iscsicli...).
  • on peut demander le chargement du driver iSCSI et dans ce cas le partage iSCSI du CD/DVD est pris en compte :-) ! Il faut lancer la commande drvinst.exe msscsi. Ouf, (presque) sauvé !
  • C: et X: sont réservés par WINPE

d'où la logique suivante : booter comme pour le partage Samba, mais utiliser un install.cmd qui va charger le driver MSISCSI puis cherche le CD/DVD dans les unités de disque D: à H: (ça devrait suffire) et lancer setup.exe.

Serveur/PC/portableServeur/PC/portableserveur DHCPserveur DHCPserveur TFTPserveur TFTPserveur HTTPserveur HTTPDVD virtuel sur serveur iSCSIDVD virtuel sur serveur iSCSIBiosDHCP requestIP/masque +serveur DNS +default routealt[Loader NON iPXE (boot network BIOS classique)]adresse serveur TFTP +nom du fichier binaire iPXEdemande du fichier binaire iPXEchargement du fichier binaire iPXE,qui prend le relai sur le BIOSrestart du boot loader avec iPXE,on reprend plus haut ("DHCP request")mais en suivant la variante [Loader iPXE][Loader iPXE]URL HTTP du script iPXEiPXEdemande fichier script iPXEchargement du script iPXEChargement Windowsdemande de l'environnement WINPEwimbootinstall.cmd (depuis install_iscsi.cmd)winpeshl.iniBCDboot.sdiboot.wimconstruction du ramdisk WINPE par wimbootet lancement de WINPEinstall.bat executionWINPE initialisationchargement du driver MSISCSI pour retrouver latarget iSCSI mise en place par le scritp iPXErecherche du DVD virtuel iSCSI contenant lesfichiers d'installation de Windows 7setup.exe et autres fichier de l'installeur WindowsInstallation Windows

Voici le script iPXE suivi du nouveau fichier install_iscsi.cmd (renommé en install.cmd par iPXE pour utiliser le même winpeshl.ini`) :

root@server:/home/public/www/boot.toto.com/htdocs/boot.php

#!ipxe

#dhcp

:start
menu Install OS
item --gap -- ----- No OS Boot -----------------------------------------
item local Local disk
item --gap -- ----- Windows - ------------------------------------------
item windows7iscsi Install Windows 7 (by iSCSI share)
choose --default local --timeout 5000 target && goto ${target}

:windows7iscsi
kernel win7install/wimboot
initrd win7install/install_iscsi.cmd  install.cmd
initrd win7install/winpeshl.ini       winpeshl.ini
initrd win7install/bcd                BCD
initrd win7install/boot.sdi           boot.sdi
initrd win7install/boot.wim           boot.wim
sanhook --drive 0x86 iscsi:boot.toto.com:::1:iqn-2018-08.com.kozodo:images.boot 
prompt Press a key
boot

root@server:/home/public/www/boot.toto.com/htdocs/boot.php

@echo off

echo -----------------------------------------------
echo Intialise the WINPE environnent
echo and wait 5 seconds for the network up
wpeinit
ping -n 5 127.0.0.1 > NUL

echo ===============================================
echo Load the iscsi drivers to acces the
echo Windows 7 iscsi virtual DVD-ROM
echo (and wait 5 seconds for the iscsi connexion)
drvinst.exe msscsi
ping -n 5 127.0.0.1 > NUL

echo -----------------------------------------------
echo Launch the Windows setup on the virtual DVD-ROM
for %%i in (h g f e d) do (
   %%i:
   if exist %%i:\setup.exe %%i:\setup.exe
)

rem Start another shell to stay on
cmd
pause

Evidamment il faut créer le 'partage' iscsi.

Un partage 'partage' iSCSI est un LUN dans un target iscsi, et le client, iPXE puis Windows, s'appelle un initiator. Nous allons donc configurer un target avec un LUN de type CDROM sur notre serveur.

Sous Debian il faut :

  • installer le package Debian TGT pour configurer le target.
  • installer le paquet Debian open-iscsi pour tester le target en tant que client, depuis le serveur.
  • créer notre target en le nommant selon les conventions iSCSI : iscsi:<ip du serveur iscsi>:::<LUN commençant à 1>:iqn-<année>-<mois>.<domain à l'envers>:<nom target> donne iscsi:boot.toto.com:::1:iqn-2018-08.com.kozodo:images.boot .
  • autoriser l'accès au target depuis n'importe quel client (on peut restreindre l'accès par nom de client (initiator name), par IP ou par mot de passe; pour simplifier on va autoriser tous les accès.
  • tester le target depuis un initiator Linux.
  • référencer le target dans le script iPXE (déjà fait plus haut dans le fichier boot.php, directive sanhook).
root@server: apt-get install tgt open-iscsi

# Création du target et du LUN de type CDROM avec l'ISO d'installation Windows 7 (on ne sort pas les fichiers de l'ISO)
root@server: tgtadm --lld iscsi --op new --mode target      --tid 1 -T iqn-2018-08.com.kozodo:images.boot
root@server: tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 -b /home/public/soft/Sys/Windows7/Win7AIO.iso --device-type=cd

# Autorisation d'accès pour tous
root@server: tgtadm --lld iscsi --op bind --tid 1 -I ALL --mode target

# Vérification (LUN 0=contrôleur iSCSI, LUN 1=notre CD/DVD)
root@server: tgtadm --lld iscsi --op show --mode target

Target 1: iqn-2018-08.com.kozodo:images.boot
    System information:
        Driver: iscsi
        State: ready
    I_T nexus information:
    LUN information:
        LUN: 0
            Type: controller
            SCSI ID: IET     00010000
            SCSI SN: beaf10
            Size: 0 MB, Block size: 1
            Online: Yes
            Removable media: No
            Prevent removal: No
            Readonly: No
            SWP: No
            Thin-provisioning: No
            Backing store type: null
            Backing store path: None
            Backing store flags: 
        LUN: 1
            Type: cd/dvd
            SCSI ID: IET     00010001
            SCSI SN: beaf11
            Size: 4665 MB, Block size: 1
            Online: Yes
            Removable media: Yes
            Prevent removal: No
            Readonly: No
            SWP: No
            Thin-provisioning: No
            Backing store type: mmc
            Backing store path: /home/public/soft/Sys/Windows7/Win7AIO.iso
            Backing store flags: 
    Account information:
    ACL information:
        ALL

# Test en tant qu'initiateur (découverte, connexion, déconnexion)
root@server: iscsiadm --mode discoverydb --type sendtargets --portal boot.toto.com:3260 --discover
192.168.100.1:3260,1 iqn-2018-08.com.kozodo:images.boot

root@server: iscsiadm --mode node --targetname iqn-2018-08.com.kozodo:images.boot --portal boot.toto.com:3260 --login
Logging in to [iface: default, target: iqn-2018-08.com.kozodo:images.boot, portal: 192.168.100.1,3260] (multiple)
Login to [iface: default, target: iqn-2018-08.com.kozodo:images.boot, portal: 192.168.100.1,3260] successful.

# et si on regarde dans les logs Linux (commande dmesg ou tail /var/log/syslog) :
[3739768.117675] scsi host7: iSCSI Initiator over TCP/IP
[3739768.125067] scsi 7:0:0:0: RAID              IET      Controller       0001 PQ: 0 ANSI: 5
[3739768.192287] scsi 7:0:0:0: Attached scsi generic sg3 type 12
[3739768.192908] scsi 7:0:0:1: CD-ROM            IET      VIRTUAL-CDROM    0001 PQ: 0 ANSI: 5
[3739768.232348] sr 7:0:0:1: [sr0] scsi-1 drive
[3739768.232673] sr 7:0:0:1: Attached scsi CD-ROM sr0
[3739768.232754] sr 7:0:0:1: Attached scsi generic sg4 type 5

# on peut alors monter le CD sous Linux (device sr0 selon le log ci-dessus) :
root@server: mount /dev/sr0 /mnt
mount: /dev/sr0 est protégé en écriture, sera monté en lecture seule

root@øerver: ls -l /mnt/
total 506
-r--r--r--  1 nobody nogroup     43 juil. 14  2009 autorun.inf
dr-xr-xr-x  4 nobody nogroup    428 oct.  27  2009 boot
-r--r--r--  1 nobody nogroup 383562 juil. 14  2009 bootmgr
dr-xr-xr-x  3 nobody nogroup     88 oct.  27  2009 efi
-r--r--r--  1 nobody nogroup 111880 juil. 14  2009 setup.exe
dr-xr-xr-x 11 nobody nogroup   8704 oct.  27  2009 sources
dr-xr-xr-x  5 nobody nogroup    180 oct.  27  2009 support
dr-xr-xr-x  3 nobody nogroup     84 oct.  27  2009 upgrade

# Pour suivre les connexions de notre target depuis le serveur (encore mieux avec la commande `watch` dans un terminal séparé)
root@øerver: tgtadm --lld iscsi --op show --mode conn --tid 1

# Démontage et déconnexion iSCSI
root@server: umount /mnt
root@server: iscsiadm --mode node --targetname iqn-2018-08.com.kozodo:images.boot --portal boot.toto.com:3260 --logout
Logging out of session [sid: 4, target: iqn-2018-08.com.kozodo:images.boot, portal: 192.168.100.1,3260]
Logout of [sid: 4, target: iqn-2018-08.com.kozodo:images.boot, portal: 192.168.100.1,3260] successful.

Pour rendre le target persistant au redémarrage du serveur Linux, il faut créer le fichier suivant :

root@server:/etc/tgt/conf.d/win7iso.conf

default-driver iscsi

<target iqn-2018-08.com.kozodo:images.boot>
    backing-store /home/public/soft/Sys/Windows7/Win7AIO.iso
    device-type cd
</target>

Notes:

  • un initiateur doit avoir un nom iSCSI du même type que le target, mais les clients (iPXE, Linux, Windows) vont en définir un automatiquement.
  • il faut bien synchroniser l'adresse du serveur iSCSI et le nom du target entre le serveur (commande TGT --op new ...) et les clients (fichier de script iPXE).
  • pour un accès au serveur iSCSI depuis internet, il suffit d'ouvrir le port 3260, ou éventuellement un port différent en créant un portal iSCSI sur un port différent (dans ce as tgtadmin lancera une autre instance de tgtd dédiée à notre target).
  • le temps de chargement/démarrage de l'environnement WINPE est bien plus long dans le cas du iSCSI. Windows doit tenter de lire ce 'disque' virtuel déclaré par iPXE mais ne le trouve pas dans cette phase (pas de connexion au target visible depuis le serveur).

Notre boot iPXE pour installer Windows depuis notre montage iSCSI donne :

Ouf !

Quelques outils supplémentaires bien utiles

Pour du dépannage, en plus de Debian Rescue nous allons installer quelques outils :

  • MemTest+ permettant de tester la mémoire d'un ordinateur récalcitrant.
  • SystemRescusCD qui un live CD Linux très complet permettant de travailler sur les disques, remonter une partition difficile...
  • CloneZilla permettant de cloner des disques et partition et de et vers pas mal de destinations (disque/partition à disque/partition, disque/partition à image sur serveur SSH/FTP/SAMBA/...).

MemTest+

Pour MemTest+ nous installons 2 packages, puis nous créons un dossier pour y copier 2 fichiers (ou mettre des liens symboliques) :

root@server: apt-get install syslinux-common memtest86+
root@server: mkdir                                 /home/public/www/boot.toto.com/htdocs/tools
root@server: cp /usr/lib/syslinux/memdisk          /home/public/www/boot.toto.com/htdocs/tools/memdisk
root@server: cp /usr/lib/memtest86+/memtest86+.iso /home/public/www/boot.toto.com/htdocs/tools/memtest86+.iso

SystemRescueCD

Il faut récupérer le fichier ISO de SystemRescueCD puis extraire une partie du contenu à placer dans l'arborescence du serveur HTTP :

root@server: wget -O /tmp/systemrescuecd_lastest.iso https://sourceforge.net/projects/systemrescuecd/files/latest/download
root@server: mount -o loop /tmp/systemrescuecd_lastest.iso /mnt
root@server: mkdir                        /home/public/www/boot.toto.com/htdocs/sysrcd
root@server: cp /mnt/isolinux/rescue32    /home/public/www/boot.toto.com/htdocs/sysrcd/
root@server: cp /mnt/isolinux/initram.igz /home/public/www/boot.toto.com/htdocs/sysrcd/
root@server: cp /mnt/sysrcd.dat           /home/public/www/boot.toto.com/htdocs/sysrcd/

root@server: umount /mnt
root@server: rm /tmp/systemrescuecd_lastest.iso

CloneZilla

La aussi, on va récupérer le fichier ISO et en sortir quelques fichiers à placer dans l'arborescence du serveur HTTP :

# Note : il y a des versions alternative (firmware non libre, version 32bits) qui peuvent être préférables à la version par défaut : voir la page https://clonezilla.org/downloads.php
root@server: wget -O /tmp/clonezilla.iso https://sourceforge.net/projects/clonezilla/files/latest/download
root@server: mount /tmp/clonezilla.iso  /mnt
root@server: mkdir -p                         /home/public/www/boot.toto.com/htdocs/clonezilla/live
root@server: cp /mnt/live/vmlinuz             /home/public/www/boot.toto.com/htdocs/clonezilla/live/
root@server: cp /mnt/live/initrd.img          /home/public/www/boot.toto.com/htdocs/clonezilla/live/
root@server: cp /mnt/live/filesystem.squashfs /home/public/www/boot.toto.com/htdocs/clonezilla/live/

root@server: umount /tmp
root@server: rm /tmp/clonezilla.iso

A nous maintenant le menu Tools depuis notre script iPXE :

#!ipxe

#dhcp

:start
menu Install OS
item --gap -- ----- No OS Boot -----------------------------------------
item local Local disk
item --gap -- ----- Tools ----------------------------------------------
item memtest MemTest+
item systemrescuecd SystemRescueCD
item clonezilla CloneZilla
item --gap -- ----- iPXE -----------------------------------------------
item shellipxe Shell
choose --default local --timeout 5000 target && goto ${target}

:memtest
kernel tools/memdisk iso raw
initrd tools/memtest86+.iso
boot

:systemrescuecd
kernel sysrcd/isolinux/rescue32
initrd sysrcd/isolinux/initram.igz
imgargs rescue32 netboot=http://boot.toto.com/sysrcd/sysrcd.dat docache setkmap=fr 
boot

:clonezilla
kernel clonezilla/live/vmlinuz
initrd clonezilla/live/initrd.img
imgargs vmlinuz boot=live username=user union=overlay config components quiet noswap edd=on nomodeset nodmraid locales=fr_FR.UTF-8 keyboard-layouts=fr ocs_live_run="ocs-live-general" ocs_live_extra_param="" ocs_
live_batch=no net.ifnames=0 nosplash noprompt fetch=http://boot.toto.com/clonezilla/live/filesystem.squashfs
boot

:shellipxe
shell

Nous y avons ajouté une option pour accéder au shell iPXE.

Scripts iPXE et PHP complet

Nous pourrions chaîner plusieurs scripts iPXE entre eux, mais le nombre d'options n'est pas assez important pour se lancer dans un sous-découpage (et iPXE peut avoir un menu plus grand que l'écran), alors nous regroupons tout dans un seul menu iPXE/script PHP :

root@server:/home/public/www/boot.toto.com/htdocs/boot.php

#!ipxe

#dhcp

:start
menu Install OS
item --gap -- ----- No OS Boot -----------------------------------------
item local Local disk
item --gap -- ----- Debian ---------------------------------------------
item debian-stable-amd64-text   Install Debian stable/amd64 from Debian repository
item debian-stable-i386-text    Install Debian stable/i386  from Debian repository
item debian-stable-amd64-auto   Install Debian stable/amd64 from Debian repository with Cinnamon desktop
item debian-stable-i386-auto    Install Debian stable/i386  from Debian repository with Cinnamon desktop
item debian-stable-amd64-rescue Rescue  Debian stable/amd64 from Debian repository
item debian-stable-i386-rescue  Rescue  Debian stable/i386  from Debian repository
item --gap -- ----- Windows - ------------------------------------------
item windows7      Install Windows 7 (by SMB/CIF share)
item windows7iscsi Install Windows 7 (by iSCSI share)
#item windows10 Windows 10
item --gap -- ----- External public menus ------------------------------
item salstar => Salstar (salstzar.sk)
item netbootxyz => NetBoot (netboot.xyz)
item --gap -- ----- Tools ----------------------------------------------
item memtest MemTest+
item systemrescuecd SystemRescueCD
item clonezilla CloneZilla
item --gap -- ----- iPXE -----------------------------------------------
item shellipxe Shell
choose --default local --timeout 5000 target && goto ${target}

:local
sanboot --no-describe --drive 0x80

:debian-stable-amd64-text
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
boot

:debian-stable-i386-text
<?php debian_with_firmware_nonfree('stable', 'i386'); ?>
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
boot

:debian-stable-amd64-auto
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
initrd http://boot.toto.com/preseed/standard.cfg preseed.cfg
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
boot

:debian-stable-i386-auto
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
initrd http://boot.toto.com/preseed/standard.cfg preseed.cfg
imgargs linux language=fr country=FR locale=fr_FR.UTF-8
boot

:debian-stable-amd64-rescue
<?php debian_with_firmware_nonfree('stable', 'amd64'); ?>
imgargs linux language=fr country=FR locale=fr_FR.UTF-8 rescue/enable=true
boot

:debian-stable-i386-rescue
<?php debian_with_firmware_nonfree('stable', 'i386'); ?>
imgargs linux language=fr country=FR locale=fr_FR.UTF-8 rescue/enable=true
boot

:windows7
kernel win7install/wimboot
initrd win7install/install.cmd             install.cmd
initrd win7install/winpeshl.ini            winpeshl.ini
initrd win7install/bcd                     BCD
initrd win7install/boot.sdi                boot.sdi
initrd win7install/boot.wim                boot.wim
prompt Press a key
boot

:windows7iscsi
kernel win7install/wimboot
initrd win7install/install_iscsi.cmd  install.cmd
initrd win7install/winpeshl.ini       winpeshl.ini
initrd win7install/bcd                BCD
initrd win7install/boot.sdi           boot.sdi
initrd win7install/boot.wim           boot.wim
sanhook --drive 0x86 iscsi:boot.toto.com:::1:iqn-2018-08.com.kozodo:images.boot 
prompt Press a key
boot

:memtest
kernel tools/memdisk iso raw
initrd tools/memtest86+.iso
boot

:systemrescuecd
kernel sysrcd/isolinux/rescue32
initrd sysrcd/isolinux/initram.igz
imgargs rescue32 netboot=http://boot.toto.com/sysrcd/sysrcd.dat docache setkmap=fr 
boot

:clonezilla
kernel clonezilla/live/vmlinuz
initrd clonezilla/live/initrd.img
imgargs vmlinuz boot=live username=user union=overlay config components quiet noswap edd=on nomodeset nodmraid locales=fr_FR.UTF-8 keyboard-layouts=fr ocs_live_run="ocs-live-general" ocs_live_extra_param="" ocs_live_batch=no net.ifnames=0 nosplash noprompt fetch=http://boot.toto.com/clonezilla/live/filesystem.squashfs
boot

:shellipxe
shell

:salstar
chain http://boot.salstar.sk

:netbootxyz
chain http://boot.netboot.xyz/

<?php

function debian_with_firmware_nonfree($dist, $arch) {
    $debian_repo   = 'debian.proxad.net';
    $root_dir      = 'debian';
    $packages_list = gzdecode(file_get_contents("http://$debian_repo/$root_dir/dists/$dist/non-free/binary-$arch/Packages.gz"));

    print "kernel http://$debian_repo/$root_dir/dists/$dist/main/installer-$arch/current/images/netboot/debian-installer/$arch/linux\n";
    print "initrd http://$debian_repo/$root_dir/dists/$dist/main/installer-$arch/current/images/netboot/debian-installer/$arch/initrd.gz\n";
    print "#initrd http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/$dist/current/firmware.cpio.gz\n";
    print "initrd http://boot.toto.com/empty_firmware_folder.cpio.gz\n";

    // Search all matching nonfree firmware
    preg_match_all('#Filename: (pool/non-free/f/firmware-nonfree/(firmware-.*.deb))#', $packages_list, $matches);    

    foreach ($matches[1] as $id => $file)
        print "initrd http://$debian_repo/$root_dir/$file /firmware/" . $matches[2][$id] . "\n";
}
?>

Notre beau menu apparaît donc sous la forme :

Boot iPXED sur CD ou clé USB

Et si l'on est pas sur le même LAN que le serveur DHCP ?

Eh bien on peut simplement récupérer un ISO à graver ou le contenu d'une clé USB, voir recompiler iPXE avec des options utiles embarqué dans le bootloader (ex: lien vers le script iPXE). Il suffit ensuite de botter dessus et de presser CTRL-B à l'affichage du boot iPXE, et saisir l'adresse du script iPXE (command chain) qui se trouve à l'autre 'bout' d'Internet :

Améliorations possibles

Il y a bien sûr beaucoup améliorations possibles :

  • sécuriser éventuellement l'accès iSCSI par initiator name (fixé dans le script iPXE).
  • copier le dépôt Debian en local pour éviter toute indisponibilité et réduire la charge des miroirs.
  • offrir plus de configuration preseed pour Debian (moins de questions, types d'installation plus nombreux...).
  • offir un Debian Live.
  • installer Windows 10.
  • ajouter des drivers (Dell...) à l'installeur Windows, et mettre à jour le DVD Windows (Windows Update ou avec Offline Updateur), préinstaller des logiciels (voir ninite.com).
  • ajouter plus d'outils.
  • chaîner vers plus de menu iPXE publiques.

Bonne dégustation

Ecrire à l'auteur