Все в порядке, но.
Этот текст мало кто будет читать и мы можем написать здесь все, что угодно, например.
Вы живете в неведении. Роботы уже вторглись в нашу жизнь и быстро захватывают мир, но мы встали на светлый путь и боремся за выживание человечества. А если серьезно, то.
В целях обеспечения безопасности сайта от кибератак нам необходимо убедиться, что вы человек. Если данная страница выводится вам часто, есть вероятность, что ваш компьютер заражен или вы используете для доступа IP адрес зараженных компьютеров.
Если это ваш частный компьютер и вы пытаетесь зайти на сайт, например, из дома — мы рекомендуем вам проверить ваш компьютер на наличие вирусов.
Если вы пытаетесь зайти на сайт, например, с работы или открытых сетей — вам необходимо обратиться с системному администратору и сообщить, что о возможном заражении компьютеров в вашей сети.
- © 2005-2023, «4PDA». 4PDA® — зарегистрированный товарный знак.
IPXE
iPXE (прежние названия gPXE и Etherboot) — свободное программное обеспечение для создания загрузочных ПЗУ для загрузки Linux и других операционных систем на компьютерах с архитектурой x86 по сети с использованием межсетевых протоколов.
Использование
iPXE может быть загружено несколькими путями:
- с таких носителей информации, как дискета, USB-диск или жесткий диск
- в виде псевдо Linux kernel
- как ELF image
- с помощью BIOS
- по компьютерной сети, как PXE загрузочный образ
См. также
Ссылки
- Официальный сайт (англ.)
- Introduction to Network Booting and Etherboot (англ.)
- Программное обеспечение по алфавиту
- Свободное сетевое программное обеспечение
- Загрузка компьютера
Wikimedia Foundation . 2010 .
iPXE — заливка linux, windows, утилит по сети
Раньше у нас использовалась заливка ОС по PXE с использованием syslinux, но как бывает с самодостаточными системами и без достаточного контроля и внимания — быстро превратилась в кашу из конфигураций, библиотек, загрузчиков.
Так же была необходимость в поддержке заливки разных ОС в режиме EFI — на тот момент заливка была только в Legacy.
В желании разобраться в этом наследстве, упростить и облегчить добавление новых образов в список, я познакомился с iPXE.
Оглавление
- Подготовка стендового сервера
- Сборка загрузчиков
- Написание меню загрузки
- Добавление образов debian, windows, утилит
- Итог
1. Подготовка стенда
Раздел частично отсылает к Загрузочный сервер — как загрузочная флешка. , первичная подготовка схожа — для базы используется чистый Debian Bullseye, но думаю, использование другого дистрибутива не будет сильно отличаться.
Дано
- VM VirtualBox
- Ram: 1 Гб
- VDI: 8Gb
- CPU: 1 Core
- Network: — NAT, подсеть 10.0.2.0, без DHCP, шлюз 10.0.2.1
1.1. TFTP
Используется для загрузчиков и ipxe скриптов:
apt install tftpd-hpa
Вношу изменения в /etc/default/tftpd-hpa:
TFTP_USERNAME="tftp" TFTP_DIRECTORY="/srv/tftp" # путь к папке tftp TFTP_ADDRESS="0.0.0.0:69" TFTP_OPTIONS="--secure -l -vvv -r blksize -m /etc/tftpd.remap"
Замена слешей Windows на Linux /etc/tftpd.remap:
1.2. Samba
Используется для установки Windows-образов, в процессе монтируя папку образа, как диск:
apt install samba
В конце /etc/samba/smb.conf добавил:
[images] comment = images path = /srv/tftp/images create mask = 0660 directory mask = 0771 writable = yes guest ok = yes browseable = yes locking = no public = yes
Создал папку, выдал права, перезапустил samba:
mkdir /srv/tftp/images chmod 777 /srv/tftp/images service smbd restart
1.3. Apache2
Используется для загрузки образа WinPE, Linux и прочих данных по протоколу HTTP (быстрее TFTP):
apt install apache2
Настройки на стенде по умолчанию, в папке /var/www/html/ создаю директории:
- /images/ — для папок ОС
- /photo/ — для фонов меню, но не обязательно
mkdir -p /var/www/html/images/photo
1.4. DHCP-сервер
Выдает IP адреса в сети. Для настройки iPXE на Windows Server лучше обратиться к документации.
apt install isc-dhcp-server
Задаю статический IP адрес для стенда.
/etc/network/interfaces:
allow-hotplug ens0s3 iface ens33 inet static address 10.0.2.5 netmask 255.255.255.0 broadcast 10.0.2.255 network 10.0.2.0 gateway 10.0.2.1
Назначаю интерфейс для прослушивания запросов.
/etc/default/isc-dhcp-server:
INTERFACESv4="enp0s3"
Формирую DHCP подсеть и добавляю ключи для загрузки с iPXE.
option domain-name "local.loc"; option domain-name-servers 10.0.2.1; # Переменная для определения архитектуры клиента: option arch code 93 = unsigned integer 16; # Объявляю пространство имен для ipxe option space ipxe; option ipxe.no-pxedhcp code 176 = unsigned integer 8; default-lease-time 600; max-lease-time 7200; ddns-update-style none; # Возможность получать клиентам IP-адреса authoritative; # Выделение подсети subnet 10.0.2.0 netmask 255.255.255.0 < range 10.0.2.100 10.0.2.200; option domain-name-servers 10.0.2.1; option domain-name "local.loc"; option routers 10.0.2.1; option broadcast-address 10.0.2.255; default-lease-time 600; max-lease-time 7200; ># Отключение задержки в iPXE ожидания прокси DHCP. # Экономит пару секунд. option ipxe.no-pxedhcp = 1; # Используя ранее установленную переменную, # в зависимости от режима загрузки клиента, # идет выбор, какой загрузчик отправить клиенту: if option arch != 00:00 < filename "ipxe.efi"; >else < filename "undionly.kpxe"; ># Установка корневой директории с загрузчиками и конфигурациями next-server 10.0.2.5; option tftp-server-name "10.0.2.5"; option root-path "tftp://10.0.2.5:/srv/tftp/";
На всякий случай проверяю синтаксис конфигурации на ошибки:
dhcpd -t -cf /etc/dhcp/dhcpd.conf
Если всё хорошо, указания ошибок в выводе не будет:
Internet Systems Consortium DHCP Server 4.4.1 Copyright 2004-2018 Internet Systems Consortium. All rights reserved. For info, please visit https://www.isc.org/software/dhcp/ Config file: /etc/dhcp/dhcpd.conf Database file: /var/lib/dhcp/dhcpd.leases PID file: /var/run/dhcpd.pid
- Настройка DHCP для PXE
- Отдача нужного загрузчика, в зависимости от режима загрузки — Legacy/UEFI
- Список кодов DHCP сервера
2. Сборка загрузчиков iPXE
В данном разделе будет:
- Загрузка пакетов
- Скрипт стартовой настройки
- Сборка загрузчиков
2.1. Загрузка пакетов
После настройки и проверки стенда, необходимо собрать из исходного кода загрузчики iPXE для режимов Legacy и UEFI.
- gcc
- binutils
- make
- perl
- liblzma или xz header files
- mtools
apt install gcc binutils make perl liblzma-dev mtools git
Скачиваю архив кода iPXE, либо клонирую репозиторий. Захожу во внутрь:
git clone git://git.ipxe.org/ipxe.git cd ipxe/src/
Большинство возможностей iPXE по умолчанию закомментировано и при сборке бинарников, будет недоступно. Полный перечень функций указан тут, перебирая разделы можно выбрать, что нужно и где включить.
В текущем случае нужно:
- в файле config/gereral.h раскомментировать строки с CONSOLE_CMD, IMAGE_PNG для команд консоли и png картинок.
- в файле config/console.h раскомментировать строку с CONSOLE_FRAMEBUFFER, тоже для картинок.
2.2. Скрипт стартовой настройки
После загрузки iPXE от DHCP появится приглашение командной строки iPXE. Дальше нужно будет получить ip и начать загрузку скрипта меню. Чтобы этого не делать вручную, можно встроить базовый скрипт в сам бинарник.
#!ipxe :start # Механизм получения ip. В случае ошибки - предлагает нажать "s" для запуска командной строки. # Иначе - начать снова. dhcp && goto next || prompt --key s --timeout 1500 Press "s" for the iPXE command line. && shell || goto start :next # Случае успеха - загрузить "меню" с tftp-сервера (о нём - позже). chain tftp://10.0.2.5/boot.ipxe
2.3. Сборка загрузчиков
Внимание! Строка имени файла является и его целевой конфигурацией, т.е. какое имя будет выбрано, для той платформы и будет сборка. В ключе EMBED= указывается имя ранее созданного скрипта.
Так же в случае ошибок при сборке, необходимо добавить ключ -fno-pie в Makefile.housekeeping на 451 строке. Предполагается, что это из-за ошибок в gcc.
make bin-i386-efi/ipxe.efi EMBED=start.ipxe make bin-i386-pcbios/undionly.kpxe EMBED=start.ipxe
Копирую полученные загрузчики в папку tftp PXE сервера
cp bin-i386-efi/ipxe.efi /srv/tftp/ cp bin-i386-pcbios/undionly.kpxe /srv/tftp/
3. Написание меню загрузки
Ранее, в загрузчики был встроен скрипт, который в случае успешного получения ip, загружал и запускал скрипт boot.ipxe.
Создаю его по пути /srv/tftp/boot.ipxe:
#!ipxe # Глобальные параметры set pass pAsWoRd # Установка пароля на вход в iPXE set httpServer http://10.0.2.5:80 # Задание адреса HTTP сервера set menu-timeout 10000 # таймаут выхода из меню ############################# ########## Графика ########## # Меняю цвет фона на серый colour --rgb 0x999999 6 # Цвет по-умолчанию (индекс 0) # Буквы - черный, фон букв - прозрачный cpair -f 0 -b 4 0 # Фоновое изображение и рамка # $ - переменная iPXE, определяет в каком режиме загружен клиент- pcbios или efi # Задаю параметры окна, картинку фона (загрузив её по http) console -l 32 -r 32 -t 32 -b 32 -k --x 1024 --y 768 -d 24 -p $/images/photo/$.png # После установки фона, удаляю картинку из памяти - # иначе она ломает wimboot загрузку WinPE imgfree $.png ######################### :login # Запрашивается пароль. Если будет нажат Ctrl+C или Esc будет выход и загрузка с диска # В идеале, можно использовать для авторизации в сервисах, но в данном случае играет роль заглушки login || goto cancel #Если пароль верен, перейти "дальше"-к меню загрузок, иначе - таймаут 5 секунд и повторный запрос пароля iseq $ $ && goto next || goto error_pass :error_pass echo Error password. Repeat after 5 seconds sleep 5 goto login :cancel echo You change "Exit". Boot from disk. sleep 1 exit # Переход к файлу с меню :next chain --replace --autofree menu.ipxe
По пути /var/www/html/images/photo/ кладу подготовленные pcbios.png и efi.png размера 1024х768. В зависимости от того, в каком режиме запустится ПК, у меня всегда будет подсказка на самом фоне меню:
Cоздаю меню загружаемых ОС и утилит.
#!ipxe # После предыдущего скрипта, настройки остаются прежними, # потому переопределяю их, чтобы было видно текст ######## Графика ######## # Цвет по-умолчанию (индекс 0) # Буквы - черный, фон-прозрачный cpair -f 0 -b 4 0 # Нормальный текст (индекс 1) # Буквы - черный, фон - прозрачный cpair -f 0 -b 4 1 # Разделители (индекс 3) # Буквы - красный, фон - прозрачный cpair -f 1 -b 4 3 ######################### ######## Главное меню ######## # На данный момент "всё в одном", но можно разделить как по разным меню, так и по разным файлам :start # Составляю меню # https://ipxe.org/cmd/menu menu iPXE boot menu item -k e exit (E)xit and boot from disk item item --gap -- -------- Windows Images -------- item -k a win10manager2021 Windows 10 M(a)nager 2021 item item --gap -- -------- Debian Images -------- item debian11 Debian 11 Bullseye item item --gap -- -------- Acronis Images -------- item acronis2021x64 Acronis 2021 64-bit item # Для режима Legacy, добавляю утилиты использующие Memdisk iseq $ pcbios && item --gap -- -------- BIOS Utilites -------- || iseq $ pcbios && item memtest Memtest86+ (v5.31) || iseq $ pcbios && item mhdd Mhdd (v4.6) || iseq $ pcbios && item hddtolls HDDtools || iseq $ pcbios && item victory Victory || iseq $ pcbios && item diskGenius DiskGenius (disk diagnostics) || iseq $ pcbios && item || item --gap -- -------- iPXE Utilites -------- item -k c config Start interactive (c)onfiguration tool item -k s shell Start (S)hell iPXE choose -d exit -t $ selected goto $ ######## # Пункты меню # В среднем состоят из трёх пунктов: # kernel - передаю ядро linux и аргументы для запуска # initrd - пакет данных для ядра # boot - команда передачи управления ядру linux ######## :debian11 # В данном случае, в kernel передаётся ссылка linux ядра на http-сервере и аргументы запуска kernel /images/bullseye/install.amd/pxe_$_preseed.cfg netcfg/dhcp_failed=note netcfg/dhcp_options="Retry network autoconfiguration" netcfg/get_domain= --- quiet initrd=initrd.gz # так же, ссылкой на http, передается пакет данных для ядра initrd $/images/bullseye/install.amd/initrd.gz # команда передачи управления ядру linux и в случае ошибки - переход к обоку обработки ошибок boot || goto error goto start # Разбор файлов для linux - будет ниже :win10manager2021 # wimboot - загрузчик .wim файлов установщика Windows, который сам патчит BCD, запускает установку и подтягивает скрипты запуска # https://ipxe.org/wimboot kernel wimboot ########################################### # Неизменный блок для любого образа Windows ########################################### initrd $/images/winpe/Boot/BCD BCD # Стандартный BCD WinPE initrd $/images/winpe/Boot/boot.sdi boot.sdi # Стандартный boot.sdi WinPE initrd $/images/winpe/boot.wim boot.wim # Стандартный boot.wim initrd configWPE/winpeshl.ini winpeshl.ini # Конфиг запускающий скрипт установки. ########################################### # Изменяемые данные в зависимости от образа initrd configWPE/win10manager2021/install.bat install.bat # Скрипт установки образа boot || goto error goto start ############################# # Утилиты загружаемые с iPXE ############################# # Acronis images :acronis2021x64 kernel $/images/utils/acr2021/dat10.dat initrd=dat11.dat initrd=dat12.dat vga=vesa MBRCRCS force_modules=usbhid quiet initrd $/images/utils/acr2021/dat11.dat initrd $/images/utils/acr2021/dat12.dat boot || goto error goto start # Утилиты загружаются через Memdisk и доступны только для BIOS режима :memtest kernel memdisk || goto error initrd $/images/utils/memtest86+-5.31b.iso || goto error imgargs memdisk iso raw || goto error boot || goto error goto start :victory kernel memdisk || goto error initrd $/images/utils/vcr35r.iso || goto error imgargs memdisk iso raw || goto error boot || goto error goto start :mhdd kernel memdisk || goto error initrd $/images/utils/mhdd32ver4.6.iso || goto error imgargs memdisk iso raw || goto error boot || goto error goto start :hddtools kernel memdisk || goto error initrd $/images/utils/hddtools.iso || goto error imgargs memdisk iso raw || goto error boot || goto error goto start :diskGenius kernel memdisk || goto error initrd $/images/utils/DiskGenius.iso || goto error imgargs memdisk iso raw || goto error boot || goto error goto start # Интерактивное меню редактирования конфигурация iPXE и сетевой карты :config config goto start # Командная строка iPXE :shell shell goto start :exit echo Boot from disk. sleep 2 exit # При ошибках выход на командную строку :error echo Failed - have error sleep 1 goto shell ##############################
4. Добавление образов Linux, Windows, утилит
После написания меню, необходимо подготовить загрузчики и пакеты загружаемых систем.
4.1. Добавление Linux
На стенде создаю папки:
mkdir -p /var/www/html/images/bullseye/install.amd # Для загрузчика, файлов ответа mkdir -p /var/www/html/images/bullseye/postinstall # Для скриптов и пакетов пост-установки. Скачиваться будут благодаря команде в файле ответов cd /var/www/html/images/install.amd/
Скачиваю ядро и пакет данных linux:
wget http://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux wget http://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
Скачиваю драйвера и встраиваю их в пакет ядра:
[ -f initrd.gz.orig ] || cp -p initrd.gz initrd.gz.orig [ -f firmware.cpio.gz ] || wget http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/stable/current/firmware.cpio.gz cat initrd.gz.orig firmware.cpio.gz > initrd.gz
Создаю 2 файла ответов для debian-installer:
- pxe_efi_preseed.cfg с разметкой диска под UEFI
- pxe_pcbios_preseed.cfg с разметкой диска под Legacy
Кладу их по пути /var/www/html/bullseye/install.amd/:
[drwxr-xr-x 4.0K] bullseye ├── [drwxr-xr-x 4.0K] install.amd │ ├── [-rw-r--r-- 138M] initrd.gz │ ├── [-rw-r--r-- 6.5M] linux │ ├── [-rw-r--r-- 15K] pxe_efi_preseed.cfg │ └── [-rw-r--r-- 15K] pxe_pcbios_preseed.cfg └──[drwxr-xr-x 4.0K] postinstall
Если сейчас попытаться загрузить linux, то все получится
4.2 Добавление Windows
Установки Windows через PXE как тачка на прокачку:
- Запускаем PXE ->
- Передаём управление iPXE ->
- Передаём управление wimboot ->
- Передаем управление WinPE ->
- Устанавливаем Windows
- Для установки Windows понадобится wimboot — загрузчик для wim образов. Умеет патчить BCD, работать с HTTP, переиспользовать память и т.д.
Скачиваю последнюю стабильную версию и кладу рядом с другими загрузчиками /srv/tftp/wimboot
- Следом понадобится чистый WinPE, а именно его
- winpe/media/Boot/BCD
- winpe/media/Boot/boot.sdi
- winpe/media/sources/boot.wim
Для использования его как «оболочки» для запуска установки Windows.
Их необходимо будет распаковать в /var/www/html/images/winpe/
- В каталог /srv/tftp/images/win10setup/ распаковываю образ установочного диска Windows и создаю там каталог wims — в неё будут складываться пакеты .wim образов и файлы ответов.
- В неё кладу заранее подготовленные:
- win10manager2021.wim — пакет корпоративного образа
- autounattend_bios_manager.xml и autounattend_efi_manager.xml — файлы ответов к нему для UEFI и Legacy режимов.
- Создаю каталоги:
- /srv/tftp/configWPE/ — для складывания в него настроек для разных образов (по аналогии с Bullseye)
- /srv/tftp/configWPE/win10manager2021/ — для непосредственно скрипта установки
- Создаю файл /srv/tftp/configWPE/winpeshl.ini — конфигурация, которая передаваемая ядру и которая указывает, какой скрипт выполнить при запуске:
[LaunchApps] "install.bat"
- Создаю сам файл:
@echo off echo wpeinit start :: Инициализация среды WinPE wpeinit :: Задержка для гарантированного запуска сетевой карты и получение IP echo We are waiting for 15 seconds for the network card to start ping -4 -n 15 -w 1000 "">nul ipconfig /renew echo echo wait 5 second pre-mount share folder with distr ping -4 -n 5 1000 "">nul ::Монтирование каталога с установщиком Windows net use Y: \10.0.2.5\images\win10setup /user:anonymous anonymous :: Получение информации, в каком режиме запущен пк - UEFI или Legacy echo check boot-mode wpeutil UpdateBootInfo for /f "tokens=2* delims spoiler">
Картинка По итогу получается:
Дерево каталогов
# Web-сервер images ├── bullseye # Файлы образа Linux │ ├── install.amd/ # Ядро и файлы ответов │ └── postinstall/ # Скрипты и пакеты для пост-установки ├── photo # Необязательный каталог для фонов меню │ ├── efi.png │ └── pcbios.png └── winpe # Файлы для запуска WinPE ├── BCD ├── boot.sdi └── boot.wim # Tftp/Samba-серва tftp ├── boot.ipxe # Установка переменных среды iPXE ├── configWPE # Хранилище конфигураций образов Windows │ ├── win10manager2021 │ │ └── install.bat # Скрипт запуска установки, где меняются samba пути │ └── winpeshl.ini # Общая для всех конфигурация, запускающая скрипт установки ├── images │ └── win10setup # Каталог стандартного установочного диска Windows │ ├── autorun.inf │ ├── boot/ │ ├── bootmgr/ │ ├── bootmgr.efi │ ├── efi/ │ ├── setup.exe │ ├── sources/ │ ├── support/ │ └── wims/ # Пакеты *.wim и файлы ответов │ ├── autounattend_bios_manager.xml │ ├── autounattend_efi_manager.xml │ └── win10manager2021.wim ├── ipxe.efi # Загрузчик для UEFI ├── menu.ipxe # Меню iPXE ├── undionly.kpxe # Загрузчик для Legacy └── wimboot # Загрузчик для Windows
4.3 Утилиты
Для них на Web-сервере создал подкаталог /var/www/html/images/utils/ и складываю либо *.iso образы для запуска через Memdisk, либо передавая в качестве ядра соответствующие файлы (например Acronis)
5. Итог
По итогу получилась система с минимальным количеством файлов для заливки и добавления:
- 3 загрузчика (Legacy,UEFI,Windows)
- 3 файл WinPE для Windows
- По 2-3 файла непосредственно образа
- По 2 файла ответов на образ
- По 1 скрипту пост-/установки на образ
- Простое меню. Если убрать графику, парольную заглушку и оставить TUI - станет еще меньше.
- Возможность работать с HTTP (. iSCSI SAN, AoE SAN и т.д.), что ускоряет загрузку.
Ожидаю вашего мнения в комментариях
P.S. Первая статья и тонна мучений с редактором.
- Использую Firefox, при нажатии на "/" в большинстве случаев ничего не появляется, либо стало появляться только после отключения всех AdBlock-еров.
- Предпросмотра для ссылок нет - перепроверить ссылки внутри статьи не могу
- В плане оформления помог плагин Unotes который конвертировал мой markdown в WYSIWYG, но потом вносил коррективы. Если что-то не будет работать - буду исправлять по мере обнаружения.
Маленькие «малинки» в крупном дата-центре: iPXE + Buildroot
Продолжим историю о появлении одноплатников в выделенных серверах. В прошлой статье мы рассмотрели отличие процесса загрузки Raspberry Pi 4 от «обычных» серверов и подробно описали, какие файлы необходимы для ее успешного завершения. Теперь нужно научиться менять этот процесс под наши потребности.
Остановились мы на загрузке по сети обратно в ОС, установленную на SD-карту. Напомню, что произошло это потому, что при загрузке ядра Linux kernel8.img мы дополнительно передали ему аргументы через файл cmdline.txt.
cat cmdline.txt console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait dwc_otg.lpm_enable=0
Ключевым аргументом здесь является root, который указывает на устройство, где располагается корневая файловая система. Логично предположить, что, меняя устройство, мы можем влиять на процесс загрузки по сети.
Прежде чем развивать гипотезу, давайте рассмотрим проверенные варианты загрузки по сети для «малинок».
Загрузка в PXE + NFS
В сети можно найти достаточно инструкций, описывающих подготовку NFS-сервера для загрузки «малинки» в полностью автономном режиме, без участия SD-карты. Повторяться не буду, но обозначу ряд важных моментов:
- Мы можем изменить аргумент root, чтобы он указывал на удаленный источник. Но все равно нужна корневая файловая система. Только «приготовленная» не локально, а на удаленном сервере.
- Теперь /boot-раздел у нас загружается через TFTP-сервер, а корневая файловая система — через NFS-сервер. Процесс загрузки мы практически никак не поменяли — только источники. Значит, и управлять мы может только с уровня загруженной по NFS операционной системы, не ранее.
- Непонятно, как решать проблему с доступом множества «малинок» к одному NFS-серверу. Создавать по одной корневой файловой системе на каждую ноду — тупиковый путь, запутаемся во множестве копий. Оставить одну в режиме только на чтение — возникнет проблема с изменяемыми данными (/tmp, /var, …).
Да, при должном усердии указанные проблемы можно решить через изменение скриптов инициализации при старте операционной системы. Тогда мы сможем на «малинке» загрузиться в какую-то среду, из которой будем запускать действия для дальнейшего развертывания сервера. Но результат все равно неоднозначен, а действий много.
piPXE
На этом месте стоит остановиться и сделать шаг назад. На «стандартных» серверах ведь уже есть среда, через которую мы управляем дальнейшей загрузкой сервера, — iPXE. Может, есть возможность перенести ее на «малинки»?
Недолгие поиски показали, что существует проект piPXE, предназначенный специально для запуска iPXE на одноплатниках. Стоит его протестировать и разобраться, как он работает.
Подготовка простая, достаточно раскатать образ на SD-карту.
curl https://github.com/ipxe/pipxe/releases/latest/download/sdcard.img | sudo dd of=/dev/sdX
После перезагрузки сервер успешно загрузится в iPXE, откуда уже можно загрузиться по сети. Рассмотрим, как именно это происходит и к каким файлам происходит обращение. Сверяться будем с официальной документацией.
- start4.elf и fixup4.dat — начинается процесс стандартно, с файлов, необходимых для инициализации видеоядра.
- сonfig.txt — с этого момента начинается самое интересное. В этот раз его содержимое отличается от того, что было в дистрибутиве Raspberry Pi OS.
arm_64bit=1 armstub=RPI_EFI.fd boot_delay=0 enable_uart=1
- RPI_EFI.fd — ключевым является параметр armstub, который указывает на файл с ARM-кодом, выполняемым до запуска ядра. В данном случае запускается код EFI-среды, основанном на EDK II (во многом пересекается с проектом RPi4, также направленном на запуск EFI-окружения на Raspberry Pi).
- /efi/boot/bootaa64.efi — исполняемый файл EFI-приложения. Здесь он располагается в директории, откуда автоматически запускаются исполняемые файлы при запуске EFI-прошивки. В данном случае iPXE, скомпилированный в EFI.
Последний пункт позволяет нам скомпилировать собственную сборку iPXE, со встроенным скриптом под наши задачи. Для примера соберем iPXE-загрузчик с нашим кастомным скриптом rpi.ipxe.
#!ipxe dhcp iseq $ efi && set uefi true || iseq $ pcbios && set uefi false || isset 224 || goto noparameter chain --autofree $&uefi=$ || goto chain_error :noparameter echo *************************************************************** echo * No 224 parameter was set: $ echo *************************************************************** goto exit0 :chain_error echo *************************************************************** echo * Error chaining to $ echo *************************************************************** goto exit0 :exit0 sleep 10 exit 0
Подробно останавливаться на работе этого скрипта не буду. Важно только отметить, что по нему iPXE получает сетевые настройки по DHCP. Проверяется наличие в DHCP-ответе опции 224 (первая из свободных опций для частного использования). Содержимое этой опции используется как адрес до образа, который запускается через chain. Для удобства добавлены диагностические сообщения, если опция 224 не задана или загрузка по ней невозможна.
Перед сборкой необходимо учесть, что мы собираем iPXE-загрузчик под архитектуру, отличную от x86_64. Поэтому сперва потребуется установка пакетов, нужных для кросс-компиляции (на примере Ubuntu 20.04). После чего уже клонировать репозиторий iPXE и собрать его утилитой make со встроенным скриптом.
apt install gcc-aarch64-linux-gnu
git clone https://github.com/ipxe/ipxe/ && cd ipxe CROSS_COMPILE=aarch64-linux-gnu- make bin-arm64-efi/ipxe.efi EMBED=./rpi.ipxe
Убедиться, что файл собран правильно под нужную архитектуру, можно через утилиту file:
file bootaa64.efi bootaa64.efi: MS-DOS executable PE32+ executable (DLL) (EFI application) Aarch64, for MS Windows
Затем достаточно скопировать готовый файл на SD-карту в /efi/boot/bootaa64.efi. После перезагрузки видим успешную загрузку в iPXE с нашим кастомным скриптом.
Казалось бы, успех?
Нет, такая схема оставляет нерешенными две проблемы:
- Зависимость от SD-карты, где располагается файл-заглушка RPI_EFI.fd. Да, его можно передать по TFTP, но в этом случае корректно он уже не запускается. Экспертизы в EDK II, чтобы это поправить, у нас нет.
- iPXE запускается через EFI. А это означает, что запускаемая установка дистрибутива должна поддерживать работу в этом режиме. Дистрибутивы под одноплатники обычно лишены этой способности за ненадобностью. Из-за этого могут не работать отдельные компоненты (например, сетевая карта), что потребует дополнительной адаптации дистрибутивов.
Опыт с запуском iPXE через EFI интересен и даже открывает некоторые перспективы (например, так обеспечивается запуск VMware ESXi на «малинках»), но, увы, для нас не подходит.
Образ Buildroot
Остановимся еще раз и оценим полученный опыт. Напрямую изменить последовательность загрузки «малинки» мы не можем. Список типов файлов, которые мы можем передавать по TFTP, определен. Возможность запуска arm stub файла до ядра нам не помогает, так как при передаче по сети корректно запускать его мы не умеем.
На что действительно можно повлиять — файл ядра Linux (и связанный с ним образ initramfs), который запускается на последнем этапе загрузки одноплатника. Нужно только собственное ядро с минимальным окружением в initramfs, которое бы позволило воспроизвести поведение iPXE. Фактически требуется собственный мини-дистрибутив, запускаемый из памяти, чтобы не зависеть от локальных носителей.
При загрузке Linux используется схема, когда сперва загружается initramfs-образ с минимально необходимой корневой файловой системой rootfs (прежде всего модули ядра). На втором этапе rootfs меняется и он перемонтируется на полноценную корневую файловую систему (локально на SD-карте или по NFS). Взять файлы ядра и initramfs-образа из существующего дистрибутива без дополнительной их адаптации мы не можем.
Можно собрать ядро Linux напрямую из исходных файлов. Инструкцию, как это сделать под Raspberry Pi 4, можно найти в официальной документации. Но к ядру еще требуется минимальное рабочее окружение из командной оболочки (shell) и базовых утилит, которые и будут образовывать корневую файловую систему, упакованную в initramfs и загружаемую в память.
Чтобы упростить эту задачу, мы обратимся к Buildroot — инструменту для сборки собственного мини-дистрибутива. О нем уже писали на Хабре. Мы же пойдем чуть дальше и воспользуемся его механизмом еxternal toolchain, чтобы отделить наши изменения от основного кода.
Предварительно создадим buildroot-окружение и установим необходимые пакеты.
# on ubuntu 20.04 export BUILDROOT="$HOME/buildroot_pi4" sudo apt -y install \ python-is-python3 \ expect-dev \ git \ bc rsync wget cpio unzip git clone --single-branch --branch 2021.08.x https://github.com/buildroot/buildroot.git $
Подготовив Buildroot-окружение, мы можем сразу же запустить сборку образа по Raspberry Pi 4, используя готовый профиль.
make raspberrypi4_64_defconfig
make
Далее можем отойти попить чаю (может, даже не одну кружку), так как скачивание зависимостей, необходимых утилит и кросс-компиляция займут значительное время. После завершения в директории ./output/images мы обнаружим необходимый нам образ sdcard.img, пригодный для записи на SD-карту.
Убедиться в этом мы можем, просматривая передаваемые ядру Linux аргументы. Видно, что корневая файловая система (root) ожидается на втором разделе SD.
cat output/images/rpi-firmware/cmdline.txt root=/dev/mmcblk0p2 rootwait console=tty1 console=ttyAMA0,115200
Нам нужно, чтобы вся корневая файловая система располагалась в памяти и не была привязана к локальным носителям. Для этого мы создадим внешнее окружение buildroot, где уже на основе существующего профиля raspberrypi4_64_defconfig создадим собственный.
EXT_BUILDROOT="$HOME/pi4_pxe_buildroot"
mkdir -p $
Для начала создадим описание нового профиля через файл external.desc.
name: raspberrypi4_64_pxe desc: raspberrypi4_64_pxe: builds special image to boot raspberry Pi4 over pxe
Далее создадим наш новый профиль. Самый простой способ для этого — скопировать существующий профиль и внести изменения.
mkdir -p $/configs
cp $/configs/raspberrypi4_64_defconfig "$_/raspberrypi4_64_pxe_defconfig"
Поскольку нам нужно, чтобы корневая файловая система (root) после запуска ядра Linux располагалась в оперативной памяти (RAM), указываем опцию в профиле. Для этого добавим в конец файла две дополнительные строки.
BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_CPIO_GZIP=y
В целом, этого достаточно, чтобы после пересборки полученные файлы Image и rootfs.cpio.gz можно было использовать для загрузки по PXE. Но пойдем чуть дальше и изменим систему инициализации с busybox на systemd. Для этого нам потребуется изменить системную библиотеку с uclibc на glibc, указав это в профиле и включив дополнительные пакеты.
# Systemd BR2_TOOLCHAIN_BUILDROOT_GLIBC=y BR2_TOOLCHAIN_BUILDROOT_LIBC="glibc" BR2_TOOLCHAIN_USES_GLIBC=y BR2_INIT_SYSTEMD=y BR2_PACKAGE_SYSTEMD_INITRD=y BR2_PACKAGE_SYSTEMD_FIRSTBOOT=y BR2_PACKAGE_SYSTEMD_HOSTNAMED=y BR2_PACKAGE_SYSTEMD_HWDB=y BR2_PACKAGE_SYSTEMD_LOCALED=y BR2_PACKAGE_SYSTEMD_LOGIND=y BR2_PACKAGE_SYSTEMD_MACHINED=y BR2_PACKAGE_SYSTEMD_MYHOSTNAME=y BR2_PACKAGE_SYSTEMD_NETWORKD=y BR2_PACKAGE_SYSTEMD_RESOLVED=y BR2_PACKAGE_SYSTEMD_TIMEDATED=y BR2_PACKAGE_SYSTEMD_TIMESYNCD=y BR2_PACKAGE_SYSTEMD_TMPFILES=y BR2_PACKAGE_SYSTEMD_VCONSOLE=y
Так как busybox больше не используется, желательно также изменить командную оболочку, например, на bash. Образ загружается по сети, поэтому неплохо иметь еще и мультиплексор tmux. Также добавим его в профиль.
BR2_SYSTEM_BIN_SH_BASH=y BR2_PACKAGE_BASH=y BR2_PACKAGE_BASH_COMPLETION=y BR2_PACKAGE_TMUX=y
Для добавления в образ наших собственных файлов и скриптов следует воспользоваться механизмом root filesystem overlay, задаваемым через опцию BR2_ROOTFS_OVERLAY.
BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_raspberrypi4_64_pxe_PATH)/files"
Через нее мы задаем путь до директории, содержимое которой будет скопировано поверх в собранный нами образ. BR2_EXTERNAL_raspberrypi4_64_pxe_PATH здесь — это автоматически генерируемая опция, связанная с нашим кастомных профилем.
Так, для примера создадим файл (относительно директории профиля) с содержимым, меняющим приглашение bash на красный цвет:
echo 'export PS1="\e[0;31m[\u@\h \W]# \e[m "' > ./files/root/.bashrc
В собранном образе он окажется в /root/.bashrc, т.е. в домашней папке пользователя root.
BR2_aarch64=y BR2_cortex_a72=y BR2_ARM_FPU_VFPV4=y BR2_TOOLCHAIN_BUILDROOT_CXX=y BR2_TOOLCHAIN_BUILDROOT_GLIBC=y BR2_TOOLCHAIN_BUILDROOT_LIBC="glibc" BR2_TOOLCHAIN_USES_GLIBC=y BR2_SYSTEM_DHCP="eth0" # Linux headers same as kernel, a 5.10 series BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_5_10=y BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_TARBALL=y BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="$(call github,raspberrypi,linux,4afd064509b23882268922824edc5b391a1ea55d)/linux-4afd06459b23882268922824edc5b391a1ea55d.tar.gz" BR2_LINUX_KERNEL_DEFCONFIG="bcm2711" # Build the DTB from the kernel sources BR2_LINUX_KERNEL_DTS_SUPPORT=y BR2_LINUX_KERNEL_INTREE_DTS_NAME="broadcom/bcm2711-rpi-4-b" BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y BR2_PACKAGE_RPI_FIRMWARE=y BR2_PACKAGE_RPI_FIRMWARE_VARIANT_PI4=y # Required tools to create the SD image BR2_PACKAGE_HOST_DOSFSTOOLS=y BR2_PACKAGE_HOST_GENIMAGE=y BR2_PACKAGE_HOST_MTOOLS=y # Filesystem / imageBR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_4=y BR2_TARGET_ROOTFS_EXT2_SIZE="120M" # BR2_TARGET_ROOTFS_TAR is not set BR2_ROOTFS_POST_BUILD_SCRIPT="board/raspberrypi4-64/post-build.sh" BR2_ROOTFS_POST_IMAGE_SCRIPT="board/raspberrypi4-64/post-image.sh" BR2_ROOTFS_POST_SCRIPT_ARGS="--add-miniuart-bt-overlay --aarch64" BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_raspberrypi4_64_pxe_PATH)/files" BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_CPIO_GZIP=y BR2_INIT_SYSTEMD=y BR2_PACKAGE_SYSTEMD_INITRD=y BR2_PACKAGE_SYSTEMD_FIRSTBOOT=y BR2_PACKAGE_SYSTEMD_HOSTNAMED=y BR2_PACKAGE_SYSTEMD_HWDB=y BR2_PACKAGE_SYSTEMD_LOCALED=y BR2_PACKAGE_SYSTEMD_LOGIND=y BR2_PACKAGE_SYSTEMD_MACHINED=y BR2_PACKAGE_SYSTEMD_MYHOSTNAME=y BR2_PACKAGE_SYSTEMD_NETWORKD=y BR2_PACKAGE_SYSTEMD_RESOLVED=y BR2_PACKAGE_SYSTEMD_TIMEDATED=y BR2_PACKAGE_SYSTEMD_TIMESYNCD=y BR2_PACKAGE_SYSTEMD_TMPFILES=y BR2_PACKAGE_SYSTEMD_VCONSOLE=y BR2_SYSTEM_BIN_SH_BASH=y BR2_PACKAGE_BASH=y BR2_PACKAGE_BASH_COMPLETION=y BR2_PACKAGE_TMUX=y
Несмотря на то, что новый профиль располагается в отдельной директории, его сборка все равно происходит из основного buildroot-окружения. Нужно только указать его расположение через переменную окружения BR2_EXTERNAL. Для ускорения пересборки buildroot использует кэш. Так как мы используем измененный профиль и заменили системную библиотеку uclibc на glibc, то пересобрать лучше с нуля.
make clean
make BR2_EXTERNAL=$ raspberrypi4_64_pxe_defconfig
make
После завершения сборки нам достаточно скопировать на удаленный TFTP-сервер файлы Image и rootfs.cpio.gz из директории output/images.
Чтобы «малинка» знала, какие файлы ей необходимо запрашивать по TFTP, необходимо указать их имена в файле config.txt:
kernel=Image initramfs rootfs.cpio.gz
Изменим также файл cmdline.txt, чтобы при загрузке дополнительно передать аргументы ядру, указывающие на расположение корневой файловой системы (root) в оперативной памяти.
cat cmdline.txt root=/dev/ram0 rootwait console=tty1 console=ttyAMA0,115200
Итоги и планы
Итак, мы добились нужного нам результата. С помощью buildroot мы создали собственный мини-дистрибутив. После загрузки по TFTP он полностью располагается в оперативной памяти и больше никак не зависит от локальных носителей (прежде всего от SD-карты, но в потенциале и от usb-дисков). И мы можем достаточно гибко модифицировать полученный образ, добавлять в него собственные скрипты с нужным функционалом.
Осталось только воспроизвести описанное поведение iPXE с получением опции 224. Но что это за опция, зачем она нужна и как она передается? Для ответа потребуется предварительно рассказать о Kea DHCP сервере и его системе hook-модулей. Этим и займемся в следующей статье.