Удалённая переустановка Raspberry Pi OS по ssh

9f1a48ea53966fb495894fc57f661b79.png

На habr’е уже была публикация про удаленную переустановку Linux по ssh. Воистину, так гланды ещё никто не удалял… Я подивился находчивости и целеустремлённости автора, и искренне пожелал себе, чтобы материал никогда не пригодился на практике.

Но внезапно мне понадобилось переустановить Raspberry Pi OS на microSD-карте, установленной в Raspberry Pi 4. При этом доставать microSD-карту очень не хотелось, дело в том, что плата Raspberry Pi установлена внутрь корпуса, который не позволяет достать microSD-карту без разборки.

Памятуя, что это не единственная моя плата Raspberry Pi и это не последний раз, когда придётся полностью переустанавливать Raspberry Pi OS, я решил, что лучше полчаса потерять, потом за пять минут обновить.

Сразу оговорюсь, что приведённая ниже инструкция не универсальна. Я буду рассматривать очень простой случай:

  • есть небольшая локальная сеть IPv4 с DHCP-сервером и выходом в Интернет;

  • имеется плата Raspberry Pi (далее rpi), подключённая к локальной сети по Ethernet, на которой уже установлена и функционирует Raspberry Pi OS не слишком старой версии;

  • в локальной сети есть компьютер (далее host), с которого мы можем подключаться к rpi по ssh (на host установлены wget, openssh-client и xz);

  • rpi может получать IP-адрес по DHCP; мы умеем найти rpi в локальной сети (хотя бы по MAC-адресу).

Вот стартовав в таких тепличных условиях, попробуем полностью обновить содержимое microSD-карты в rpi, перезагрузиться и снова подключиться к rpi по ssh.

Не нагуглив готового решения для Raspberry Pi OS, я обратился к посту Удаленная переустановка Linux по ssh без доступа к консоли. Ключевым моментом изложенной там методики является возможность на ходу логически «отключить» накопитель, с которого работает «старая» ОС, а затем уже переподключить его, но так, что «старая» ОС будет его считать другим накопителем и не будет противиться его полной перезаписи.
Увы, то, что дозволено делать со SCSI-диском не дозволено делать с microSD-картой, по крайней мере мне подобные возможности найти не удалось.

Я рассмотрел другие возможности:

  • порвать с /dev/mmcblk0 при помощи pivot_root;

  • перезагрузиться, так, чтобы совсем не задействовать /dev/mmcblk0 для /: использовать nfsroot или nbd для rootfs.

Эти решения сопряжены с дополнительными хлопотами, которых хотелось бы избежать.

И почти готовое решение нашлось: Alpine Linux.

Alpine Linux это дистрибутив, который требует очень скромных ресурсов накопителя и ОЗУ так, что оказывается возможным, загрузившись с microSD-карты, развернуть корневую файловую систему только в ОЗУ.

Будем действовать так:

  • загрузим на rpi Alpine Linux в специальной конфигурации, в которой производится автоматическая настройка сетевого интерфейса Ethernet по DHCP и обеспечивается запуск ssh-сервера с беспарольным доступом;

  • из Alpine Linux запишем на microSD-карту базовый образ Raspberry Pi OS и проведём его минимальную настройку;

  • перезагрузимся в Raspberry Pi OS.

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

Подключаемся по ssh к root@rpi, где работает Raspberry Pi OS, и развёртываем там Alpine Linux.

Для Raspberry Pi OS microSD-карта размечается так, что карте создаётся два раздела:

  • /dev/mmcblk0p1 --- небольшой раздел с файловой системой FAT, где находятся загрузчик и всё, что надо для запуска ядра ОС;

  • /dev/mmcblk0p2 --- раздел с файловой системой EXT4; этот раздел содержит корневую файловую систему ОС.

NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
mmcblk0     179:0    0 59.7G  0 disk
├─mmcblk0p1 179:1    0  512M  0 part /boot/firmware
└─mmcblk0p2 179:2    0 59.2G  0 part /

