Compare commits

...

1 Commits
v3.0 ... v4.0

Author SHA1 Message Date
nnz1024
15e9a095a1 Version v4.0 (2011-04-29 20:05) [AUTO] 2017-08-17 23:05:37 +03:00

290
s4a.tex
View File

@@ -1098,6 +1098,296 @@ systemd и на этот случай есть простое решение, и
+systemctl start+ отменяет действия +systemctl stop+, +systemctl enable+
отменяет действие +systemctl disable+, а +rm+ отменяет действие +ln+.
\section{Смена корня}
Практически все администраторы и разработчики рано или поздно встречаются с
\href{http://linux.die.net/man/1/chroot}{chroot-окружениями}. Системный вызов
+chroot()+ позволяет задать для определенного процесса (и его потомков) каталог,
который они будут рассматривать как корневой +/+, тем самым ограничивая для них
область видимости иерархии файловой системы отдельным поддеревом. Большинство
применений chroot-окружений можно отнести к двум классам задач:
\begin{enumerate}
\item Обеспечение безопасности. Потенциально уязвимый демон chroot'ится
в отдельный каталог, и даже в случае успешной атаки, взломщик
увидит лишь содержимое этого каталога, а не~всю файловую
систему~--- он окажется в ловушке chroot'а.
\item Подготовка и управление образом операционной системы при отладке,
тестировании, компиляции, установке или восстановлении. При этом
вся иерархия файловых систем гостевой ОС монтируется или
создается в каталоге системы-хоста, и при запуске оболочки (или
любого другого приложения) внутри этой иерархии, их корень
сдвигается в этот каталог. Система, которую <<видят>> такие
программы, может сильно отличаться от ОС хоста. Например, это
может быть другой дистрибутив, или даже другая аппаратная
архитектура (запуск i386-гостя на x86\_64-хосте). Гостевая ОС
не~может увидеть полной иерархии ОС хоста.
\end{enumerate}
В системах, использующих классический SysV init, использовать chroot-окружения
сравнительно несложно. Например, чтобы запустить выбранного демона внутри
иерархии гостевой ОС, достаточно смонтировать внутри этой иерархии +/proc+,
+/sys+ и остальные API ФС, воспользоваться программой +chroot(1)+ для входа в
окружение, и выполнить соответствующий init-скрипт, запустив +/sbin/service+
внутри окружения.
Но в системах, использующих systemd, уже не~все так просто. Одно из важнейших
достоинств systemd состоит в том, что параметры среды, в которой запускаются
демоны, никак не~зависит от метода их запуска. В системах, использующих SysV
init, многие параметры среды выполнения (в частности, лимиты на системные
ресурсы, переменные окружения, и т.п.) наследуются от оболочки, из которой был
запущен init-скрипт. При использовании systemd ситуация меняется радикально:
пользователь просто уведомляет init-демона о необходимости запустить ту или иную
службу, и тот запускает демона в чистом, созданном <<с нуля>> и тщательно
настроенном окружении, параметры которого никак не~зависят от настроек среды, из
которой была отдана команда. Такой подход полностью отменяет традиционный метод
запуска демонов в chroot-окружениях: теперь демон порождается процессом init
(PID~1) и наследует корневой каталог от него, вне зависимости от того, находился
ли пользователь, отдавший команду на запуск, в chroot-окружении, или нет. Кроме
того, стоит особо отметить, что взаимодействие управляющих программ с systemd
происходит через сокеты, находящиеся в каталоге +/run/systemd+, так что
программы, запущенные в chroot-окружении, просто не~смогут взаимодействовать с
init-подсистемой (и это, в общем, неплохо, а если такое ограничение будет
создавать проблемы, его можно легко обойти, используя bind-монтирование).
В свете вышесказанного, возникает вопрос: как правильно использовать
chroot-окружения в системах на основе systemd? Что ж, постараемся дать подробный
и всесторонний ответ на этот вопрос.
Для начала рассмотрим первое из перечисленных выше применений chroot: изоляция в
целях безопасности. Прежде всего, стоит заметить, что защита, предоставляемая
chroot'ом, весьма эфемерна и ненадежна, так как chroot не~является <<дорогой с
односторонним движением>>. Выйти из chroot-окружения сравнительно несложно, и
соответствующее предупреждение даже
\href{http://linux.die.net/man/2/chroot}{присутствует на странице руководства}.
Действительно эффективной защиты можно достичь, только сочетая chroot с другими
методиками. В большинстве случаев, это возможно только при наличии поддержки
chroot в самой программе. Прежде всего, корректное конфигурирование
chroot-окружения требует глубокого понимания принципов работы программы.
Например, нужно точно знать, какие сокеты использует программа, чтобы обеспечить
bind-монтирование соответствующих каталогов. С учетом вышесказанного,
эффективный chroot-защита обеспечивается в том случае, когда она реализована в
коде самого демона. Именно разработчик лучше других знает (\emph{обязан знать}),
как правильно сконфигурировать chroot-окружение, и какой минимальный набор
файлов, каталогов и файловых систем необходим внутри для нормальной работы
демона. Уже сейчас существуют демоны, имеющие встроенную поддержку chroot.
К сожалению, в системе Fedora, установленной с параметрами по умолчанию, таких
демонов всего два: \href{http://avahi.org/}{Avahi} и RealtimeKit. Оба они
написаны одним очень хитрым человеком ;-) (Вы можете собственноручно
убедится в этом, выполнив команду +ls -l /proc/*/root+.)
Возвращаясь к тема нашего обсуждения: разумеется, systemd позволяет помещать
выбранных демонов в chroot, и управлять ими точно так же, как и другими.
Достаточно лишь указать параметр +RootDirectory=+ в соответствующем
service-файле. Например:
\begin{Verbatim}
[Unit]
Description=A chroot()ed Service
[Service]
RootDirectory=/srv/chroot/foobar
ExecStartPre=/usr/local/bin/setup-foobar-chroot.sh
ExecStart=/usr/bin/foobard
RootDirectoryStartOnly=yes
\end{Verbatim}
Рассмотрим этот пример подробнее. Параметр +RootDirectory=+ задает каталог, в
который производится chroot перед запуском исполняемого файла, заданного
параметром +ExecStart=+. Заметим, что путь к этому файлу должен быть указан
относительно каталога chroot (так что, в нашем случае, с точки зрения основной
системы, на исполнение будет запущен файл +/srv/chroot/foobar/usr/bin/foobard+).
Перед запуском демона будет вызван сценарий оболочки +setup-foobar-chroot.sh+,
который должен обеспечить подготовку chroot-окружения к запуску демона
(например, смонтировать в нем +/proc+ и/или другие файловые системы, необходимые
для работы демона). Указав +RootDirectoryStartOnly=yes+, мы задаем, что
+chroot()+ будет выполняться только перед выполнением файла из +ExecStart=+, а
команды из других директив, в частности, +ExecStartPre=+, будут иметь полный
доступ к иерархии файловых систем ОС (иначе наш скрипт просто не~сможет
выполнить bind-монтирование нужных каталогов). Более подробную информацию по
опциям конфигурации вы можете получить на
\href{http://0pointer.de/public/systemd-man/systemd.service.html}{страницах}
\href{http://0pointer.de/public/systemd-man/systemd.exec.html}{руководства}.
Поместив приведенный выше текст примера в файл
+/etc/systemd/system/foobar.service+, вы сможете запустить chroot'нутого демона
командой +systemctl start foobar.service+. Информацию о его текущем состоянии
можно получить с помощью команды +systemctl status foobar.service+. Команды
управления и мониторинга службы не~зависят от того, запущена ли она в chroot'е,
или нет. Этим systemd отличается от класического SysV init.
Новые ядра Linux поддерживают возможность создания независимых пространств имен
файловых систем (в дальнейшем FSNS, от <<file system namespaces>>). По
функциональности этот механизм аналогичен +chroot()+, однако предоставляет
гораздо более широкие возможности, и в нем отсутствуют проблемы с безопасностью,
характерные для chroot. systemd позволяет использовать при конфигурировании
юнитов некоторые возможности, предоставляемые FSNS. В частности, использование
FSNS часто является гораздо более простой и удобной альтернативой созданию
полноценных chroot-окружений. Используя директивы +ReadOnlyDirectories=+,
+InaccessibleDirectories=+, вы можете задать ограничения по использованию
иерархии файловых систем для заданной службы: ее корнем будет системный корневой
каталог, однако указанные в этих директивах подкаталоги будут доступны только
для чтения или вообще недоступны для нее. Например:
\begin{Verbatim}
[Unit]
Description=A Service With No Access to /home
[Service]
ExecStart=/usr/bin/foobard
InaccessibleDirectories=/home
\end{Verbatim}
Такая служба будет иметь доступ ко всей иерархии файловых систем ОС, с
единственным исключением~--- она не~будет видеть каталог +/home+, что позволит
защитить данные пользователей от потенциальных хакеров. (Подробнее об этих
опциях можно почитать на
\href{http://0pointer.de/public/systemd-man/systemd.exec.html}{странице
руководства}).
Фактически, FSNS по множеству параметров превосходят +chroot()+. Скорее всего,
Avahi и ReltimeKit в ближайшем будущем перейдут с +chroot()+ к использованию
FSNS.
Итак, мы рассмотрели вопросы использования chroot для обеспечения безопасности.
Переходим ко второму пункту: Подготовка и управление образом операционной
системы при отладке, тестировании, компиляции, установке или восстановлении.
chroot-окружения, по сути, весьма примитивно: они изолируют только иерархии
файловых систем. Даже после chroot'а в определенный подкаталог, процесс
по-прежнему имеет полный доступ к системным вызовам, может убить любой процесс
из основной системы, и т.п. Вследствие этого, запуск полноценной ОС (или ее
части) внутри chroot'а несет угрозу для хост-системы: у гостя и хоста отличаются
лишь содержимое файловой системы, все остальное у них общее. Например, если вы
обновляете дистрибутив, установленный в chroot-окружении, и пост-установочный
скрипт пакета отправляет +SIGTERM+ процессу init для его
перезапуска\footnote{Прим. перев.: Во избежание путаницы отметим, что перезапуск
процесса init (PID~1) <<на лету>> при получении +SIGTERM+ поддерживается только
в systemd, в классическом SysV init такой возможности нет}, на него среагирует
именно хост-система! Кроме того, хост и chroot'нутая система будут иметь общую
разделяемую память SysV (SysV shared memory), общие сокеты из абстрактных
пространств имен (abstract namespace sockets) и другие элементы IPC. Для
отладки, тестирования, компиляции, установки и восстановлении ОС не~требуется
абсолютно безопасная изоляция, однако нужна защита от \emph{случайного}
воздействия на ОС хоста изнутри chroot-окружения, иначе вы можете получить целый
букет проблем, как минимум, от пост-инсталляционных скриптов при установке
пакетов в chroot-окружении.
systemd имеет целый ряд возможностей, полезных для работы с chroot-системами:
Прежде всего, управляющая программа +systemctl+ автоматически определяет, что
она запущена в chroot-системе. В такой ситуации будут работать только команды
+systemctl enable+ и +systemctl disable+, во всех остальных случаях +systemctl+
просто не~будет ничего делать, возвращая код успешного завершения операции.
Таким образом, пакетные скрипты смогут включить/отключить запуск <<своих>> служб
при загрузке (или в других ситуациях), однако команды наподобие
+systemctl restart+ (обычно выполняется при обновлении пакета) не~дадут никакого
эффекта внутри chroot-окружения\footnote{Прим. перев.: автор забывает отметить
не~вполне очевидный момент: такое поведение +systemctl+ проявляется только в
<<мертвых>> окружениях, т.е. в тех, где не~запущен процесс init, и
соответственно отсутствуют управляющие сокеты в +/run/systemd+. Такая ситуация
возникает, например, при установке системы в chroot через
debootstrap/febootstrap. В этом случае возможности +systemctl+ ограничиваются
операциями с символьными ссылками, определяющими триггеры активации юнитов, т.е.
выполнением действий +enable+ и +disable+, не~требующих непосредственного
взаимодействия с процессом init}.
Однако, куда более интересные возможности предоставляет программа
\href{http://0pointer.de/public/systemd-man/systemd-nspawn.html}{systemd-nspawn},
входящая в стандартный комплект поставки systemd. По сути, это улучшенный аналог
+chroot(1)+~--- она не~только подменяет корневой каталог, но и создает отдельные
пространства имен для дерева файловых систем (FSNS) и для идентификаторов
процессов (PID NS), предоставляя легковесную реализацию системного
контейнера\footnote{Прим. перев.: Используемые в +systemd-nspawn+ механизмы
ядра Linux, такие, как FS NS и PID NS, также лежат в основе
\href{http://lxc.sourceforge.net/}{LXC}, системы контейнерной изоляции для
Linux, которая позиционируется как современная альтернатива классическому
\href{http://wiki.openvz.org/Main_Page}{OpenVZ}. Стоит отметить, что LXC
ориентирована прежде всего на создание независимых виртуальных окружений,
с поддержкой раздельных сетевых стеков, ограничением на ресурсы, сохранением
настроек и т.п., в то время как +systemd-nspawn+ является лишь более удобной и
эффективной заменой команды +chroot(1)+, предназначенной прежде всего для
развертывания, восстановления, сборки и тестирования операционных систем. Далее
автор разъясняет свою точку зрения на этот вопрос}.
+systemd-nspawn+ проста в использовании как +chroot(1)+, однако изоляция
от хост-системы является более полной и безопасной. Всего одной командой
вы можете загрузить внутри контейнера \emph{полноценную} ОС (на базе systemd
или SysV init). Благодаря использованию независимых пространств идентификаторов
процессов, процесс init внутри контейнера получит PID~1, что позволит работать
ему в штатном режиме. Также, в отличие от +chroot(1)+, внутри окружения
будут автоматически смонтированы +/proc+ и +/sys+.
Следующий пример иллюстрирует возможность запустить Debian в на Fedora-хосте
всего тремя командами:
\begin{Verbatim}
# yum install debootstrap
# debootstrap --arch=amd64 unstable debian-tree/
# systemd-nspawn -D debian-tree/
\end{Verbatim}
Вторая из этих команд обеспечивает развертывание в подкаталоге +./debian-tree/+
файловой структуры дистрибутива Debian, после чего третья команда запускает
внутри полученной системы процесс командной оболочки. Если вы хотите запустить
внутри контейнера полноценную ОС, воспользуйтесь командой
\begin{Verbatim}
# systemd-nspawn -D debian-tree/ /sbin/init
\end{Verbatim}
После быстрой загрузки вы получите приглашение оболочки, запущенной внутри
полноценной ОС, функционирующей в контейнере. Изнутри контейнера невозможно
увидеть процессы, которые находятся вне его. Контейнер сможет пользоваться сетью
хоста, однако не~имеет возможности изменить ее настройки (это может привести к
серии ошибок в процессе загрузки гостевой ОС, но ни~одна из этих ошибок
не~должна быть критической). Контейнер получает доступ к +/sys+ и +/proc/sys+,
однако, во избежание вмешательства контейнера в конфигурацию ядра и аппаратного
обеспечения хоста, эти каталоги будут смонтированы только для чтения. Обратите
внимание, что эта защита блокирует лишь \emph{случайные}, \emph{непредвиденные}
попытки изменения параметров. При необходимости, процесс внутри контейнера,
обладающий достаточными полномочиями, сможет перемонтировать эти файловые
системы в режиме чтения-записи.
Итак, что же такого хорошего в +systemd-nspawn+?
\begin{enumerate}
\item Использовать эту утилиту очень просто. Вам даже не~нужно вручную монтировать
внутри окружения +/proc+ и +/sys+~--- она сделает это за вас, а
ядро автоматически отмонтирует их, когда последний процесс
контейнера завершится.
\item Обеспечивается надежная изоляция, предотвращающая случайные
изменения параметров ОС хоста изнутри контейнера.
\item Теперь вы можете загрузить внутри контейнера полноценную ОС, а
не~одну-единственную оболочку.
\item Эта утилита очень компактна и присутствует везде, где установлен
systemd. Она не~требует специальной установки и настройки.
\end{enumerate}
systemd уже подготовлен для работы внутри таких контейнеров. Например, когда
подается команда на выключение системы внутри контейнера, systemd на последнем
шаге вызывает не~+reboot()+, а просто +exit()+.
Стоит отметить, что +systemd-nspawn+ все же не~является полноценной системой
контейнерной виртуализации/изоляции~--- если нужно именно это, воспользуйтесь
\href{ http://lxc.sourceforge.net/}{LXC}. Этот проект использует те же самые
механизмы ядра, но предоставляет куда более широкие возможности, включая
виртуализацию сети. Если вам угодно, +systemd-nspawn+ как реализация контейнера
похожа на GNOME~3~--- компактна и проста в использовании, опций для настройки
очень мало. В то время как LXC больше похож на KDE: опций для настройки больше,
чем строк кода. Я создал +systemd-nspawn+ специально для тестирования, отладки,
сборки, восстановления. Именно для этих задач вам стоит ее использовать~--- она
неплохо с ними справляется, куда лучше, чем +chroot(1)+.
Что ж, пора заканчивать. Итак:
\begin{enumerate}
\item Использование +chroot()+ для обеспечения безопасности дает
наилучший результат, когда оно реализовано непосредственно в
коде самой программы.
\item +ReadOnlyDirectories=+ и +InaccessibleDirectories=+ могут быть
удобной альтернативой созданию полноценных chroot-окружений.
\item Если вам нужно поместить в chroot-окружение какую-либо службу,
воспользуйтесь опцией +RootDirectory=+.
\item +systemd-nspawn+~--- очень неплохая штука.
\item chroot'ы убоги, FSNS~---
\href{http://ru.wikipedia.org/wiki/Leet}{1337}.
\end{enumerate}
И все это уже сейчас доступно в Fedora~15.
\end{document}
vim:ft=tex:tw=80:spell:spelllang=ru