← Все гайды

Загрузочная флешка · Часть 2

Свой live-образ Debian: загрузился — и сразу по ssh

Live-образ Debian, который грузится с флешки и сразу пускает по ssh с вашими ключами: сборка через live-build, имя live.local, грабли и безопасность.

· 8 мин

Системное администрирование

Закрытый ноутбук с флешкой соединён светящейся линией с ключом с парящим окном терминала; Максим спокойно смотрит, кот-голограмма идёт по линии

Соберите образ через live-build: ядро и прошивки из backports, ваш authorized_keys для root, sshd в автозапуске и mDNS-имя live.local. Загрузили машину с флешки — и через минуту ssh root@live.local с ноутбука, без монитора и клавиатуры у самой машины.

Флешка в порт, питание — и вы уже в консоли этой машины со своего ноутбука.

Зачем это нужно

Обычный live-образ Debian или Ubuntu грузит рабочий стол и ждёт человека за клавиатурой. Чтобы зайти на него по сети, нужно сесть за машину, поставить и запустить ssh-сервер, задать пароль, узнать адрес. Свой образ делает всё это сам:

  • машина без монитора — сервер, мини-ПК, роутер на x86: загрузили с флешки, и она в сети;
  • сломанная система — Windows или Linux не загружается, а диск нужно смонтировать, починить или скопировать по сети;
  • проверка железа — диски (SMART), память, сетевые карты, Wi-Fi, прежде чем ставить систему;
  • удобство — работать в своём терминале, с буфером обмена и историей, а не за чужой клавиатурой.

Что будет в образе:

  • Debian stable с ядром и прошивками из backports: свежие сетевые карты и Wi-Fi определяются там, где установщик stable их ещё не знает;
  • ssh-сервер в автозапуске; root пускает только по вашим ключам;
  • имя в сети live.local (mDNS через Avahi): IP-адрес знать не нужно;
  • адреса машины на экране входа, как только DHCP их выдаст;
  • утилиты для дисков и сети: mdadm, lvm2, cryptsetup, smartctl, nvme-cli, parted, testdisk, ddrescue, ntfs-3g, rsync, tmux, mc, iftop, tcpdump, mtr, iperf3;
  • меню загрузки из трёх пунктов: лёгкий рабочий стол XFCE (по умолчанию, через 5 секунд), консоль (меньше памяти, быстрее) и консоль в безопасном режиме nomodeset — если на экране нет картинки. ssh работает во всех трёх.

Готовый образ кладётся на мультизагрузочную флешку из первой части серии рядом с установщиками.

Что нужно для сборки

  • компьютер или виртуальная машина с Debian (подойдёт и Ubuntu);
  • пакет live-build, права root и доступ к deb.debian.org;
  • около 10 ГБ свободного места в рабочей папке, например в /var/tmp;
  • время: первая сборка — около 25 минут, повторная с кешем пакетов — около 10;
  • ваши открытые ssh-ключи в одном файле authorized_keys.

Дальше — ключевые шаги сборочного скрипта. Каждый блок — кусок одного bash-скрипта, который запускается от root из пустой рабочей папки.

Сборка через live-build

1. Основа образа

DIST=trixie                      # Debian 13
LIVE_USER=sysadmin               # пример: замените своими
LIVE_PASSWORD=sysadmin           # пример: обязательно замените

lb config \
  --distribution "$DIST" \
  --archive-areas "main contrib non-free non-free-firmware" \
  --debian-installer none \
  --binary-images iso-hybrid \
  --bootloaders "grub-efi,syslinux" \
  --memtest none \
  --bootappend-live "boot=live components hostname=live username=$LIVE_USER locales=en_US.UTF-8 keyboard-layouts=us timezone=Etc/UTC"

grub-efi грузит образ в UEFI, syslinux — в BIOS, iso-hybrid даёт файл, который можно и записать на флешку, и положить в Ventoy. Язык, раскладки и часовой пояс в --bootappend-live поставьте свои.

