Table of Contents

IN PROGRESS

Création d'un CD d'installation automatisée pour Debian

La distribution GNU/Linux Debian permet une installation automatique non assistée en utilisant un fichier de préconfiguration preseed.cfg.

preseed.cfg contient les réponses pré-remplies aux questions posées lors d'une installation interactive classique.

La méthode décrite ci-dessous crée un CD d'installation automatisée.

Une procédure similaire d'installation par le réseau fera l'objet d'un document dédié.

Vue générale de la procédure d'installation classique

Lors de l'installation un système d'exploitation minimal est chargé en mémoire.

Ce système d'exploitation minimal a pour seule fonction d'installer sur le disque dur de l'ordinateur le système d'exploitation final.

La procédure d'installation interactive requiert la présence d'un administrateur du début à la fin et l'absence d'une réponse à une question suspend l'installation.

La présence physique d'un administrateur pour superviser cette opération n'est pas toujours possible et la fréquence à laquelle cette procédure doit être réalisée peut contrarier la faisabilité d'un projet.

Le fichier preseed.cfg

Le fichier preseed.cfg contient un ensemble d'instructions pour les différents composants de l'installateur.

Un exemple de fichier preseed.cfg est fourni par Debian à cette adresse et mérite d'être examiné.

Quand le programme installateur debian rencontre un fichier preseed.cfg à la racine du système d'exploitation minimal, il l'utilisera pour renseigner les composantes d'installation au lieu de solliciter une interaction.

En quoi consiste la création d'un CD d'installation non assistée

Le système d'exploitation minimal est modifié pour inclure le fichier preseed.cfg à sa racine.

Pour créer cette version automatisée de CD d'installation, il faut se munir des paquets suivants :

# apt-get install bsdtar genisoimage

Un CD d'installation classique est nécessaire. Le netinst de debian peut faire l'affaire pour le test mais cela fonctionne de la même manière pour les autres formats.

# wget debian-9.6.0-amd64-netinst.iso

Extraire le contenu de l'image nous permet d'avoir une copie modifiable. Le paquet bsdtar simplifie l'opération.

# mkdir isofiles
# bsdtar -C isofiles -xf debian-9.6.0-amd64-netinst.iso

Le contenu du CD d'installation est maintenant disponible à la modification dans le dossier isofiles.

Modification de l'initrd.gz

initrd.gz contient le système d'exploitation minimal.

Pour modifier cette archive, il faut la décompresser. Mais d'abord il faut veiller à autoriser l'écriture dans le dossier contenant initrd.gz.

# ls -l isofiles/install.amd/
total 20M
dr-xr-xr-x 2 sysadm sysadm 4,0K nov.  10 12:34 gtk
dr-xr-xr-x 2 sysadm sysadm 4,0K nov.  10 12:34 xen                             \o/
-r--r--r-- 1 sysadm sysadm  16M janv. 11 12:34 initrd.gz   <------------------- |
-r--r--r-- 1 sysadm sysadm   45 nov.  10 12:34 install.bat                     / \
-r--r--r-- 3 sysadm sysadm 4,1M nov.  10 12:34 vmlinuz
# chmod +w -R isofiles/install.amd/
# gunzip isofiles/install.amd/initrd.gz
# ls -l isofiles/install.amd/
total 52M
drwxr-xr-x 2 sysadm sysadm 4,0K nov.  10 12:34 gtk
drwxr-xr-x 2 sysadm sysadm 4,0K nov.  10 12:34 xen
-rw-r--r-- 1 sysadm sysadm  48M janv. 11 12:36 initrd
-rw-r--r-- 1 sysadm sysadm   45 nov.  10 12:34 install.bat
-rw-r--r-- 3 sysadm sysadm 4,1M nov.  10 12:34 vmlinuz

Incorporer pressed.cfg dans initrd et création du CD final

