26 октября 2019

Как я всякую дичь через PXE грузил.



Загрузка по сети - это очень нужная и полезная в хозяйстве вещь. Достаточно подключить кабель к сетевой карте, нажать кнопку питания и выбрать сетевую карту в качестве загрузочного устройства. Никаких дисков, никаких флешек. Вот только настраивать всё это сложнее.

Как обычно я нахватался информации по разным сайтам, собрал всё это в кашу в голове и что-то там сделал, что вроде как работает.

Про настройку DHCP-сервера я уже написал, напишу теперь про настройку сервера, с которого непосредственно всё будет грузиться.

Сетевую загрузку через UEFI я не пробовал, потому что просто не на чем, но не думаю, что там будет много отличий.

Все действия я производил на ОС Debian 10 "Buster".

Для начала надо поставить tftp-сервер и http-сервер. В качестве второго я использовал lighttpd, но можно и любой другой. Он нужен для быстрой загрузки файлов по сети в процессе загрузки.
tftp-сервер нужен на этапе начальной загрузки, когда прошивка подгружает PXE-загрузчик. В качестве tftp-сервера я использовал "tftpd-hpa" из одноимённого пакета. Настраивается он с помощью единственного файла "/etc/default/tftpd-hpa":

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

"TFTP_USERNAME" - имя пользователя, с правами которого будет запущен сервер.
"TFTP_DIRECTORY" - каталог с файлами, которые будут доступны для загрузки.
"TFTP_ADDRESS" - интерфейс, на котором сервер будет ждать подключения.
"TFTP_OPTIONS" - дополнительные опции. В данном случае включается подробный лог и переход в каталог с файлами сразу после запуска.

Корневой каталог tftp-сервера должен быть доступен через http-сервер.

Теперь в каталог надо положить необходимые для загрузки файлы. Для этого я установил следующие пакеты:
sudo apt install syslinux syslinux-common syslinux-efi syslinux-utils pxelinux

В корневой каталог tftp-сервера необходимо скопировать следующие файлы и каталоги:
/usr/lib/PXELINUX/lpxelinux.0 - непосредственно загрузчик, который будет запускать прошивка материнской платы. В том же каталоге есть ещё файл "pxelinux.0", но он умеет работать только с tftp и не умеет загружать файлы через http.
/usr/lib/syslinux/modules/bios - все модули загрузчика. Весят они мало, а вот угадывать зависимости потом не придётся.
Файл "ldlinux.c32" из предыдущего пункта необходимо скопировать в корень, иначе загрузка закончится ошибкой.

В текущем состоянии загрузчик запустится, но будет жаловаться, что не смог найти свой конфигурационный файл и некоторые модули, лежащие в каталоге "bios". Для решения этой проблемы надо создать файл "default" в каталоге "pxelinux.cfg" с примерно таким содержанием:

PATH bios
DEFAULT vesamenu.c32
TIMEOUT 600
MENU TITLE PXE BOOT MENU

В данном куске задаётся путь поиска модулей ("PATH"), драйвер для отображения графического меню вместо текстового, таймаут выбора пункта ("TIMEOUT") и заголовок ("MENU TITLE").

Теперь надо чем-нибудь заполнить меню. Например, добавим загрузку Hiren's BootCD 15.2. Для этого в корень tftp сервера надо положить файл "memdisk" из каталога "/usr/lib/syslinux" и непосредственно сам iso-файл "Hirens.BootCD.15.2.iso". Теперь в конфиг загрузчика после меню необходимо дописать следующее:

LABEL HIREN
MENU LABEL Hirens's boot CD 15.2
LINUX  http://192.168.1.30/tftp/memdisk
INITRD http://192.168.1.30/tftp/Hirens.BootCD.15.2.iso
APPEND iso

Здесь происходит загрузка ядра "memdisk" и рамдиска "Hirens.BootCD.15.2.iso". После загрузки появится привычное меню диска. Некоторые пункты грузиться откажутся, некоторые загрузятся с ошибкой. Такова цена эмуляции диска из памяти. В принципе сам загрузчик расчитан на использование его для запуска ядра Linux, по этой причине опции имеют такое название.

Кроме syslinux есть еще более навороченный загрузчик iPXE. Изначально он разрабатывался как альтернативное решение для прошивки чипов bootrom сетевых карт, но может работать и как PXE-загрузчик.

С ним мне пришлось довольно сильно повозиться.
Сам загрузчик пришлось собирать из исходников по инструкции https://ipxe.org/download. В результате получается целая пачка разных вариантов загрузчика, которые в принципе делают одно и то же, но запускаются разными путями. Как такового конфигурационного файла у него нет, но есть скриптовый язык, на нём некоторое подобие конфига и пишется.

Для загрузки по сети используется файл "ipxe.pxe". Его надо положить в корень tftp-сервера и прописать имя в настройках DHCP-сервера. При загрузке он будет в бесконечном цикле настраивать сеть и перезапускаться. Чтобы такого избежать, надо написать минимальный скрипт и пересобрать загрузчик. Я создавал в каталоге "src" с исходниками файл "boot.ipxe" с таким содержанием:

