OSreboot: компьютер сам восстанавливает стёртую систему через интернет

Рыжов Иван · группа ВПИ 26

github.com/Irenid/OSreboot · osreboot.epsti.site

24 октября 2026 г.

Аннотация

Компьютер с уничтоженной системой на диске обычно не поднимется без человека с загрузочной флешкой. В дата-центрах эту задачу решают контроллер удалённого управления (BMC) и сервер сетевой загрузки в той же сети, а производители ПК — закрытые механизмы восстановления в прошивке. В OSreboot ноутбук без BMC восстанавливает стёртые разделы сам, через интернет. Прошивка запускает iPXE с раздела, который затирание не трогает, iPXE по HTTPS загружает с VPS среду восстановления, а она скачивает образы разделов, проверяет SHA-256, записывает их и перезагружает машину. Сервер измеряет простой по сигналам heartbeat раз в секунду и показывает этапы восстановления в реальном времени. На ноутбуке весь простой, от команды затирания до первого heartbeat восстановленной системы, занял 164 и 166 с в двух прогонах. Больше половины этого времени ушло на две загрузки машины, треть — на скачивание, которое упиралось в 100-мегабитное подключение ноутбука. Обзор существующих решений показал, что ни одно из них не сочетает работу без BMC, загрузку через интернет, открытый стек для Linux и внешний замер простоя.

1. Введение

Система на диске может погибнуть по многим причинам: ошибка администратора (rm -rf /, dd не на тот диск), сбой обновления, шифровальщик, повреждение файловой системы. Итог один: компьютер не загружается, и чинить его приходит человек с загрузочной флешкой. Если компьютер стоит далеко — например, домашний сервер, пока владелец в отъезде, — простой длится часы или дни.

В дата-центре эта задача решена давно. У сервера есть BMC (iDRAC, iLO, IPMI) — отдельный компьютер на плате, через который администратор включает питание, видит экран и подключает образ, а системы вроде MAAS и Ironic переустанавливают ОС по сети [1, 2]. Им нужны BMC и свой сервер сетевой загрузки в той же сети. У обычного ПК и ноутбука нет ни того, ни другого.

Производители ПК встроили восстановление по сети в прошивку и ОС [3, 4, 5, 6, 7]. Но эти механизмы закрыты и восстанавливают только «свою» систему — Windows или macOS, — как правило, со своих серверов.

Цель работы — показать, что обычный компьютер без BMC и без помощника в локальной сети может сам восстановить уничтоженную систему через интернет открытыми средствами, и измерить, сколько при этом длится простой. Требования к решению:

Вклад работы:

  1. цепочка восстановления, проверенная на ноутбуке без BMC: прошивка UEFI → iPXE по HTTPS → среда восстановления в памяти → проверка и запись образов → загрузка восстановленной системы;
  2. сервер, который измеряет простой по сигналам heartbeat и показывает этапы восстановления в реальном времени;
  3. обзор готовых решений и исследований (раздел 2);
  4. первые замеры на ноутбуке и в виртуальной машине (раздел 4).

2. Существующие решения и исследования

Обзор охватывает готовые решения — промышленные и открытые — и исследования о восстановлении систем. Поиск шёл в октябре 2026 года по документации производителей и проектов и по научным публикациям; ниже разобраны решения, ближайшие к задаче. Ключевые утверждения сверены по самим страницам источников. Но «не нашли» не значит «не существует»: поиск не исчерпывающий.

2.1. Восстановление в прошивке и ОС персональных компьютеров

Производители ПК и ОС давно встроили восстановление по сети в прошивку или в среду восстановления на диске:

Ближе всего к OSreboot по устройству Dell BIOSConnect: при повреждённой системе он скачивает служебную ОС по HTTPS в память. Но все эти решения восстанавливают только систему своего производителя и со своих серверов (HP позволяет свой сервер, но только для Windows), а Quick Machine Recovery не переживает стирания диска вместе с WinRE.

2.2. Системы для дата-центров

Переустановку ОС по сети в дата-центрах автоматизировали давно:

Обе системы рассчитаны на BMC и на свою сеть, в которой работает DHCP-сервер загрузки. Научные работы ускоряют подготовку таких машин. M2 [8] (2018) загружает узлы через iPXE с сетевых дисков iSCSI вместо копирования образа. Подготовка узла занимает около 5 минут, больше 3 из них — инициализация прошивки. Это примерно втрое быстрее Foreman и Ironic, которые проходят её дважды.

2.3. Образы дисков и обновления с откатом

Открытые инструменты вроде Clonezilla [9] снимают и разливают образы дисков по сети, в том числе на много машин сразу (multicast). Но запускает их человек: они отвечают на вопрос «как восстановить», но не «когда».

Системы с A/B-разделами защищают от неудачного обновления. Новая версия ставится во вторую копию системы, и если она не загрузится, загрузчик возвращается к старой; в systemd-boot для этого есть счётчик неудачных загрузок [10]. Но все копии и загрузчик лежат на одном диске, и его стирание уносит их все.

