Чтение/запись /etc с файловой системой только для чтения

Вопрос или проблема

У меня есть настройка для встроенного проекта, в котором я хотел бы сохранить корневую файловую систему только для чтения, а конфигурацию системы на разделе с возможностью записи. Пользователи не могут устанавливать программы, но они могут изменять конфигурацию. Большинство из этого достаточно просто, за исключением каталога /etc. Он содержит конфигурацию, которая нужна при запуске (например, fstab), а также конфигурацию, которую можно изменить (например, passwd).

Я не уверен, как лучше всего двигаться вперед. Могу ли я иметь стандартный каталог /etc и монтировать его после загрузки? Как насчет того, чтобы сделать символьные ссылки на все изменяемые файлы конфигурации в раздел с возможностью записи? Другое, лучшее решение? Для простоты рассмотрим, как это будет сделано на Raspberry Pi.

Кто-то может (справедливо) спросить, как обрабатываются обновления. Обновления — это сжатые и подписанные образы корневого и загрузочного разделов, которые встроенное программное обеспечение знает, как установить. Отсюда желание иметь корневую файловую систему только для чтения, поскольку любые изменения будут потеряны при обновлении.

У меня есть два возможных варианта для вас. Первый довольно сложный. Второй должен быть простым, если вы можете использовать btrfs.

initramfs

Типичный способ загрузки Linux сегодня — это использование initramfs. Это, по сути, минимальная rootfs, содержащая только то, что необходимо для монтирования реальной rootfs. Бинарники и конфигурации упакованы в архив, хранящийся в /boot и распаковывающийся при старте системы во временную файловую систему как первый rootfs.

Это означает, что вы можете иметь процесс загрузки, при котором временная файловая система загружается из архива, который вы можете изменить, где ядро вызовет исполняемый файл под названием init, который вы можете изменить, предназначенный для монтирования реального корня и выполнения других системных операций при запуске.

Для более детального объяснения этой части вы можете, например, прочитать процесс загрузки Arch. (особенно часть 5 (initramfs) и 6 (раннее пользовательское пространство))

Ваш шанс: initramfs может быть любым и делать что угодно. Wiki Gentoo дает вам представление о том, что возможно.
Плохая часть: это не просто. Обычно используются некоторые инструменты для создания initramfs, такие как initramfs-tools на Debian или mkinitcpio для Arch или dracut или другие. У них есть параметры конфигурации, но наличие отдельного /etc слишком специфично.

Для initramfs-tools (я не знаю другие инструменты) может быть целесообразно добавить вашу собственную монтировку /etc. Типичные для Debian initramfs-tools – это, по сути, набор скриптов оболочки, упаковывающих ранний init, который также является просто скриптом оболочки.

Инструментарий содержит хуки, которые можно использовать для добавления пользовательских файлов в initramfs (название хук может быть немного ошибочно; они выполняются update-initramfs при создании новых initramfs, обычно после обновления ядра) и скрипты, которые исполняются в различных точках при раннем пользовательском пространстве init (снова название немного сбивает с толку, потому что это то, что я бы назвал хуком).

Удачи, если вы выберете этот путь. Если вы хотите initramfs на Debian, вы можете извлечь его (они располагаются в /boot/initrd.image*) с помощью команды unmkinitramfs и изучить, как интегрируются другие инструменты, например, dropbear-initramfs.

Btrfs

Другим, гораздо более простым вариантом является использование btrfs в качестве корневой файловой системы. Если использование btrfs для вас возможно, вы можете использовать функцию субтомов btrfs, которая может быть доступной только для чтения.

Субтом btrfs может быть смонтирован как отдельная файловая система, но для вас прекрасная вещь заключается в том, что в этом нет нужды. Монтирование субтома автоматически монтирует все подтома ниже.

Вы можете довольно просто сделать /etc отдельным субтомом¹. После установки вы можете выполнить что-то вроде