Une fois le fichier preseed.cfg préparé, il s'agit de l'incorporer dans l'initrd. initrd est une archive qui peut être manipulée avec la commande cpio installée par défaut. <a id=“org8ba0f68”></a>

# ls -F
isofiles/
preseed.cfg
# echo preseed.cfg | cpio -H newc -o -A -F isofiles/install.amd/initrd
[...]
# gzip isofiles/install.amd/initrd
# chmod -R -w isofiles/install.amd/

Une dernière étape est nécessaire avant la création de l'iso. L'installateur Debian s'appuie sur un fichier contenant la somme de contrôle de tous les fichiers contenus dans le CD pour les valider. Ce fichier est contenu à la racine du cd et est nommé md5sum.txt

Puisque nous avons modifié le fichier initrd.gz nous devons recalculer sa somme de contrôle pour la rapporter dans le fichier md5sum.txt

La commande suivante nous permet reconstruire md5sum.txt pour tous les fichiers contenus dans le projet d'iso.

# cd isofiles
# md5sum `find -follow -type f` | sudo tee md5sum.txt
# cd ..

Ça y est, le dossier isofiles est fin prêt à être converti en fichier iso.

Nous utilisons la commande genisoimage pour ce faire avec la syntaxe suivante :

# genisoimage -quiet -r -J -b isolinux/isolinux.bin \
              -c isolinux/boot.cat \
              -no-emul-boot \
              -boot-load-size 4 \
              -boot-info-table \
              -o DEBIAN-automatique.iso isofiles

La commande crée un fichier DEBIAN-automatique.iso à partir du contenu du dossier isofiles dans lequel nous avons construit un initrd.gz avec un preseed.cfg incorporé.

Il ne reste plus qu'à graver le CD sur un support usb ou disque optique, tester et profiter d'une installation automatisée.

Si celle-ci échoue il faudra modifier le fichier preseed.cfg et revenir dans le cycle : incorporation du preseed.cfg dans l'initrd.gz, reconstruction du fichier de sommes de calculs md5sum.txt et création de l'iso.

Cette opération peut elle-même être automatisée pour accélérer la procédure. Un exemple est donné en fin d'article.

Aller plus loin avec preseed/late_command

Un des éléments les plus intéressants dans cette méthode est la possibilité d'exécuter des commandes juste avant la fin de l'installation avec la clé preseed/late_command.

À la fin du preseed.cfg exemple fourni par Debian, nous pouvons lire

# This command is run just before the install finishes, but when there is
# still a usable /target directory. You can chroot to /target and use it
# directly, or use the apt-install and in-target commands to easily install
# packages and run commands in the target system.
 
# d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh

Quelques informations importantes sont données ici :

Dans l'exemple du pressed.cfg fourni par Debian, le paquet zsh est installé et fera partie du système d'exploitation final. Ensuite, avec in-target, la commande de changement de shell de connexion (chsh -s) est exécutée avec le disque dur contenant le système d'exploitation final comme environnement d'exécution.

Exemple d'exploitation de preseed/late_command

Après avoir maîtrisé la procédure de modification du système d'exploitation minimal, une customisation plus approfondi du système d'exploitation final est possible.

Rien n'empêche l'ajout d'autres fichiers que le preseed.cfg dans le système d'installation initrd.gz.

Nous pouvons y incorporer, une liste de clés publiques ssh et les injecter dans le système final. Ça nous permettra d'accéder à la machine sans mot de passe.

Nous pouvons également injecter des scripts d'initialisation qui s'exécuteront au premier redémarrage de la machine après la fin de l'installation.

Les exemples donnés ici sont triviaux et ne servent qu'à illustrer la procédure.

De la même manière que nous avons ajouté le fichier preseed.cfg dans initrd, nous allons ajouter un fichier contenant des clés ssh publiques et nous utilisons preseed/late_command pour les injecter dans le système d'exploitation final.

# echo preseed.cfg | cpio -H newc -o -A -F isofiles/install.amd/initrd
# echo authorized_keys | cpio -H newc -o -A -F isofiles/install.amd/initrd