2. Ядро и прошивки из backports

A="deb http://deb.debian.org/debian $DIST-backports main contrib non-free non-free-firmware"
echo "$A" > config/archives/backports.list.chroot
echo "$A" > config/archives/backports.list.binary
for t in chroot binary; do cat > config/archives/backports.pref.$t <<PREF
Package: linux-image-* linux-headers-* linux-kbuild-* linux-base* linux-binary-* linux-modules-* firmware-* amd64-microcode intel-microcode
Pin: release n=$DIST-backports
Pin-Priority: 990
PREF
done

Без этого шага образ получит ядро stable, и сетевые карты, которые появились после его выхода, просто не увидит. Приоритет 990 касается только ядра и прошивок: остальная система остаётся stable.

3. Пакеты

Свой список кладите в отдельный файл, например config/package-lists/custom.list.chroot:

openssh-server avahi-daemon libnss-mdns network-manager sudo
firmware-linux firmware-linux-nonfree firmware-misc-nonfree firmware-realtek firmware-iwlwifi
mdadm lvm2 cryptsetup smartmontools nvme-cli parted gdisk ntfs-3g testdisk gddrescue
rsync tmux htop mc iftop tcpdump mtr-tiny dnsutils iperf3 ethtool lshw
xorg lightdm xfce4 xfce4-terminal network-manager-gnome firefox-esr gparted

4. Ключи и ssh

install -d -m 700 config/includes.chroot/root/.ssh config/includes.chroot/etc/skel/.ssh
install -m 600 authorized_keys config/includes.chroot/root/.ssh/authorized_keys
install -m 600 authorized_keys config/includes.chroot/etc/skel/.ssh/authorized_keys

install -d config/includes.chroot/etc/ssh/sshd_config.d
cat > config/includes.chroot/etc/ssh/sshd_config.d/10-live.conf <<'CONF'
PasswordAuthentication yes          # пользователь — по ключу или паролю
PermitRootLogin prohibit-password   # root — только по ключу
CONF

Всё из config/includes.chroot/ попадает в образ как есть. Через /etc/skel ключи достаются и пользователю, которого live-config создаёт при загрузке. Если вход по паролю по сети вам не нужен, пишите PasswordAuthentication no.

5. Хуки: службы, ключи хоста и пароль

cat > config/hooks/normal/9000-live-ssh.hook.chroot <<'HOOK'
#!/bin/sh
set -e
systemctl enable ssh.service avahi-daemon.service NetworkManager.service
rm -f /etc/ssh/ssh_host_*    # ключи хоста создаются заново при каждой загрузке
HOOK

# live-config зашивает пароль «live» в свой скрипт — подменяем хешем своего
openssl passwd -6 "$LIVE_PASSWORD" > config/includes.chroot/etc/live-password.hash
cat > config/hooks/normal/9010-live-password.hook.chroot <<'HOOK'
#!/bin/sh
set -e
f=/usr/lib/live/config/0030-user-setup
h=$(cat /etc/live-password.hash); rm -f /etc/live-password.hash
sed -i "s|^\(\s*_PASSWORD=\).*|\1'${h}'|" "$f"
grep -qF "$h" "$f"
HOOK
chmod +x config/hooks/normal/*.hook.chroot

install -d config/includes.chroot/etc/sudoers.d
echo "$LIVE_USER ALL=(ALL) NOPASSWD:ALL" > config/includes.chroot/etc/sudoers.d/live-user
chmod 440 config/includes.chroot/etc/sudoers.d/live-user

Хуки выполняются внутри будущей системы во время сборки. Последняя строка хука с паролем — проверка: если live-config поменяет формат файла, сборка упадёт, а не выпустит образ со старым паролем.

6. Адрес на экране входа

install -d config/includes.chroot/etc/NetworkManager/dispatcher.d
cat > config/includes.chroot/etc/NetworkManager/dispatcher.d/90-live-issue <<'DISP'
#!/bin/sh
mkdir -p /run/issue.d
{ echo "  ssh root@live.local"; ip -4 -br addr show scope global | awk '{print "  "$1"  "$3}'; echo; } \
  > /run/issue.d/50-live-ip.issue
agetty --reload 2>/dev/null || true
DISP
chmod 755 config/includes.chroot/etc/NetworkManager/dispatcher.d/90-live-issue

Когда NetworkManager получает адрес, скрипт переписывает приветствие консоли, и адреса видны прямо на экране входа. Тот же ip -4 -br addr стоит положить в /etc/profile.d/, чтобы адреса печатались и при входе в консоль.

7. Меню загрузки

Шаблоны загрузчиков копируются в config/bootloaders/ и правятся:

cp -r /usr/share/live/build/bootloaders/{grub-pc,syslinux_common,isolinux} config/bootloaders/
sed -i 's/^timeout .*/timeout 50/' config/bootloaders/isolinux/isolinux.cfg    # 5 секунд
sed -i 's/^set default=0$/set default=0\nset timeout=5/' config/bootloaders/grub-pc/config.cfg