mv /etc /etc-tmp
btrfs subvolume create /etc
mv /etc-tmp/* /etc-tmp/.* /etc
rmdir /etc-tmp

Теперь у вас есть /etc в другом субтоме, чем все остальное, вы можете сделать корневой субтом доступным только для чтения с помощью следующей команды, а /etc останется доступным для записи.

btrfs property set -t subvol / ro true

¹Кстати говоря: тот же подход можно использовать, чтобы сделать /usr доступным только для чтения. Всегда забавно, если перемещаете существующий /usr, создаете субтом и, когда вы хотите вернуть содержимое обратно, вас встречает “mv: команда не найдена”, потому что ваш /usr/bin в данный момент пуст.

У меня есть Raspberry Pi с корневой файловой системой только для чтения. Есть второй раздел, смонтированный с возможностью записи. /etc, /var, /home и /srv — это монтирования привязки к разделу с возможностью записи. Это, вероятно, будет работать на любом Linux.

Я начал с образа Raspberry Pi, загруженного из интернета. Я записал образ Raspberry Pi на SD-карту. Образ Raspberry Pi не заполнит всю SD-карту, поэтому в конце SD-карты оставалось неиспользованное неформатированное пространство. Я расширил корневой раздел, затем использовал оставшееся пространство для создания другого раздела ext4. Я внес несколько изменений в /etc, такие как учетная запись пользователя, имя узла, часовой пояс и др. Я переместил /etc/fstab в /fstab и сделал /etc/fstab символической ссылкой на ../fstab. Я скопировал /etc, /var, /home и /srv на второй раздел. Затем я изменил /fstab на:

PARTUUID=19de2757-02  /                      ext4   ro,noatime  0 1
PARTUUID=19de2757-01  /boot                  vfat   ro          0 2
PARTUUID=19de2757-03   /readwrite_partition  ext4   defaults    0 2
/readwrite_partition/var  /var  none bind 0 0
/readwrite_partition/etc  /etc  none bind 0 0
/readwrite_partition/home /home none bind 0 0
/readwrite_partition/srv  /srv  none bind 0 0

Этот Raspberry Pi выключается отключением питания, без нормального завершения работы. Я сделал корневой раздел только для чтения, чтобы было меньше шансов повреждения данных при завершении работы.

Если возникнет какая-то проблема, и невозможно будет смонтировать раздел с возможностью записи, он должен использовать версии /etc, /home/, /srv и /var из раздела только для чтения и должен уметь запускать утилиты восстановления диска на разделе с возможностью записи.

Я использую это два года и не имел проблем.

Другие люди делают /var отдельным разделом, а затем /home символической ссылкой на /var/local/home.

Для /etc нужно, чтобы только некоторые файлы были доступны для записи. Я сделал весь /etc доступным для записи, потому что мне было лень разбираться, какие файлы нуждаются в доступе для записи, а какие могут быть только для чтения.

Я включил /srv в раздел с возможностью записи, потому что другие люди предложили это. Но /srv пуст, поэтому это не имеет значения.

Я переместил /etc/fstab в /fstab, потому что думал, что могут возникнуть проблемы, если будет две версии /etc/fstab, одна на разделе только для чтения и одна на разделе с возможностью записи, и на версиях будут различия. Раздел с возможностью записи монтируется согласно /etc/fstab на разделе только для чтения, но как только система работает, /etc/fstab на разделе только для чтения скрыт /readwrite_partition/etc/fstab, и /etc/fstab на разделе только для чтения не может быть отредактирован. Я не знал, потребуется ли мне редактировать /etc/fstab, поэтому я переместил /etc/fstab в /fstab, чтобы я мог отредактировать /etc/fstab. Если вы уверены, что вам никогда не потребуется редактировать /etc/fstab, то нет необходимости перемещать /etc/fstab в /fstab.

Сложнее всего решить, сколько конфигурации и редактирования в /etc вы хотите сделать до копирования /etc из раздела только для чтения на раздел с возможностью записи.

Большинство дистрибутивов Linux выполняют неконфигурацию при первом запуске и начинают создавать логи. Вы можете захотеть выполнить первый запуск перед копированием /etc, /var, /home и /srv на раздел с возможностью записи, или вам может понадобиться скопировать файлы до первого запуска. Преимущество первого запуска до копирования в том, что оба /etc будут включать конфигурацию первого запуска. Преимущество копирования до первого запуска заключается в том, что версии только для чтения /etc, /home, /var и /srv сохранят исходную конфигурацию и не будут тратить место на диске на бесполезные логи.

Ответ или решение

Для реализации проекта на встраиваемых устройствах с файловой системой root в режиме "только для чтения" и возможностью модификации системных конфигураций, в том числе в директории /etc, существует несколько подходов. В описанной вами ситуации, когда пользователи могут изменять конфигурации, но не могут устанавливать программы, существует необходимость отдельного управления файловой системой, особенно в отношении /etc, где размещены как необходимые для старта системы файлы, так и те, которые могут быть изменены во время работы.

Опция 1: Использование initramfs

Initramfs (initial RAM filesystem) предлагает решение для временной загрузки файловой системы в оперативную память. Вы можете разработать initramfs, который будет содержать команды для монтирования необходимых частей основной файловой системы и конфигурации, а также выполнять другие скрипты инициализации. Это позволит вам загружать минимальную среду, в которой вскроете необходимую функциональность для монтирования основной файловой системы с разделами "только для чтения" и "чтение/запись". Данный метод сложен в реализации, и требует знаний о строении системы и скриптовых корректировках, однако он обеспечит гибкость в управлении конфигурациями.

Опция 2: Использование Btrfs

Файловая система Btrfs предлагает простой и эффективный способ управления разделами за счет создания подтомов (subvolumes), которые могут быть независимыми по свойствам. В данном случае, /etc может быть создан как отдельный подтом Btrfs, и корневой раздел может быть установлен в режим "только для чтения", в то время как /etc останется доступной для записи. Это достигается следующими командами:

mv /etc /etc-tmp
btrfs subvolume create /etc
mv /etc-tmp/* /etc-tmp/.* /etc
rmdir /etc-tmp
btrfs property set -t subvol / ro true

Опция 3: Использование привязок (bind mounts)

Если вы используете Raspberry Pi или аналогичное устройство с двумя разделами, root в режиме "только чтение" и дополнительным разделом для записи, можно использовать механизмы bind mounts. Это позволит монтировать необходимые директории, такие как /etc, /var, /home, из раздела для записи.

Пример изменения файла /etc/fstab для такой конфигурации:

PARTUUID=19de2757-02 / ext4 ro,noatime 0 1
PARTUUID=19de2757-01 /boot vfat ro 0 2
PARTUUID=19de2757-03 /readwrite_partition ext4 defaults 0 2
/readwrite_partition/var /var none bind 0 0
/readwrite_partition/etc /etc none bind 0 0
/readwrite_partition/home /home none bind 0 0
/readwrite_partition/srv /srv none bind 0 0

Этот подход позволяет сохранить изменения в конфигурациях и избежать повреждений данных при неожиданном отключении питания.

Таким образом, в зависимости от ваших технологических возможностей и требований относительно уровня сложности и надежности, вы можете выбрать один из указанных методов. Эти решения позволяют гибко управлять файлами конфигурации в /etc даже при корневом разделе в режиме "только для чтения".

Оцените материал
Добавить комментарий

Капча загружается...