#!ipxe

:start
dhcp
chain tftp://${next-server}/boot.ipxe || goto shell

:shell
echo Type 'exit' to get the back to the menu
shell
set menu-timeout 0 set submenu-timeout 0
goto start

И собирал загрузчик такой командой:

make bin/ipxe.pxe EMBED=boot.ipxe

Этот скрипт пытается настроить сеть, загрузить с tftp-сервера скрипт "boot.ipxe" и запустить его. В случае неудачи запускается встроенная оболочка загрузчика.

В итоге получается, что для настройки загрузчика достаточно будет изменить файл "boot.ipxe" без пересборки.

На сайте написано, что "chain" достаточно передать имя файла на tftp-сервере без указания полного адреса. Это работает из встроенной оболочки, но при загрузки с помощью встроенного скрипта появляется ошибка. Пришлось добавить сборку полного адреса с помощью переменной "${next-server}" из ответа DHCP-сервера для обхода такого поведения.

Если же надо загружать iPXE из меню syslinux, то надо брать файл "ipxe.lkrn". Никакой скрипт включать в него не надо, потому что его можно подгрузить инструкциями отдельно. Я сделал загрузку примерно так:

LABEL IPXE
MENU LABEL iPXE Boot Menu
KERNEL http://192.168.1.30/tftp/ipxe.lkrn
INITRD http://192.168.1.30/tftp/boot.ipxe

Вариант с использованием tftp-сервера:

LABEL IPXE
MENU LABEL iPXE Boot Menu
KERNEL ipxe.lkrn
INITRD boot.ipxe

Сам скриптовый язык мне чем-то напоминает batch/cmd. Там нет функций, но есть условия, метки и переходы.

Во время экспериментов я собрал вот такой скрипт. В нём есть основное меню и подменю, загрузка из iso-образов, загрузка ядра и ram-диска непосредственно из сети.

Чтобы не дублировать код, можно использовать некое подобие вызова функций с помощью перехода по меткам. Для загрузки образов WinPE я сделал такую "функцию":

:winpeload
dhcp
kernel ${boot-url}/wimboot pause gui
initrd ${boot-url}/${pearch}/media/Boot/BCD                     BCD
initrd ${boot-url}/${pearch}/media/Boot/boot.sdi                boot.sdi
initrd ${boot-url}/${pearch}/media/sources/boot.wim             boot.wim
imgstat
boot

В ней используются переменная "pearch", которая устанавливаются перед переходом по метке:

:winpe1064
set pearch win10pe64
goto winpeload

Загрузчик wimboot можно скачать с сайта iPXE по ссылке на странице http://ipxe.org/wimboot

Похожую функцию я сделал и для iso-образов.

Образы я пробовал грузить двумя способами.

Первый способ - это загрузка с помощью "memdisk" на манер syslinux.
Второй способ - использование команды "sanboot". В этом случае файл не загружается полностью, а подгружается по мере необходимости. Так же эта команда позволяет загружаться через iSCSI.

Сложнее было заставить нормально грузиться по сети SystemRescueCd. По инструкции на сайте вроде бы всё правильно адаптировал для iPXE, но ядро при загрузке упорно пыталось искать диск, а не грузило образ по сети.

Оказалось, что syslinux тихо добавляет ядру параметры для настройки сети, а вот iPXE делает только то, что прописано в скрипте. В итоге оказалось, что для правильно загрузки ядру необходимо было передать параметр "ip=::", тогда и начиналась нормальная загрузка по сети. Кусок скрипта получился такой:

:sysresccd
dhcp
kernel --name vmlinuz ${boot-url}/sysresccd/boot/x86_64/vmlinuz archisobasedir=tftp/sysresccd archiso_http_srv=http://${http-server}/ ip=:: checksum
initrd ${boot-url}/sysresccd/boot/intel_ucode.img
initrd ${boot-url}/sysresccd/boot/amd_ucode.img
initrd ${boot-url}/sysresccd/boot/x86_64/sysresccd.img
boot

С загрузкой WinPE с помощью "wimboot" тоже не всё просто. Внутри файла "boot.wim" обязательно должен содержаться каталог "Windows\Boot\PXE" с необходимыми файлами для загрузки. Без него загрузка закончится ошибкой ещё до запуска системы. Например, в Hiren’s BootCD PE такого каталога нет, но диск всё равно можно будет загрузить с помощью ISO-образа.

Хотя я раньше и писал, что загрузка файлов по http заметно быстрее загрузки по tftp, но при использовании syslinux это не сильно заметно, а вот при использовании iPXE файлы по http скачиваются практически так же быстро, как это происходило бы при использовании браузера. Точно с секундомером не сравнивал, но на глаз разница между этими двумя загрузчиками огромная.