В config/bootloaders/grub-pc/grub.cfg место @LINUX_LIVE@ занимают три пункта меню:

menuentry "Live — рабочий стол XFCE (ssh включён)" {
	linux	@KERNEL_LIVE@ @APPEND_LIVE@
	initrd	@INITRD_LIVE@
}
menuentry "Live — консоль (ssh включён)" {
	linux	@KERNEL_LIVE@ @APPEND_LIVE@ systemd.unit=multi-user.target
	initrd	@INITRD_LIVE@
}
menuentry "Live — консоль, безопасный режим (nomodeset)" {
	linux	@KERNEL_LIVE@ @APPEND_LIVE@ nomodeset systemd.unit=multi-user.target
	initrd	@INITRD_LIVE@
}

Для BIOS те же три пункта пишутся в config/bootloaders/syslinux_common/live.cfg.in в синтаксисе syslinux (label, menu label, linux, initrd, append).

8. Сборка и проверка

lb build 2>&1 | tee build.log
for p in live-boot live-config openssh-server; do
  chroot chroot dpkg -s "$p" >/dev/null || { echo "в образе нет $p"; exit 1; }
done
sha256sum *.hybrid.iso

Перед тем как класть образ на флешку, проверьте его в QEMU — в режиме BIOS и UEFI (прошивка OVMF из пакета ovmf), с пробросом ssh на порт 2222:

qemu-system-x86_64 -m 4G -enable-kvm -cdrom live-image-amd64.hybrid.iso \
  -netdev user,id=n0,hostfwd=tcp::2222-:22 -device virtio-net,netdev=n0
# UEFI: добавьте  -bios /usr/share/ovmf/OVMF.fd
ssh -p 2222 root@127.0.0.1      # должен пустить по ключу

Грабли live-build

  • live.list.chroot — занятое имя. В нём live-build держит служебные пакеты (live-boot, live-config). Если назвать так свой список, он перезапишет служебный, и образ соберётся, но не загрузится как live. Свой список — под любым другим именем.
  • Меню ждёт вечно. В шаблоне isolinux timeout 0, то есть без автозагрузки, а в шаблоне grub таймаута нет вовсе. Для машины без монитора это значит «не загрузится никогда» — таймаут ставьте явно.
  • В grub.cfg подставляются не все метки. Работают @KERNEL_LIVE@, @INITRD_LIVE@ и @APPEND_LIVE@, а вот failsafe-вариантов, как в syslinux, нет. Безопасный режим в grub собирайте сами: @APPEND_LIVE@ плюс nomodeset.
  • live-config выключает вход по паролю в ssh. Он сам пишет PasswordAuthentication no. Свой файл в sshd_config.d читается раньше основного конфига и поэтому побеждает.
  • Пароль «live» зашит в скрипт live-config (0030-user-setup). Свой пароль подставляет хук из шага 5.
  • \4 в /etc/issue показывает 127.0.0.1. Escape-последовательность agetty берёт адрес в момент вывода экрана, то есть до DHCP. Поэтому адрес пишет скрипт NetworkManager из шага 6.

