#!/bin/sh # # Prepare for FAI installation of Debian Edu. set -x LC_ALL=C export LC_ALL ## FIXME: Why is resolv.conf empty or missing? Because network ## was started in the chroot (target)? ## Try to find the DNS from the leases file, if that fails use ## default DNS: if [ ! -s /etc/resolv.conf ] ; then DNS="10.0.2.2" LEASEDIR=/var/lib/dhcp/ if [ -d $LEASEDIR ] ; then LEASEFILE=$LEASEDIR`ls -tr -1 $LEASEDIR | tail -n 1` if [ -r $LEASEFILE ] ; then if DNSLEASE=`cat $LEASEFILE | grep domain-name-servers | \ tail -n 1 | \ grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+"` ; then DNS=$DNSLEASE echo "info: Found leases file and domain-name-server: $DNS." else echo "info: Could not extract DNS from leases file." fi fi fi echo "info: Create temporary /etc/resolv.conf with DNS: $DNS." cat >> /etc/resolv.conf < /etc/resolvconf/run/resolv.conf, i.e.: ## rm /etc/resolv.conf; ln -s /etc/resolvconf/run/resolv.conf /etc/resolv.conf nameserver $DNS search intern EOF fi # Make sure the created directories and files are readable by tfptd # run as user nobody. umask 022 # Fetch ftp_proxy and http_proxy if set globally if [ -f /etc/environment ] ; then . /etc/environment fi ### ### FAI Setup ### [ "$archs" ] || archs=$(command -V dpkg 1>/dev/null 2>/dev/null && dpkg --print-architecture || uname -m) [ "$codenames" ] || codenames=$(cat /etc/os-release | grep VERSION_CODENAME | cut -d "=" -f2) [ "$http_proxy" ] || unset http_proxy [ "$ftp_proxy" ] || unset ftp_proxy [ "$mirrorurl" ] || mirrorurl=http://deb.debian.org/debian [ "$rootpw" ] || rootpw="" [ "$localuser" ] || localuser="" [ "$localuserpw" ] || localuserpw="" [ "$wifi_essid" ] || wifi_essid="" [ "$wifi_passphrase" ] || wifi_passphrase="" [ "$tftpdir" ] || tftpdir="/srv/tftp" [ "$fai_logserver" ] || unset fai_logserver [ "$fai_loguser" ] || unset fai_loguser [ "$school_tag" ] || school_tag="SKOLE" [ "$http_proxy" ] || unset http_proxy # required for pre-selecting the default boot item in iPXE config [ "$default_arch" ] || default_arch="$(echo ${archs} | cut -d " " -f1)" [ "$default_codename" ] || default_codename=$(echo ${codenames} | cut -d " " -f1) # only set plymouth theme if known by the desktop-theme ... if [ -e /etc/alternatives/desktop-theme/plymouth ]; then [ "$theme" ] || theme="$(ls -L /etc/alternatives/desktop-theme/plymouth | grep script | cut -d'.' -f 1)" fi # source Debian Edu's config file if [ -f /etc/debian-edu/config ] ; then . /etc/debian-edu/config fi # source debian-edu-fai's config file # Allow site specific overrides to the variables if [ -f /etc/debian-edu/debian-edu-fai.conf ] ; then . /etc/debian-edu/debian-edu-fai.conf fi # derived from mirrorurl... [ "$apt_cdn" ] || apt_cdn="$(echo "$mirrorurl" | sed -E -e 's@(.*://[^/]+)/.*@\1@g')" # keep a copy of /srv/tftp/ltsp if this is the first attempt to deploy # debian-edu-fai on this system if [ -d "${tftpdir}/debian-edu-fai" ] && [ ! -h "${tftpdir}/debian-edu-fai" ]; then mv "${tftpdir}/ltsp" "${tftpdir}/ltsp.moved-aside-by-debian-edu-fai" elif [ -h "${tftpdir}/debian-edu-fai" ]; then rm "${tftpdir}/debian-edu-fai" fi # For Debian Edu, we will create the FAI tftp boot dir in subfolder ltsp/. mkdir -p "${tftpdir}/ltsp/" # and symlink that to what really is inside... ln -nsf "ltsp" "${tftpdir}/debian-edu-fai" # Start from a clean state after any configuration changes have been made. rm -f $tftpdir/ltsp/fai-installers.cfg.~ rm -f $tftpdir/ltsp/fai-menuitems.cfg.~ rm -f $tftpdir/ltsp/debian-edu-fai.ipxe rm -f $tftpdir/ltsp/ltsp.ipxe rm -f $tftpdir/ltsp/memtest* rm -f $tftpdir/ltsp/snponly.efi rm -f $tftpdir/ltsp/undionly.kpxe # prepare menufile creation (always start fresh) menuitems=${tftpdir}/debian-edu-fai/fai-menuitems.cfg menuindex=1 menufile=${tftpdir}/debian-edu-fai/fai-installers.cfg if [ -e "${menuitems}" ]; then mv "${menuitems}" "${menuitems}.~" fi if [ -e "${menufile}" ]; then mv "${menufile}" "${menufile}.~" fi touch "${menuitems}" touch "${menufile}" for codename in ${codenames}; do # skip codenames that don't sound like Debian suites... if ! echo "bullseye bookworm trixie forky sid unstable" | grep -q "${codename}"; then echo "WARNING: The name '${codename}' is not a known and recent Debian distribution codename. Skipping..." continue fi # iterate over configured FAI client architectures... for arch in ${archs}; do set +x echo echo "###" echo "### Creating/updating FAI server configuration" echo "### (codename: ${codename}, architecture: ${arch})" echo "###" set -x # create codename based fai base config faiconfig="/etc/debian-edu/fai/debian-edu-fai.${arch}+${codename}" if [ -d /etc/debian-edu/fai/debian-edu-fai.TEMPLATE ]; then if [ -d "${faiconfig}" ]; then rm -Rf "${faiconfig}" fi cp -a /etc/debian-edu/fai/debian-edu-fai.TEMPLATE "${faiconfig}" touch "${faiconfig}/DONT_MODIFY_FILES_IN_THIS_DIRECTORY" else echo "ERROR: Failed to create FAI configuration in ${faiconfig}, no debian-edu-fai.TEMPLATE directory found" exit 1 fi # fill in placeholders (@rootpw@, @codename@ and @arch@) in nfsroot.conf.in find "${faiconfig}" -name '*.in' | while read file_to_adapt; do cp ${file_to_adapt} ${file_to_adapt%.in} [ "$rootpw" ] && export rootpw && perl -p -e "s/\@rootpw\@/\$ENV{rootpw}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$mirrorurl" ] && export mirrorurl && perl -p -e "s/\@mirrorurl\@/\$ENV{mirrorurl}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$codename" ] && export codename && perl -p -e "s/\@codename\@/\$ENV{codename}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$arch" ] && export arch && perl -p -e "s/\@arch\@/\$ENV{arch}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" # FIXME: also comment out variables that are not set (anymore) in /etc/debian-edu/faiinstall.cfg [ "$fai_logserver" ] && export fai_logserver && perl -p -e "s/^(#|)LOGSERVER=.{0,1}\@fai_logserver\@.{0,1}\s*\$/LOGSERVER=\'\$ENV{fai_logserver}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$fai_loguser" ] && export fai_loguser && perl -p -e "s/^(#|)LOGUSER=.{0,1}\@fai_loguser\@.{0,1}\s*\$/LOGUSER=\'\$ENV{fai_loguser}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" # hack for non-free-firmware repo area added since Debian 12 (aka bookworm) [we only support Debian 11 (aka bullseye) and upwards] if [ "$codename" = "bullseye" ]; then perl -p -e "s/ non-free-firmware//g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" fi chown root:root ${file_to_adapt%.in} chmod 0600 ${file_to_adapt%.in} rm ${file_to_adapt} done # source the NFS conf file... this might override our FAI_CONFIGDIR # (but should not as recommended in our nfsroot.conf.in template) if [ -f "$faiconfig/nfsroot.conf" ]; then . $faiconfig/nfsroot.conf else echo "ERROR: No nfsroot.conf file found in $faiconfig/, can't continue..." exit 1 fi # hard-code some sensible defaults in case they have been commented out in $faiconfig/nfsroot.conf [ "$FAI_DEBOOTSTRAP" ] || FAI_DEBOOTSTRAP="${codename} http://deb.debian.org/debian" [ "$FAI_ROOTPW" ] || FAI_ROOTPW="${rootpw}" [ "$NFSROOT" ] || NFSROOT="/srv/fai/nfsroot.debian-edu-fai/${arch}+${codename}" [ "$TFTPROOT" ] || TFTPROOT="${tftpdir}/debian-edu-fai.${arch}+${codename}" [ "$NFSROOT_HOOKS" ] || NFSROOT_HOOKS="/etc/debian-edu/fai/debian-edu-fai.${arch}+${codename}/" [ "$FAI_DEBOOTSTRAP_OPTS" ] || FAI_DEBOOTSTRAP_OPTS="--arch=${arch}" [ "$FAI_CONFIGDIR" ] || FAI_CONFIGDIR="/srv/fai/config" FAI_CONFIGDIR_REAL="${FAI_CONFIGDIR}" # if FAI_CONFIGDIR is a symlink, we need to find the real location... if [ -h ${FAI_CONFIGDIR} ]; then FAI_CONFIGDIR_REAL="$(readlink ${FAI_CONFIGDIR})" fi set +x echo echo "###" echo "### Installing/updating FAI config space (this takes some time)" echo "### (codename: ${codename}, architecture: ${arch})" echo "###" debian-edu-fai_updateconfigspace "${FAI_CONFIGDIR_REAL}" set -x # Update variables to be customized in FAI config space # This code block might be executed on the same FAI_CONFIGDIR several times # (once per arch and codename). # This is a known issue and works as designed. People might have chosen to # use difference FAI_CONFIGDIR values for different environments and with # such a choice executing the below per arch and per codename makes sense. set +x echo echo "###" echo "### Tweaking FAI config space" echo "### (codename: ${codename}, architecture: ${arch})" echo "###" set -x find ${FAI_CONFIGDIR_REAL} -name '*.in' | while read file_to_adapt; do cp ${file_to_adapt} ${file_to_adapt%.in} # FIXME: also comment out variables that are not set (anymore) in /etc/debian-edu/debian-edu-fai.conf [ "$rootpw" ] && export rootpw && perl -p -e "s/^(#|)ROOTPW=.{0,1}\@rootpw\@.{0,1}\s*\$/ROOTPW=\'\$ENV{rootpw}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$localuser" ] && export localuser && perl -p -e "s/^(#|)username=.{0,1}\@localuser\@.{0,1}\s*\$/username=\'\$ENV{localuser}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$localuserpw" ] && export localuserpw && perl -p -e "s/^(#|)USERPW=.{0,1}\@localuserpw\@.{0,1}\s*\$/USERPW=\'\$ENV{localuserpw}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$wifi_essid" ] && export wifi_essid && perl -p -e "s/^(#|)wifi_essid=.{0,1}\@wifi_essid\@.{0,1}\s*\$/wifi_essid=\'\$ENV{wifi_essid}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$wifi_passphrase" ] && export wifi_passphrase && perl -p -e "s/^(#|)wifi_passphrase=.{0,1}\@wifi_passphrase\@.{0,1}\s*\$/wifi_passphrase=\'\$ENV{wifi_passphrase}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$fai_logserver" ] && export fai_logserver && perl -p -e "s/^(#|)LOGSERVER=.{0,1}\@fai_logserver\@.{0,1}\s*\$/LOGSERVER=\'\$ENV{fai_logserver}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$fai_loguser" ] && export fai_loguser && perl -p -e "s/^(#|)LOGUSER=.{0,1}\@fai_loguser\@.{0,1}\s*\$/LOGUSER=\'\$ENV{fai_loguser}\'\n/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$school_tag" ] && export school_tag && perl -p -e "s/\@school_tag\@/\$ENV{school_tag}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$mirrorurl" ] && export mirrorurl && perl -p -e "s/\@mirrorurl\@/\$ENV{mirrorurl}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$apt_cdn" ] && export apt_cdn && perl -p -e "s/\@apt_cdn\@/\$ENV{apt_cdn}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" [ "$http_proxy" ] && export http_proxy && perl -p -e "s/^(#|)APTPROXY=\@http_proxy\@/APTPROXY=\$ENV{http_proxy}/g" "${file_to_adapt%.in}" > "${file_to_adapt%.in}.new" && mv "${file_to_adapt%.in}.new" "${file_to_adapt%.in}" chown root:root ${file_to_adapt} chmod 0600 ${file_to_adapt} done # set APTPROXY for use by fai-make-nfsroot... if [ -n "${http_proxy}" ]; then export APTPROXY="${http_proxy}" export http_proxy fi set +x echo echo "###" echo "### Creating FAI nfsroot installer environment" echo "### (codename: ${codename}, architecture: ${arch})" echo "###" # the NFSROOT variable we should have obtained from sourcing $faiconfig/nfsroot.conf # (aka /etc/fai/nfsroot.conf) above... if [ -n "${NFSROOT}" ] && [ -n "${codename}" ]; then # create nfs-root from scratch (if not present or not fully created in a previous run) # Create a ".DEBIAN_EDU_FAI_NFSROOT_INSTALLATION_COMPLETED" file at the end # of fai-make-nfsroot and check for the presence of that file for detecting # whether a fresh NFSROOT setup is required or just an NFSROOT update/upgrade. if [ ! -f "${NFSROOT}/.DEBIAN_EDU_FAI_NFSROOT_INSTALLATION_COMPLETED" ]; then # enforce NFSROOT re-creation (or initial creation) mkdir -p "${NFSROOT}/proc" mount -t proc proc "${NFSROOT}/proc" TMPDIR=/tmp fai-make-nfsroot -v -f -N -C ${faiconfig} touch "${NFSROOT}/.DEBIAN_EDU_FAI_NFSROOT_INSTALLATION_COMPLETED" else mount -t proc proc "${NFSROOT}/proc" # update packages (and clean old kernel images) in NFSROOT TMPDIR=/tmp fai-make-nfsroot -v -k -N -C ${faiconfig} # adjust nfsroot configuration (SSH pubkeys, rootpw, etc.) TMPDIR=/tmp fai-make-nfsroot -v -a -C ${faiconfig} fi [ -d "${NFSROOT}/proc/self" ] && umount "${NFSROOT}/proc" [ -d "${NFSROOT}/sys/class" ] && umount "${NFSROOT}/sys" # Remove /srv/tftp/debian-edu-fai.ARCH+CODENAME after NFSROOT creation. # We don't need that as we use our own iPXE boot config (instead # of syslinux which is used by FAI by default). if [ -d "${TFPTROOT}/" ]; then rm -Rf "${TFTPROOT}/" fi # symlink kernel and initrd files into if [ -e "${NFSROOT}/vmlinuz" ] && [ -e "${NFSROOT}/initrd.img" ]; then # create kernel dir in tftp area mkdir -p "${tftpdir}/debian-edu-fai/${arch}+${codename}/" # symlink vmlinuz / initrd in the NFSROOT if [ -e "${tftpdir}/debian-edu-fai/${arch}+${codename}/vmlinuz" ]; then rm "${tftpdir}/debian-edu-fai/${arch}+${codename}/vmlinuz" fi cp -a "${NFSROOT}/$(readlink "${NFSROOT}/vmlinuz")" "${tftpdir}/debian-edu-fai/${arch}+${codename}/vmlinuz" if [ -e "${tftpdir}/debian-edu-fai/${arch}+${codename}/initrd.img" ]; then rm "${tftpdir}/debian-edu-fai/${arch}+${codename}/initrd.img" fi cp -a "${NFSROOT}/$(readlink "${NFSROOT}/initrd.img")" "${tftpdir}/debian-edu-fai/${arch}+${codename}/initrd.img" fi fi cat >> "${menuitems}" <> "${menufile}" < "${tftpdir}/debian-edu-fai/ltsp.ipxe" #!ipxe # # Configure iPXE for network installations # Set the default image (img) based on arch, or to root-path if it's not empty #cpuid --ext 29 && set img amd64+${codename} || set img i386+${codename} # choose default boot entry (via configurable variables, see /etc/debian-edu/debian-edu-fai.conf) set img ${default_arch}+${default_codename} goto start :start # To completely hide the menu, set menu-timeout to -1 isset \${menu-timeout} || set menu-timeout 5000 iseq "\${menu-timeout}" "-1" && goto \${img} || menu Debian Edu iPXE boot menu || goto \${img} item item --gap Debian Edu installation: $(cat ${menuitems}) item item --gap Other options: item --key m memtest Memory test item --key c config Enter iPXE configuration item --key s shell Drop to iPXE shell item --key d disk Boot from the first local disk item item --key x exit Exit iPXE and continue BIOS boot choose --timeout \${menu-timeout} --default \${img} img || goto cancel goto \${img} :memtest iseq \${platform} pcbios && kernel ${memtest_bios} || kernel ${memtest_efi} # Boot "fails" on normal memtest exit with Esc, so show the menu again boot || goto start :config config goto start :shell echo Type 'exit' to get the back to the menu shell goto start :disk # Boot the first local HDD sanboot --no-describe --drive 0x80 || goto failed :exit exit 1 :cancel echo You cancelled the menu, dropping to a shell goto shell :failed echo Booting failed, dropping to a shell goto shell EOF cat $menufile >> "${tftpdir}/debian-edu-fai/ltsp.ipxe" ln -sf ltsp.ipxe "${tftpdir}/debian-edu-fai/debian-edu-fai.ipxe"