ВНИМАНИЕ! Не забудьте сохранить ценные данные с microSD-карты, если таковые там есть, через несколько минут они отправятся в страну вечной охоты!

Будем развёртывать Alpine в разделе загрузчика, то есть в /boot/firmware:

root@raspberrypi:~# cd /boot/firmware/
root@raspberrypi:/boot/firmware# wget -O - https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/aarch64/alpine-rpi-3.18.5-aarch64.tar.gz | tar fxz -
root@raspberrypi:/boot/firmware# wget https://raw.githubusercontent.com/macmpi/alpine-linux-headless-bootstrap/f35a86687534306895ea147953730f56af4cff37/headless.apkovl.tar.gz

Скачанный файл headless.apkovl.tar.gz как раз и обеспечивает настройку сетевого интерфейса rpi при старте и запуск ssh-сервера. Подробности тут.

Итак, microSD-карта настроена так, что при следующей загрузке на rpi запустится Alpine Linux.

Перезагружаемся:

root@raspberrypi:/boot/firmware# reboot

Развёртывание и настройка Raspberry Pi OS

Немного подождём и попробуем подключиться к rpi с работающим Alpine Linux по ssh.

Это можно сделать так:

host$ ssh -x -o userknownhostsfile=/dev/null -o stricthostkeychecking=no root@rpi

Дополнительные опции нужны, чтобы клиент ssh не смущался новому, неведомому серверу, жить которому только несколько минут.

Чтобы не набирать такую длинную команду несколько раз, закинем её в переменную:

host$ RPISSH="ssh -o userknownhostsfile=/dev/null -o stricthostkeychecking=no root@rpi"

Освобождаем mmcblk0:

host$ $RPISSH umount /.modloop/
host$ $RPISSH umount /media/mmcblk0p1/

Теперь можно накатывать образ Raspberry Pi OS. Чтобы не возиться с установкой на Alpine Linux xz-utils, будем запускать xz на host:

host$ wget -O - https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-06/2023-12-05-raspios-bookworm-arm64-lite.img.xz | xz -cd | $RPISSH dd bs=8M of=/dev/mmcblk0

С некоторых пор нельзя просто так взять и подключиться к Raspberry Pi OS по ssh, см. https://www.raspberrypi.com/news/raspberry-pi-bullseye-update-april-2022/, необходима небольшая начальная настройка.

Начнём с того, что разрешим запуск ssh-сервера при старте:

host$ $RPISSH mount /dev/mmcblk0p1 /mnt
host$ $RPISSH "echo > /mnt/ssh"

Опционально можно включить использование последовательного порта в качестве консоли:

host$ $RPISSH "echo enable_uart=1 >> /mnt/config.txt"

Работы с разделом загрузчика завершены, этот раздел можно отключить:

host$ $RPISSH umount /mnt

В современной Raspberry Pi OS по умолчанию ни пользователю root, ни пользователю pi нельзя подключиться по ssh. Поэтому подложим пользователю root готовый открытый ключ, чтобы в дальнейшем можно было подключиться к root@rpi без пароля:

host$ $RPISSH mount /dev/mmcblk0p2 /mnt
host$ $RPISSH "cat > /mnt/root/.ssh/authorized_keys" < ~/.ssh/id_rsa.pub
host$ $RPISSH umount /mnt

Подробнее про ключи openssh можно почитать тут.

Перезагрузимся в Raspberry Pi OS:

host$ $RPISSH reboot

Подождём не более пары минут и voilà:

host$ $RPISSH uname -a
...
Linux raspberrypi 6.1.0-rpi7-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2023-11-24) aarch64 GNU/Linux

Эпилог

Инструкция несколько раз проверена на Raspberry Pi 4 Model B. Для других плат понадобятся внести незначительные корректировки, но, полагаю, вдумчивый читатель догадается где надо заменить aarch64/arm64 на armhf.

© Habrahabr.ru