Как пользоваться

  1. Загрузите машину с флешки и выберите образ в меню Ventoy.

  2. Через минуту-другую с ноутбука в той же сети:

    ssh root@live.local
    

    Если в сети две такие машины, Avahi назовёт вторую live-2.local. Имя не находится — адрес виден на экране машины, а с неё самой ip -br a покажет его, nmtui настроит Wi-Fi или статический адрес.

  3. Ключи хоста у live новые при каждой загрузке, поэтому ssh будет ругаться на смену ключа. Чтобы не чистить known_hosts каждый раз, заведите для live отдельный блок в ~/.ssh/config:

    Host live.local live-*.local
      User root
      StrictHostKeyChecking no
      UserKnownHostsFile /dev/null
    

Для дисков в образе есть всё основное. Как читать вывод smartctl у NVMe-диска — объём записи и Percentage Used, — разобрано в гайде про износ SSD: он про Mac, но поля SMART у NVMe везде одинаковые.

Безопасность

  • Пароль по умолчанию — только пример. Пара sysadmin / sysadmin выше показывает, где задаются имя и пароль; в своей сборке задайте другие. А если образ уже собран с простым паролем — смените его сразу после загрузки командой passwd, особенно в чужой сети: у пользователя sudo без пароля, так что его пароль равен правам root.
  • root — только по ключу. Пароля у root нет, а PermitRootLogin prohibit-password не пустит root по паролю, даже если его задать.
  • Ключи хоста — новые при каждой загрузке. Так в образе не лежат закрытые ключи, одинаковые для всех копий. Обратная сторона: ssh не отличит вашу live-машину от подменённой. Пользуйтесь им в своей сети и не включайте проброс агента (ssh -A) на live.
  • В образе только открытые ключи. Они не секрет, но решают, кого пустят: перед сборкой проверьте, что в authorized_keys нет лишних.

Случай из практики

Ноутбук с Windows 11 перестал загружаться после неудачной чистки диска. Папку от прошлого обновления удаляли, сначала забрав права на неё рекурсивно (takeown и icacls с /r и /t). Внутри неё были junction-ссылки на настоящие системные папки, и команды прошли по ним: права на ProgramData, профиль по умолчанию и часть профиля пользователя заменились на «только этот пользователь». Системная учётная запись SYSTEM потеряла доступ, и службы, без которых не стартует оболочка, перестали запускаться. Файлы при этом не пострадали.

Починка шла из live-образа по ssh, вся работа — из терминала на другом компьютере:

  1. загрузка с флешки, ssh root@live.local с другого компьютера;
  2. том NTFS был помечен как «грязный», поэтому сначала ntfsfix, затем монтирование через ntfs-3g на запись;
  3. перед любыми изменениями — резервная копия текущих прав каждой папки, чтобы можно было откатить;
  4. права на системные каталоги восстановлены наследованием от родителя, сверху вниз, а особые права — по документации Microsoft;
  5. перезагрузка в Windows, проверка DISM и sfc — чисто.

Из Windows такую поломку чинить тяжело: она уже не запускается, а среда восстановления даёт только грубый сброс прав, который теряет особые настройки. Live-образ с ssh дал полноценный Linux с доступом к диску и возможность работать из своего терминала, со своими скриптами.

И вывод на будущее: папки прошлых обновлений Windows ($WINDOWS.~BT, Windows.old) удаляйте штатной «Очисткой диска», а не сменой прав и удалением вручную.

Следующая часть серии — Windows без учётной записи Microsoft: какую редакцию положить на флешку, чтобы при установке получилась обычная локальная учётка.