Отсюда для OSreboot два урока. Счётчик неудачных загрузок — готовый способ запускать восстановление автоматически (раздел 5). А спасательная система должна жить там, куда поломка не дотянется: в OSreboot это iPXE на основном разделе EFI и среда восстановления на сервере.

2.4. Сравнение и место OSreboot

Решение Запуск без человека Без BMC и vPro Образ через интернет Переживает стирание диска Свой образ и сервер Открытый код Замер простоя
Dell OS Recovery + BIOSConnect да да да частично1 нет нет нет
HP Sure Recover частично2 да да да частично3 нет нет
Lenovo Cloud bare metal recovery нет да да да нет нет нет
Apple macOS Recovery (Intel) частично4 да да да нет нет нет
Windows Quick Machine Recovery частично5 да да нет нет нет нет
MAAS, Ironic частично6 нет нет да да да нет
Clonezilla нет да нет да да да нет
A/B-обновления, systemd-boot да да нет7 нет да да нет
OSreboot частично8 да да да9 да да да
  1. Только вариант BIOSConnect, целиком встроенный в прошивку.
  2. Администратор может заранее отправить подписанную команду: по расписанию или после N перезагрузок.
  3. Свой сервер разрешён, но образ — только Windows (WIM).
  4. Среда восстановления загружается сама, переустановку запускает пользователь.
  5. Запускается сам, но лишь применяет исправления Microsoft и работает из WinRE на том же диске.
  6. По команде оператора.
  7. Новые версии могут приходить по сети, но откат идёт на локальную копию.
  8. Восстановление идёт само после команды затирания. При настоящей поломке его пока никто не запускает (раздел 5).
  9. Если уцелел основной раздел EFI с iPXE.

Ни одно из найденных решений не закрывает все столбцы сразу:

Опубликованных замеров простоя при самовосстановлении одной машины найти не удалось. Ближайшая цифра получена на кластере и для другой задачи: около 5 минут на подготовку узла у M2 [8].

Эту нишу и занимает OSreboot. Он открыт и работает на обычном компьютере без BMC, восстанавливает свою систему с образа из интернета и измеряет простой внешним сервером. Его слабое место — запуск: пока восстановление начинает команда затирания, а не обнаруженная поломка.

3. Как устроен OSreboot

3.1. Компоненты

Компонент Где работает Что делает
Сервер (src/, web/) VPS, за nginx Принимает heartbeat и отчёты об этапах, считает простой, показывает страницу состояния, раздаёт файлы восстановления
Клиент (client/) Тестовая система и среда восстановления Скрипт на POSIX shell и curl: heartbeat раз в секунду и отчёты об этапах
Тестовая система (image/) Разделы osreboot-esp и osreboot-root Arch Linux, собранный mkosi: systemd-boot, единый образ ядра (UKI), heartbeat, обратный SSH-туннель и команда osreboot-wipe
iPXE (ipxe/) Основной системный раздел EFI, который затирание не трогает Сетевой загрузчик с HTTPS и встроенным сценарием; его запускает systemd-boot по записи по умолчанию, которую ставит osreboot-wipe
Среда восстановления (recovery/) Оперативная память UKI на 132 МБ: скачивает, проверяет и записывает образы, ставит BootNext и перезагружает машину

3.2. Цепочка восстановления

osreboot-wipe --apply (в тестовой системе)
  → запись systemd-boot по умолчанию = osreboot-ipxe.conf
  → стоп heartbeat → этап wipe → этап netboot
  → затирание первых 64 MiB обоих тестовых разделов → сброс машины
прошивка UEFI → первая запись BootOrder: systemd-boot основного раздела EFI
systemd-boot → запись по умолчанию → iPXE
iPXE → DHCP → DNS (повторы, затем 1.1.1.1 и 8.8.8.8); при ошибке заново через 10 с
     → HTTPS: boot.ipxe → osreboot-recovery.efi (UKI, работает из памяти)
среда восстановления → этапы recovery → partition → download → verify
                     → write → bootloader → reboot
прошивка UEFI → по BootNext загружает восстановленную тестовую систему
тестовая система → osreboot-netboot-off убирает запись iPXE по умолчанию
                 → первый heartbeat с новым boot ID → сервер закрывает простой
Схема: основной раздел EFI с iPXE, среда восстановления в памяти, тестовые разделы и сервер на VPS; по HTTPS идут boot.ipxe, UKI и образы, обратно — этапы и heartbeat
Рисунок 1. Что где лежит и что идёт по сети. Цифры — порядок шагов.

Демонстрация начинается с команды osreboot-wipe --apply в тестовой системе. Сначала она проверяет, что первая запись BootOrder запускает systemd-boot с основного раздела EFI и что там есть запись iPXE, и делает эту запись записью по умолчанию (LoaderEntryDefault). Затем останавливает heartbeat, сообщает серверу этапы wipe и netboot, затирает начало обоих тестовых разделов и сбрасывает машину без штатного выключения. Так выглядит «система уничтожена»: при следующей загрузке на разделах нет ни загрузчика, ни файловой системы.