Et dans le fichier preseed.cfg :

# [....]
d-i preseed/late_command string \
    in-target mkdir                 /root/.ssh; \
    cp /authorized_keys             /target/root/.ssh/; \
    in-target chmod 700             /root/.ssh; \
    in-target chmod 600             /root/.ssh/authorized_keys; \
    true;

Cycle de développement et de test du preseeding

Même en excluant la nécessité d'interaction lors de la procédure d'installation d'un système d'exploitation, l'opération demeure lourde.

Utiliser un système de virtualisation pour tester immédiatement le résultat est incontournable pour avancer en toute sécurité et pour maîtriser rapidement la procédure.

Ci-dessous un script qui fait usage de libvirt pour tester l'iso « preseedé ». Une introduction à libvirt est accessible à cette page.

#!/bin/bash
 
##########################################################################################
# This  script  help on  testing  preseed.cfg  for automatic  unattended debian install  #
#                                                                                        #
# The script needs genisoimage, bsdtar, syslinux-utils installed on your system          #
##########################################################################################
 
MAKE_VM="$1"            # calling this script with `vm' as argument launch the installation
                        # with the preseeded iso on a virsh domain
 
# OPDIR="$( cd "$(dirname "$0")" ; pwd -P )"/
ORIGIN_ISO="$(basename "/home/aziz/debian-9.6.0-amd64-netinst.iso")"
TARGET_VM=sarahdeb     # the name of the vm to create
DISC_SIZE=8
RAM_SIZE=512
CPU=1
PROVISION_FILES="postinstall.sh preseed.cfg rc.local authorized_keys" # files injected into the install process
 
RESULT_ISO=sarahdeb-${ORIGIN_ISO}        # 'generated iso name' will be 'origin iso name' prefixed with 'preseed-'
ORIGIN_ISO_PATH="/home/aziz/debian-9.6.0-amd64-netinst.iso"
RESULT_ISO_PATH=${OPDIR}${RESULT_ISO}
 
EXTRACTED_ISO_DIR=${ORIGIN_ISO/\.iso/}
EXTRACTED_ISO_PATH=/tmp/${EXTRACTED_ISO_DIR}/ # The path to directory containing extracted iso files
INSTALLAMD_DIR=install.amd/
INSTALLAMD_PATH=${EXTRACTED_ISO_PATH}${INSTALLAMD_DIR}
INITRDGZ_FILE=initrd.gz
INITRD_FILE=initrd
INITRDGZ_PATH=${INSTALLAMD_PATH}${INITRDGZ_FILE}        # path to initrd.gz
INITRD_PATH=${INSTALLAMD_PATH}${INITRD_FILE}
 
LIST_RUNNING_VM_COMMAND="virsh -c qemu:///system list --state-running --name"
LIST_ALL_VM_COMMAND="virsh -c qemu:///system list --all --name"
 
 
function check_deps(){
    dpkg-query -l bsdtar &> /dev/null ;
 
    if [ ! $? == "0" ];then
        sudo apt-get install -y bsdtar
    fi
 
    dpkg-query -l genisoimage &> /dev/null ;
 
    if [ ! $? == "0" ];then
        sudo apt-get install -y genisoimage
    fi
}
 
check_deps || exit 2;
 
function purge_vm(){
    # before beginning the vm will be deleted
    for i in $(eval $LIST_RUNNING_VM_COMMAND); do
        if [ $i == $TARGET_VM ];then
            virsh -c qemu:///system destroy $TARGET_VM
            break;
        fi
    done
 
    for i in $(eval $LIST_ALL_VM_COMMAND); do
        if [ $i == $TARGET_VM ];then
            virsh -c qemu:///system undefine $TARGET_VM --remove-all-storage
            break;
        fi
    done
}
 
function extract_iso(){
    cd $OPDIR
    mkdir $EXTRACTED_ISO_PATH
    bsdtar -C $EXTRACTED_ISO_PATH -xf $ORIGIN_ISO_PATH
}
 