Прошивка загружает первую запись BootOrder — systemd-boot основной системы, а он запускает iPXE. Запись по умолчанию хранится в NVRAM и остаётся до конца восстановления, поэтому после отключения питания на любом шаге машина снова попадёт в iPXE. iPXE лежит на основном системном разделе EFI, который затирание не трогает, и собран с поддержкой HTTPS, корневыми сертификатами ISRG (Let's Encrypt) как единственными доверенными и встроенным сценарием. Сценарий получает адрес по DHCP, разрешает имя сервера, по HTTPS загружает boot.ipxe, а тот — среду восстановления. Если какой-то шаг не удался, например нет кабеля или сервер не отвечает, iPXE показывает ошибку и через 10 секунд начинает с DHCP заново, сколько бы это ни длилось.

Среда восстановления — Arch Linux, собранный mkosi в один UKI, который целиком работает в памяти. Её сервис находит тестовые разделы по меткам, скачивает сжатые zstd образы и SHA256SUMS в память, проверяет контрольные суммы, записывает образы, читает их обратно и сверяет ещё раз, расширяет корневую файловую систему, ставит BootNext на восстановленную систему, сохраняет туда свой журнал и перезагружает машину. Каждый шаг он сообщает серверу как этап, но от отчётов восстановление не зависит.

Восстановленная система снова шлёт heartbeat. Новый boot ID в нём закрывает простой, и сервер дописывает строку в data/outages.csv: начало, конец и время каждого этапа.

3.3. Как измеряется простой

Сервер — один процесс Node.js без базы данных. Машина шлёт heartbeat раз в секунду. Если их нет дольше HEARTBEAT_TIMEOUT_MS (3 с), сервер считает машину недоступной. Началом простоя служит последний heartbeat перед ним, концом — первый после него, и оба момента берутся по часам сервера. Поэтому таймаут влияет только на то, когда простой станет виден, а не на его длительность, и сбитые часы самой машины замер не портят.

Перезагрузка быстрее таймаута тоже считается простоем: её выдаёт смена boot ID в heartbeat. Отчёт об этапе открывает простой, если его ещё нет, потому что скрипт затирания отчитывается раньше, чем система упадёт. По времени этапов простой раскладывается на части: затирание, сетевая загрузка, скачивание, проверка, запись, перезагрузка.

В терминах теории распределённых систем это детектор отказов на heartbeat с фиксированным таймаутом [11]. Он может принять задержку сети за отказ. Адаптивные детекторы вроде φ accrual [12] подстраивают порог под наблюдаемые задержки, но для замера длительности простоя одной машины фиксированного порога хватает: ложная тревога лишь откроет простой, который закроется следующим heartbeat.

Страница / показывает по каждому устройству heartbeat, таймер простоя, текущий этап с прогрессом, прошлые простои и скачивания файлов. Обновления приходят через server-sent events. Страница /probe проверяет доступ к серверу из текущей сети: время ответа, скорость скачивания и POST-запросы.

3.4. Предохранители

Среда восстановления пишет на диск, поэтому она осторожна:

Вся цепочка не должна оставлять машину там, куда без человека не добраться. Обрыв сети её только приостанавливает, а после отключения питания она начинается заново с iPXE, пока восстановленная система не запустится и не уберёт запись iPXE по умолчанию.

Что происходит при обрыве сети и отключении питания:

Шаг Выдернули кабель Отключили питание
osreboot-wipe до затирания отчёты о wipe и netboot теряются, затирание идёт дальше запись iPXE уже стоит: после включения машина восстанавливает систему
прошивка, systemd-boot — после включения снова iPXE
iPXE: DHCP, DNS, загрузка UKI ошибка, через 10 с всё заново, пока кабель не вернут после включения снова iPXE
среда восстановления: скачивание ждёт сеть, затем докачивает с места обрыва после включения снова iPXE, скачивание с начала
проверка, запись, BootNext сеть не нужна, теряются только отчёты разделы записаны не до конца: после включения снова iPXE и запись заново
загрузка восстановленной системы heartbeat ждёт сеть, простой закрывается, когда она вернётся если BootNext уже израсходован, снова iPXE и восстановление заново
восстановленная система работает heartbeat ждёт сеть запись iPXE уже убрана: машина загружает основную систему

osreboot-wipe ничего не затирает, пока не убедится, что после сброса запустится iPXE.

В образах нет секретов, поэтому их можно раздавать публично. Токен устройства зашифрован TPM машины (systemd-creds) и расшифровывается только на ней.

3.5. Что пришлось решать по ходу

4. Результаты

Замеры сделаны 23.10.2026 на ноутбуке — потребительском, без BMC, с прошивкой Insyde, проводной сетью и NVMe-диском. Сервер — VPS с одним виртуальным процессором: Node.js под systemd за nginx (deploy/osreboot.service). Сжатые образы двух разделов вместе занимают 404,8 MiB, UKI среды восстановления — 132,3 MiB. Все времена взяты по часам сервера: строки data/outages.csv лежат в results/outages-2026-10-23.csv, состояние сервера со списком скачиваний — в results/state-2026-10-23.json, а таблицу из них строит dist/outage-stats.js (см. приложение).

Таблица 1. Прогоны на ноутбуке, секунды.
№ Время, МСК Запуск Сетевая загрузка Среда восстановления из неё скачивание проверка запись Перезагрузка до heartbeat Весь простой
1 06:25 из основной системы1 — 56,4 42,5 4,8 8,1 38,3 —
2 06:30 osreboot-wipe 68,1 55,1 41,0 4,8 8,3 40,5 164,0
3 18:49 из основной системы1 — 54,0 39,7 4,8 8,3 41,1 —
4 18:53 osreboot-wipe, демонстрация 67,9 55,9 41,7 4,8 8,3 41,6 165,7
  1. Запущены из основной системы ноутбука, без затирания, поэтому весь простой тестовой системы для них не определён.

«Сетевая загрузка» — от этапа wipe до первого отчёта среды восстановления; «Среда восстановления» — от этого отчёта (recovery) до этапа reboot; «Перезагрузка до heartbeat» — от reboot до первого heartbeat восстановленной системы, включая паузу 3 с перед перезагрузкой; «Весь простой» — от последнего heartbeat перед затиранием до первого после восстановления.

Прогоны почти не отличаются: среда восстановления работала от 54,0 до 56,4 с, весь простой — 164,0 и 165,7 с, а проверка SHA-256 каждый раз заняла 4,8 с.

Таблица 2. Из чего сложились 165,7 с демонстрации (прогон 4). Границы внутри сетевой загрузки и скачивания взяты из списка скачиваний сервера: когда iPXE запросил boot.ipxe и когда закончилась передача каждого файла.
Отрезок, с Что происходит Длительность, с Доля
0 → 24,4 сброс, прошивка, systemd-boot, iPXE: DHCP, DNS, TLS, запрос boot.ipxe 24,4 15 %
24,4 → 39,7 скачивание UKI среды восстановления, 132,3 MiB, 8,7 MiB/с 15,3 9 %
39,7 → 68,2 старт среды восстановления в памяти: ядро, systemd, сеть, первый отчёт 28,5 17 %
68,2 → 69,0 поиск разделов, SHA256SUMS 0,8 <1 %
69,0 → 110,7 скачивание образов, 404,8 MiB 41,7 25 %
110,7 → 115,5 проверка SHA-256 4,8 3 %
115,5 → 123,8 запись, обратное чтение и сверка, e2fsck и resize2fs 8,3 5 %
123,8 → 124,1 BootNext, сохранение журнала 0,3 <1 %
124,1 → 165,7 пауза 3 с, перезагрузка, прошивка, загрузка тестовой системы, первый heartbeat 41,6 25 %
Временная шкала прогона 4: девять шагов из таблицы 2, загрузки машины 94,5 с, скачивание 57,0 с, проверка и запись 13,1 с
Рисунок 2. Таблица 2 на шкале времени. Строит dist/downtime-figure.js (см. приложение).

По видам работы это:

Внутри среды восстановления время уходит в основном на сеть: скачивание заняло 40–42 с из 54–56. Сама передача образов в прогоне 4 длилась 35,8 с (11–12 MiB/с), а остальное — паузы цикла, который раз в 2 с проверяет, закончилось ли скачивание, и отправляет отчёт.

Эту скорость ограничивал не сервер, а подключение ноутбука. Его сетевая карта согласовала с роутером 100 Мбит/с, а через такой канал по TCP проходит около 11,2 MiB/с полезных данных. Основной образ, 320 MiB, шёл со скоростью 11,0 MiB/с — это 98 % предела. Другому клиенту тот же сервер отдаёт 24 MiB/с. По журналу ноутбука его подключение поднималось на 1 Гбит/с только 10.10, а во все остальные дни с 3 октября — на 100 Мбит/с, так что дело, скорее всего, в кабеле или порте роутера. Насколько ускорится скачивание на гигабите, зависит от канала до сервера и покажет следующий прогон.

Но во всём простое больше половины занимают две загрузки машины: в среду восстановления и обратно, в восстановленную систему. Ускорять в первую очередь нужно их. Меньший UKI быстрее скачивается и быстрее стартует, а переход в восстановленную систему через kexec обошёл бы второй проход прошивки. Для сравнения: в работе M2 [8] одна инициализация прошивки серверов занимала больше трёх минут, а на ноутбуке весь путь от сброса до запроса iPXE к серверу занял 24 с.

Таблица 3. Виртуальная машина.
Что измерено Результат
QEMU, OVMF, NVMe: от включения до работающей восстановленной системы 65 с
От загрузки восстановленной системы до входа по SSH через туннель 14 с
Разметка как у ноутбука: от osreboot-wipe --apply до входа по SSH в восстановленную систему — systemd-boot, iPXE, UKI, работа среды восстановления (28 с), перезагрузка 54 с

В виртуальной машине весь путь втрое короче, но её канал до VPS другой, чем у ноутбука, а прошивка OVMF стартует быстрее. Четыре прогона на ноутбуке — ещё не статистика; серию прогонов запускает scripts/run-series.sh (см. приложение).

5. Ограничения

6. Выводы

Обычный ноутбук без BMC и без помощника в локальной сети может сам восстановить уничтоженную систему через интернет. Для этого хватает прошивки UEFI, iPXE с HTTPS, среды восстановления в памяти и VPS с образами. На ноутбуке полный простой, от затирания до первого heartbeat восстановленной системы, занял 164 и 166 с в двух прогонах, а работа среды восстановления — от 54 до 56 с в четырёх. Внутри среды восстановления время уходит в основном на скачивание, а его скорость ограничивало 100-мегабитное подключение ноутбука. Но во всём простое больше половины занимают две загрузки машины: прошивка, загрузчики и ядра.

Обзор показал, что части задачи давно решены по отдельности. Сетевую переустановку умеют системы для дата-центров, восстановление из облака — прошивки и ОС Dell, HP, Lenovo, Apple и Microsoft, откат неудачного обновления — системы с A/B-разделами. Открытого решения для обычного компьютера без BMC, которое восстанавливает стёртую систему через интернет и измеряет простой извне, найти не удалось.

Следующие шаги:

  1. серия прогонов (scripts/run-series.sh) с разбивкой простоя по этапам;
  2. более быстрое восстановление: гигабитное подключение вместо 100 Мбит/с, меньший UKI среды восстановления, скачивание без пауз опроса, переход в восстановленную систему через kexec;
  3. автоматический запуск восстановления по счётчику неудачных загрузок;
  4. проверка UEFI HTTP Boot на прошивках других машин и с адресом от DHCP (опции 60 и 67), чтобы обойтись без iPXE на диске;
  5. подпись цепочки для Secure Boot.

Источники

Электронные ресурсы проверены 9–23 октября 2026 года.

  1. MAAS: Manage machines // Canonical. — URL: https://canonical.com/maas/docs/stable/how-to-guides/manage-machines/
  2. OpenStack Ironic: User Guide. — URL: https://docs.openstack.org/ironic/victoria/user/index.html
  3. Dell SupportAssist OS Recovery: User's Guide. — Dell Technologies, 2023. — URL: https://dl.dell.com/topicspdf/support-assist-os-recovery_users-guide_en-us.pdf
  4. HP Sure Recover // HP Developers. — URL: https://developers.hp.com/node/12370
  5. Cloud bare metal recovery (for selected models) // ThinkPad P15v Gen 3, T15p Gen 3 User Guide. — Lenovo. — URL: https://download.lenovo.com/pccbbs/pubs/tp_p15v_t15p_gen3/ug/html_en/en/Cloud_bare_metal_recovery_(for_selected_models).html
  6. Use macOS Recovery on an Intel-based Mac // Mac User Guide. — Apple. — URL: https://support.apple.com/guide/mac-help/use-macos-recovery-on-an-intel-based-mac-mchl338cf9a8/11.0/mac/11.0
  7. Quick machine recovery // Microsoft Learn. — URL: https://learn.microsoft.com/en-us/windows/configuration/quick-machine-recovery/
  8. Mohan A., Turk A., Gudimetla R. S. et al. M2: Malleable Metal as a Service // IEEE International Conference on Cloud Engineering (IC2E). 2018. P. 61–71. DOI: 10.1109/IC2E.2018.00028
  9. Clonezilla. — URL: https://clonezilla.org/
  10. Automatic Boot Assessment // systemd.io. — URL: https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/
  11. Chandra T. D., Toueg S. Unreliable Failure Detectors for Reliable Distributed Systems // Journal of the ACM. 1996. Vol. 43, no. 2. P. 225–267. DOI: 10.1145/226643.226647
  12. Hayashibara N., Défago X., Yared R., Katayama T. The φ Accrual Failure Detector // 23rd IEEE International Symposium on Reliable Distributed Systems (SRDS 2004). 2004. P. 66–78. DOI: 10.1109/RELDIS.2004.1353004

Приложение. Как запустить

Запуск

cp config/devices.example.json config/devices.json   # добавьте хеши токенов, см. «Устройства»
docker compose up -d --build

Сервер слушает только 127.0.0.1:8090. Из интернета к нему обращаются через обратный прокси, см. «Развёртывание». Файлы для раздачи кладутся в files/.

Для разработки нужен Node.js 24 или новее:

npm ci
npm run dev      # собирает страницы и запускает сервер с перезапуском при изменениях
npm run verify   # проверка типов, тесты и сборка

Настройки берутся из переменных окружения:

Переменная По умолчанию
PORT 8090
HOST 127.0.0.1 (0.0.0.0 в контейнере)
HEARTBEAT_TIMEOUT_MS 3000
DATA_DIR data
FILES_DIR files
DEVICES_FILE config/devices.json

Устройства

У каждого устройства свой токен, а сервер хранит только его хеш SHA-256:

openssl rand -hex 32 | tr -d '\n' > laptop.token
sha256sum laptop.token   # значение для tokenSha256 в config/devices.json

На устройстве токен хранится зашифрованным с помощью TPM этого устройства, поэтому в самом образе системы секретов нет:

systemd-creds encrypt -T --name=osreboot-token laptop.token /etc/credstore.encrypted/osreboot-token

API

Метод Путь Запрос и ответ
POST /api/heartbeat {"boot": "<boot_id>"}, ответ 204
POST /api/progress {"stage": "download", "percent": 42, "message": "server.raw.zst"}, ответ 204
GET /api/state текущее состояние в JSON
GET /api/events то же состояние в виде server-sent events
GET, HEAD /files/<path> файлы из files/, поддерживаются диапазоны байтов
GET /health ok

POST-запросам нужен заголовок Authorization: Bearer <токен устройства>. Имя этапа должно соответствовать [a-z0-9][a-z0-9_-]{0,31}. Для wipe, netboot, recovery, partition, download, verify, write, bootloader и reboot на странице состояния есть понятные названия; остальные имена показываются как есть.

Клиент

client/osreboot-client — скрипт на POSIX shell, которому нужен только curl:

osreboot-client heartbeat
osreboot-client stage download 42 "server.raw.zst"

Адрес сервера он берёт из OSREBOOT_URL, а токен — из OSREBOOT_TOKEN_FILE или из systemd credential osreboot-token. client/osreboot-heartbeat.service запускает heartbeat как сервис.

Любой heartbeat завершает простой, поэтому скрипт в работающей системе останавливает сервис heartbeat, прежде чем сообщить этап.

Тестовая система

image/ собирает с помощью mkosi 25 или новее систему, которую демонстрация затирает и восстанавливает: Arch Linux с systemd-boot и единым образом ядра (unified kernel image), DHCP на проводных сетевых картах, SSH только по ключам, сервис heartbeat и обратный SSH-туннель к серверу (см. SSH в тестовую систему). На консоли tty1 вход под root выполняется автоматически. Система ставится на два раздела целевого диска с метками osreboot-esp (системный раздел EFI не меньше 512 MiB) и osreboot-root.

Локальные настройки в git не хранятся. image/mkosi.local.conf добавляет дополнительное дерево файлов:

[Content]
ExtraTrees=mkosi.local.extra

В image/mkosi.local.extra/ лежат etc/systemd/system/osreboot-heartbeat.service.d/server.conf с переменной OSREBOOT_URL, root/.ssh/authorized_keys и, при необходимости, etc/credstore.encrypted/osreboot-token — токен устройства, зашифрованный TPM машины; расшифровать его может только этот TPM.

mkosi -C image build   # сначала собирает собственное дерево инструментов

В образе нет секретов в открытом виде, поэтому для восстановления его можно раздавать публично. image/write-test-system.sh запускается на целевой машине из её основной системы. Он записывает образы обоих разделов и считывает их обратно для сверки, расширяет корневую файловую систему, шифрует токен устройства с помощью TPM машины и кладёт его в тестовую систему, а также добавляет загрузочную запись в прошивку UEFI, не меняя порядок загрузки:

sudo bash write-test-system.sh DIR           # только проверки
sudo bash write-test-system.sh DIR --apply

В DIR лежат osreboot-test.esp.raw.zst, osreboot-test.root-x86-64.raw.zst, SHA256SUMS несжатых образов и, при необходимости, osreboot.token. mkosi создаёт только несжатые образы .raw:

cd image/mkosi.output
zstd -T0 -12 osreboot-test.esp.raw osreboot-test.root-x86-64.raw
sha256sum osreboot-test.esp.raw osreboot-test.root-x86-64.raw > SHA256SUMS

Ещё в тестовой системе есть osreboot-wipe — шаг демонстрации «система уничтожена». Запущенный под root в тестовой системе, он проверяет, что первая запись BootOrder запускает systemd-boot с основного раздела EFI и что там есть запись osreboot-ipxe.conf вместе с её программой, делает эту запись записью по умолчанию (bootctl set-default), останавливает heartbeat, сообщает этапы wipe и netboot, затирает первые 64 MiB обоих разделов и сбрасывает машину. Запись по умолчанию переживает отключение питания. Её убирает osreboot-netboot-off.service, когда запустится восстановленная система:

osreboot-wipe           # только проверки и план
osreboot-wipe --apply

Загрузка по сети

iPXE лежит на основном системном разделе EFI, который затирание не трогает, а запускает его systemd-boot основной системы по записи по умолчанию. ipxe/build.sh собирает ipxe/out/ipxe.efi — iPXE на зафиксированном коммите с поддержкой HTTPS, корневыми сертификатами ISRG от Let's Encrypt в качестве единственных доверенных и встроенным скриптом ipxe/osreboot.ipxe. Этот скрипт получает адрес по DHCP, разрешает имя сервера (повторяет попытки, затем пробует DNS-серверы 1.1.1.1 и 8.8.8.8) и загружает по цепочке (chain) https://HOST/files/boot.ipxe. При любой ошибке он ждёт 10 секунд (Ctrl-B открывает консоль iPXE) и начинает заново с DHCP, а не возвращается в меню загрузчика.

ipxe/install-ipxe.sh запускается из основной системы целевой машины. Он кладёт ipxe.efi в /EFI/osreboot/ipxe.efi на её разделе EFI и добавляет запись systemd-boot osreboot-ipxe.conf, проверив сначала, что первая запись BootOrder запускает systemd-boot с этого раздела. Запись по умолчанию в меню не меняется: если бы с новой записью она поменялась, скрипт удаляет запись и останавливается.

bash ipxe/build.sh osreboot.example.org
sudo bash ipxe/install-ipxe.sh ipxe/out/ipxe.efi           # только проверки
sudo bash ipxe/install-ipxe.sh ipxe/out/ipxe.efi --apply
sudo systemctl reboot --boot-loader-entry=osreboot-ipxe.conf   # восстановление один раз

Это запись загрузчика, а не прошивки: прошивка Insyde демо-ноутбука, найдя переписанный тестовый раздел EFI, создаёт для него свою запись и дважды записала её под номером записи, которой не было в BootOrder. ipxe/boot.ipxe кладётся в files/ на сервере и запускает среду восстановления из того же каталога. Если это не удалось, он возвращает управление встроенному скрипту, и тот перезагружает машину.

Встроенная в прошивку загрузка по HTTP (HTTP Boot) не может заменить iPXE на демо-ноутбуке: его прошивка Insyde удаляет загрузочные записи с адресом сервера при следующем включении, не обратившись к нему, а адрес для своей записи HTTP Boot берёт только у DHCP-сервера сети (раздел 3.5).

Среда восстановления

recovery/ собирает osreboot-recovery.efi — целую систему в едином образе ядра, которая работает из памяти. Её сервис osreboot-recovery находит разделы с метками osreboot-esp и osreboot-root, скачивает оба образа и SHA256SUMS из files/ на сервере, проверяет их, записывает, считывает обратно, расширяет корневую файловую систему, ставит BootNext на тестовую систему, сохраняет свой журнал в /var/log/osreboot/ тестовой системы и перезагружает машину. Он сообщает этапы recovery, partition, download, verify, write, bootloader и reboot или failed с причиной; восстановление от этих отчётов не зависит. Без сети он ждёт: запросы к серверу повторяются каждые 10 с, а прерванное скачивание продолжается с места обрыва (curl -C -). После ошибки он оставляет вход на консоли и по SSH, убирает запись iPXE по умолчанию и через 20 минут перезагружает машину в её основную систему; systemctl stop osreboot-fallback-reboot.timer оставляет её в среде восстановления. Он отказывается работать с разделом, если у того другой тип, файловая система не vfat и не ext4 или размер больше 4 GiB (ESP) или 128 GiB (root), и ничего не пишет на диск за пределами этих двух разделов.

Локальные настройки среда восстановления берёт у тестовой системы. recovery/mkosi.local.conf:

[Content]
ExtraTrees=../image/mkosi.local.extra
ExtraTrees=mkosi.local.extra

recovery/mkosi.local.extra/etc/systemd/system/osreboot-recovery.service.d/server.conf задаёт OSREBOOT_URL. В виртуальной машине, где нет TPM целевой машины, токен можно передать как systemd credential osreboot-token.

mkosi -C recovery build

После этого в files/ на сервере лежат boot.ipxe, osreboot-recovery.efi, оба образа .raw.zst и SHA256SUMS.

SSH в тестовую систему

У восстановленной системы нет публичного адреса, поэтому osreboot-tunnel.service открывает обратный SSH-туннель к серверу. Он входит под пользователем osreboot-tunnel и пробрасывает Unix-сокет /var/lib/osreboot-tunnel/sock/ssh.sock на сервере к своему sshd; nginx на сервере публикует этот сокет на порту 2221:

ssh -p 2221 root@osreboot.example.org

Туннель — это сокет, а не TCP-порт, потому что система перед затиранием была сброшена, не закрыв своё соединение. sshd на сервере держит такую сессию часами, и TCP-порт остался бы занят, а сокет заменяется при следующем подключении (StreamLocalBindUnlink). Восстановленная система доступна через несколько секунд после загрузки.

osreboot-ssh-host-key.service до запуска sshd кладёт на место постоянный ключ хоста, поэтому после каждого восстановления клиенты видят один и тот же ключ хоста. Оба закрытых ключа — это credentials, зашифрованные TPM машины, как и токен устройства. Без них туннель повторяет попытки каждые 10 с, а sshd генерирует новый ключ хоста. make-ssh-credentials.sh создаёт оба ключа на целевой машине из её основной системы:

sudo bash make-ssh-credentials.sh DIR

Он записывает в DIR файлы osreboot-tunnel-key.cred и osreboot-ssh-host-key.cred вместе с их .pub; закрытые ключи в открытом виде никогда не покидают память. Локальные настройки в image/mkosi.local.extra/:

Среда восстановления использует те же файлы и image/mkosi.extra, поэтому во время работы открывает тот же туннель.

На сервере под root, имея под рукой osreboot-tunnel-key.pub:

useradd --system --user-group --home-dir /var/lib/osreboot-tunnel \
	--no-create-home --shell /usr/sbin/nologin osreboot-tunnel
install -d -m 0755 /var/lib/osreboot-tunnel /var/lib/osreboot-tunnel/.ssh
install -d -m 0755 -o osreboot-tunnel -g osreboot-tunnel /var/lib/osreboot-tunnel/sock
echo "restrict,port-forwarding,command=\"/usr/sbin/nologin\" $(cat osreboot-tunnel-key.pub)" \
	>/var/lib/osreboot-tunnel/.ssh/authorized_keys
install -m 0644 deploy/sshd-osreboot-tunnel.conf /etc/ssh/sshd_config.d/90-osreboot-tunnel.conf
sshd -t && systemctl reload ssh

deploy/nginx-osreboot-ssh.conf подключается в контекст stream {} nginx. Пользователь osreboot-tunnel может пробросить только этот сокет и ничего больше: ни shell, ни TCP-портов. Клиенту достаточно один раз добавить ключ хоста тестовой системы в доверенные:

echo "[osreboot.example.org]:2221 $(cut -d' ' -f1,2 osreboot-ssh-host-key.pub)" >>~/.ssh/known_hosts

Серия прогонов и статистика

scripts/run-series.sh повторяет демонстрацию для серии замеров; ему нужны ssh, curl и jq. Перед каждым прогоном он проверяет, что устройство в сети и что osreboot-wipe проходит свои проверки, и печатает скорость проводного подключения: при 100 Мбит/с скачивание не может идти быстрее 11 MiB/с. Затем запускает osreboot-wipe --apply через SSH-туннель и ждёт, пока восстановленная система снова пришлёт heartbeat. Если прогон не закончился за TIMEOUT_S (900 с), серия останавливается: неудачное восстановление скрипт повторно не затирает.

OSREBOOT_URL=https://osreboot.example.org OSREBOOT_SSH=root@osreboot.example.org \
	scripts/run-series.sh 10

dist/outage-stats.js сводит data/outages.csv в таблицу: строка на каждый прогон со средой восстановления, время между этапами, а внизу минимум, медиана и максимум каждого столбца. Прогон, который не дошёл до перезагрузки (ошибка или обрыв скачивания), остаётся в таблице с пометкой в последнем столбце, но в минимум, медиану и максимум не входит: его простой длится, пока кто-нибудь не загрузит тестовую систему.

docker compose exec osreboot node dist/outage-stats.js data/outages.csv
node dist/outage-stats.js results/outages-2026-10-23.csv   # замеры раздела 4

dist/downtime-figure.js рисует шкалу времени одного прогона, как на рисунке 2. Этапы он берёт из outages.csv, а границы внутри сетевой загрузки — из списка скачиваний. Этот список есть только в ответе /api/state: в state.json на диске сервер хранит одни устройства. Сервер помнит только 10 последних скачиваний, это около двух прогонов, поэтому run-series.sh сохраняет ответ /api/state после каждого прогона в SAVE_DIR (по умолчанию results/series-<время UTC> в каталоге проекта). Без третьего аргумента берётся последний полный прогон osreboot-wipe, а третий аргумент выбирает прогон по началу его времени в UTC:

node dist/downtime-figure.js results/outages-2026-10-23.csv results/state-2026-10-23.json >docs/downtime.svg
node dist/downtime-figure.js results/outages-2026-10-23.csv results/state-2026-10-23.json --dark >docs/downtime-dark.svg

Развёртывание

Сервер демонстрации работает прямо на VPS, без Docker: Node.js из /opt/node запускает deploy/osreboot.service под временным пользователем (DynamicUser) с ограничениями systemd. Код лежит в /opt/osreboot (package.json и dist/), состояние и outages.csv — в /var/lib/osreboot, файлы для раздачи — в /srv/osreboot/files, хеши токенов — в /etc/osreboot/devices.json. nginx на VPS проксирует к 127.0.0.1:8090, то есть REMOTE_PORT в конфигах ниже — это 8090. Обновление сервера:

npm ci && npm run build
tar czf - -C dist . | ssh root@VPS 'cd /opt/osreboot && rm -rf dist.new && mkdir dist.new &&
	tar xzf - -C dist.new --no-same-owner && rm -rf dist && mv dist.new dist &&
	systemctl restart osreboot'
ssh root@VPS /opt/node/bin/node /opt/osreboot/dist/outage-stats.js /var/lib/osreboot/outages.csv

Если сервер работает на машине без публичного адреса, его можно опубликовать через VPS туннелем FRP: он передаёт порт 8090 на VPS, а nginx отдаёт его по пути /osreboot/.