function unlock (){ chmod +w -R $1; }   # to make a dir writable
 
function ziplock(){ chmod -w -R $1; }   # to revert to readonly
 
function reuse_extracted_iso_path(){
    # if an iso has been already extracted
    # just pick the initrd.gz file from a temporary mount
    # instead of extracting
    tempdir=$(mktemp -d)
    sudo mount -o loop $ORIGIN_ISO_PATH $tempdir
    unlock $INSTALLAMD_PATH
    rm $INITRDGZ_PATH
    cp $tempdir/${INSTALLAMD_DIR}${INITRDGZ_FILE} $INITRDGZ_PATH
    ziplock $INSTALLAMD_PATH
    sudo umount $tempdir
    rm -rf $tempdir
}
 
function rewrite_initrd(){
    # Usage: rewrite_initrd file1 file2
    # inject space separated list of files into initrd
    # then gzip initrd
    to_inject="$@"
    unlock $INSTALLAMD_PATH
    gunzip $INITRDGZ_PATH
    cd $OPDIR
    for file in $to_inject;
    do
        echo Adding $file to initrd
        echo $file | cpio -H newc -o -A -F $INITRD_PATH
    done
    gzip $INITRD_PATH
    ziplock $INSTALLAMD_PATH
}
 
 
function rewrite_isolinux_cfg() {
    unlock ${EXTRACTED_ISO_PATH}isolinux
    FILE=${EXTRACTED_ISO_PATH}isolinux/txt.cfg
    rm $FILE
    cat >$FILE <<EOF
default install
    label install
        menu label ^Install
        menu default
        kernel /${INSTALLAMD_DIR}vmlinuz
        append vga=788 initrd=/${INSTALLAMD_DIR}initrd.gz --- quiet
EOF
 
    sed -i 's/^timeout.*/timeout 1/' ${EXTRACTED_ISO_PATH}isolinux/isolinux.cfg
    sed -i 's/^timeout.*/timeout 1/' ${EXTRACTED_ISO_PATH}isolinux/prompt.cfg
 
    ziplock ${EXTRACTED_ISO_PATH}isolinux
 
}
 
function rebuild_md5sum_file(){
    # The md5sum.txt file must be rebuilt
    # after changing iso contents
    cd $EXTRACTED_ISO_PATH
    md5sum `find -follow -type f` | sudo tee md5sum.txt >/dev/null
    cd $OPDIR
}
 
function geniso(){
    # Generating the preseeded iso
    cd $OPDIR
    sudo genisoimage -quiet -r -J -b isolinux/isolinux.bin \
         -c isolinux/boot.cat \
         -no-emul-boot \
         -boot-load-size 4 \
         -boot-info-table \
         -o $RESULT_ISO_PATH $EXTRACTED_ISO_PATH
 
    sudo isohybrid $RESULT_ISO_PATH
}
 
function install_vm_from_preseed(){
    # Creating the vm to test install
    cd $OPDIR
    virt-install --connect qemu:///system \
                 --virt-type kvm \
                 --os-variant generic \
                 --os-type linux \
                 --network network=default \
                 --name=$TARGET_VM \
                 --disk size=$DISC_SIZE \
                 --ram=$RAM_SIZE \
                 --vcpus=$CPU \
                 --graphics vnc \
                 --console pty,target_type=serial \
                 --cdrom $RESULT_ISO_PATH
}
 
function main(){
    if [ ! -z $MAKE_VM ]; then
        purge_vm;
    fi
 
    if [ ! -d $EXTRACTED_ISO_PATH ]; then
        extract_iso;
    else
        reuse_extracted_iso_path;
    fi
 
    rewrite_isolinux_cfg;
    rewrite_initrd $PROVISION_FILES;
    rebuild_md5sum_file;
    geniso;
 
    if [ ! -z $MAKE_VM ]; then
        install_vm_from_preseed;
    fi
}
 
main
#purge_vm;
function install_vm_from_location(){
    cd $OPDIR
    virt-install --connect qemu:///system \
                 --virt-type kvm \
                 --os-variant generic \
                 --os-type linux \
                 --network network=default \
                 --name=$TARGET_VM \
                 --disk size=$DISC_SIZE \
                 --ram=$RAM_SIZE \
                 --vcpus=$CPU \
                 --graphics vnc \
                 --console pty,target_type=serial \
                 --initrd-inject preseed.cfg \
                 --extra-args "ipv6.disable=1 BOOT_DEBUG=0 DEBIAN_FRONTEND=text console=ttyS0,115200n8" \
                 --location "http://ftp2.de.debian.org/debian/dists/stretch/main/installer-amd64/"
}
#install_vm_from_location

Exemple d'un fichier preseed.cfg

preseed.cfg
#########################
# Keyboard and TimeZone #
#########################
d-i keyboard-configuration/xkb-keymap select fr(latin9)
d-i debian-installer/locale string fr_FR
d-i time/zone string Africa/Algiers
d-i clock-setup/ntp boolean true
d-i clock-setup/utc boolean true
###################
# Hardware detect #
###################
d-i hw-detect/load_firmware boolean true
#################
# User creation #
#################
d-i passwd/root-login boolean false
d-i passwd/user-fullname string dino
d-i passwd/username string dino
d-i passwd/user-password-again password dingo
d-i passwd/user-password password dingo
#################################
# Network and hostname settings #
#################################
d-i netcfg/get_hostname string dalton
d-i netcfg/hostname string dalton
 
d-i netcfg/choose_interface select auto
d-i netcfg/get_domain string
d-i netcfg/link_wait_timeout string 5
d-i netcfg/wireless_wep string
#########################
# Partitionning section #
#########################
d-i partman-auto/method string regular
d-i partman-auto/choose_recipe select atomic
d-i partman-auto/disk string /dev/sda
d-i partman/mount_style select uuid
d-i partman/choose_partition select finish
 
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/confirm boolean true
d-i partman-md/device_remove_md boolean true
 
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
 
#######################
# Apt mirror settings #
#######################
apt-mirror-setup	apt-setup/no_mirror	boolean	true
 
d-i mirror/country string manual
d-i mirror/http/directory string /debian
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/proxy string
d-i mirror/protocol string http
d-i mirror/suite string stretch
d-i mirror/udeb/suite string stretch
 
d-i apt-setup/contrib boolean true
d-i apt-setup/non-free boolean true
d-i apt-setup/security_host string security.debian.org
d-i apt-setup/services-select multiselect security, updates
d-i apt-setup/use_mirror boolean false
 
d-i base-installer/install-recommends boolean true
 
d-i debian-installer/allow_unauthenticated boolean true
############################
# Do not ask about more CD #
############################
apt-cdrom-setup	apt-setup/cdrom/set-double	boolean	false
apt-cdrom-setup	apt-setup/cdrom/set-first	boolean	false
apt-cdrom-setup	apt-setup/cdrom/set-next	boolean	false
#########################
# No popularity contest #
#########################
popularity-contest popularity-contest/participate boolean false
#######################
# Packages to install #
#######################
tasksel tasksel/first multiselect standard
d-i pkgsel/include string openssh-server
d-i pkgsel/upgrade select none
################
# Grub install #
################
d-i grub-installer/bootdev  string default
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
#################
# Late commands #
#################
d-i preseed/late_command string \
    in-target mkdir                 /home/dino/.ssh; \
    cp /authorized_keys             /target/home/dino/.ssh/; \
    in-target chown -R dino:        /home/dino/.ssh; \
    in-target chmod 700             /home/dino/.ssh; \
    in-target chmod 600             /home/dino/.ssh/authorized_keys; \
    true;
############
# Good Bye #
############
d-i finish-install/reboot_in_progress note