From fb2a8ab3cd32bca8c0e0a1c9fd4ca128bf74d848 Mon Sep 17 00:00:00 2001 From: nnz1024 <0comffdiz@inbox.ru> Date: Thu, 26 Oct 2017 14:40:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D1=8C=D0=B8=20"Dynamic=20Users=20with=20syst?= =?UTF-8?q?emd"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- s4a.tex | 685 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 684 insertions(+), 1 deletion(-) diff --git a/s4a.tex b/s4a.tex index 6234f8c..410bc5c 100644 --- a/s4a.tex +++ b/s4a.tex @@ -18,6 +18,8 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}} %\setcounter{tocdepth}{1} % А почему бы и нет? % Несколько сокращений \newcommand{\sectiona}[1]{\section*{#1}\addcontentsline{toc}{section}{#1}} +\newcommand{\subsectiona}[1]{\subsection*{#1}% + \addcontentsline{toc}{subsection}{#1}} \newcommand{\hreftt}[2]{\href{#1}{\texttt{#2}}} \newenvironment{caveat}[1][]{\smallskip\par\textbf{Предупреждение#1: }}% {\smallskip\par} @@ -26,6 +28,8 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}} \newcommand{\qna}[1]{\medskip\par\textbf{Вопрос: #1}\nopagebreak\par Ответ:} \newcommand\yousaywtf[1]{\emph{#1}} \newcommand\yousaywtfsk[1]{\yousaywtf{#1}\medskip\par} +\newcommand\llquote{\texorpdfstring{<<}{"}} +\newcommand\rrquote{\texorpdfstring{>>}{"}} % Настройка макета страницы \setlength{\hoffset}{-1.5cm} \addtolength{\textwidth}{2cm} @@ -2762,6 +2766,7 @@ PrivateNetwork=yes котором настраивается только интерфейс обратной петли. \subsection{Предоставление службам независимых каталогов \texttt{/tmp}} +\label{sec:privatetmp} Еще одна простая, но мощная опция настройки служб~--- +PrivateTmp=+: \begin{Verbatim} @@ -5234,7 +5239,7 @@ RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX (network namespace), иерархию монтирования (mount namespace, также использовался термин filesystem namespace), иерархию контрольных групп (cgroup namespace), очереди сообщений POSIX и интерфейсы SysV IPC (IPC namespace), имя хоста (UTS -namespace), независимые списки индентификаторов процессов (PID namespace) и +namespace), независимые списки идентификаторов процессов (PID namespace) и пользователей/групп (user namespace). Лежат в основе систем контейнерной изоляции, таких как LXC и Docker. Также широко используются в systemd для обеспечения безопасности~--- см. главы \ref{sec:chroots} и @@ -5262,6 +5267,684 @@ IPC)\footnote{Прим. перев.: Как и в предыдущем случ директивы в поставляемые по умолчанию юнит-файлы, так как эти люди лучше знают, какие именно привилегии минимально необходимы для функционирования их демонов. +\sectiona{Динамические пользователи} + +\emph{Коротко о главном: теперь вы можете настроить systemd таким образом, чтобы +он автоматически выделял новый идентификатор пользователя (UID) для службы при +ее запуске, и автоматически освобождал его при остановке службы. Такой подход +абсолютно безопасен и может также применяться в сочетании с другими технологями +systemd, в частности, одноразовыми службами (transient services), службами с +активацией через сокет и экземплярами служб.} + +В выпуске +\href{https://lists.freedesktop.org/archives/systemd-devel/2017-October/039589.html}% +{systemd 235}, помимо прочих улучшений, значительно расширена поддержка +\emph{динамических пользователей} (базовая функциональность для которой +появилась еще в версии 232). Это мощная и удобная, но пока малоизвестная +технология, и в данной статье я попытаюсь исправить последний недостаток, +рассказав о ней более подробно. + +Понятие \emph{пользователя} является одной из ключевых концепций безопасности +POSIX-совместимых операционных систем. Большинство созданных впоследствии +механизмов обеспечения безопасности (SELinux и другие системы мандатного +контроля доступа, capabilities, пространства имен пользователей и т.д.) так или +иначе связаны или взаимодействуют с концепцией пользователя. Даже если вы хотите +пересобрать ядро Linux, полностью отключив все возможные механизмы безопасности, +от пользователей вы, скорее всего, не~избавитесь. + +Изначально, понятие пользователя было введено при создании многопользовательских +систем, рассчитанных на одновременную работу нескольких +пользователей-\emph{людей}, обеспечивая взаимную изоляцию и разделение ресурсов. +Однако, большинство современных UNIX-систем используют концепцию пользователей +иначе: несмотря на то, что реальный пользователь у них может быть только один +(или вообще ни одного!), в их списках пользователей (то есть файлах ++/etc/passwd+), записей намного больше. В наше время, большинство пользователей +типичной UNIX-системы являются \emph{системными}~--- они соответствуют +не~сидящим перед компьютером живым людям, а наборам полномочий (security +identity), с которыми запускаются системные службы (т.е. программы). Хотя +традиционные многопользовательские системы постепенно теряют свою актуальность, +лежащее в их основе понятие <<пользователь>> стало одним из краеугольных камней +в технологиях защиты UNIX-систем. Большинство служб в современной системе +работает от имени своего пользователем, которому даны минимально возможные +полномочия. + +Создатели ОС Android хорошо понимали значимость концепции пользователя для +безопасности системы, и пошли еще дальше: в Android пользователи создаются +не~только для системных служб, но и для каждого графического приложения, что +позволяет обеспечить разделение ресурсов и защиту процессов разных приложений +друг от друга. + +Тем не~менее, в традиционных Linux-системах концепции пользователей пока +уделяется меньше внимания. Несмотря на то, что она являтся ключевой технологией +обеспечения безопасности UNIX-системы, механизмы создания пользователей и +управления ими пока не~обладают достаточной гибкостью. В большинстве случаев, +если вы устанавливаете службу, которая выполняется от своего пользователя, +работу по созданию учетных записей берут на себя установочные скрипты RPM или +DEB-пакетов. После этого, созданный пользователь останется в системе уже +навсегда, даже если вы удалите соответствующий пакет. Большинство дистрибутивов +Linux используют для идентификаторов системных пользователей диапазон от 0 до +1000~--- не~так уж и много. Следовательно, создание пользователей является +<<дорогой>> операцией: количество идентификаторов ограничено, и не~существует +механизма для их автоматического освобождения после использования. Если вы +будете активно использовать концепцию системных пользователей, вы рано или +поздно исчерпаете доступный лимит. + +У вас может возникнуть вопрос: почему системные пользователи не~удаляются при +удалении создавшего их пакета (как минимум, в большинстве дистрибутивов)? +Причиной этому является одно из важных свойств концепции пользователя (его можно +даже назвать \emph{ошибкой дизайна}): идентификаторы пользователей +сохраняются в атрибутах файлов (и некоторых других объектов, в частности, +элементов IPC). Если служба, работающая из-под отдельного системного +пользователя, создаст где-либо новый файл, то даже после остановки службы и +удаления ее пакета, числовой идентификатор ее пользователя (UID) останется в +метаданных этого файла. Если бы наша система удаляла системных пользователей +вместе с их пакетами, то рано или поздно этот идентификатор заняла бы +какая-нибудь другая служба. И это уже можно считать уязвимостью, так как файл с +данными одной службы становится доступен для другой. Как следствие, дистрибутивы +склонны избегать повторного использования UID, и каждый созданный системный +пользователь остается в системе навсегда. + +Выше мы рассматривали сложившуюся к настоящему моменту ситуацию. А теперь +посмотрим, что нового вносит наша концепция динамических пользователей, и какие +проблемы она может решить. + +\subsectiona{Что такое \llquote{}динамический пользователь\rrquote{}?} + +Реализованный в systemd механизм динамических пользователей нацелен на упрощение +и удешевление операций создания пользователей <<на лету>>, и в перспективе +позволит значительно расширить область примнения концепции пользователей в +целом. + +При создании или редактировании service-файла вашей службы, вы можете включить +для нее механизм динамических пользователей, добавив в секцию +[Service]+ +директиву +\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#DynamicUser=}% +{DynamicUser=yes}. После этого, при каждом запуске данной службы, для нее будет +на лету создаваться системный пользователь. При остановке службы он будет +автоматически удаляться. UID такого пользователя выбирается из диапазона +61184--65519 (при этом производится автоматическая проверка, исключающая +использование UID, занятых кем-либо еще). + +Вы спросите, как же в таком случае решается вышеописанная проблема привязки +идентификаторов пользователей к файлам? Существуют как минимум два очевидных +подхода к ее решению: +\begin{enumerate} + \item Запретить службе создавать файлы, каталоги и объекты IPC. + \item Автоматически удалять созданные службой файлы, каталоги и объекты + IPC при ее остановке. +\end{enumerate} + +systemd реализует одновременно обе стратегии, но применяет их к различным +областям рабочего окружения службы. А именно: +\begin{enumerate} + \item Установка +DynamicUser=yes+ также применяет директивы + \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#ProtectSystem=}% + {ProtectSystem=strict} и + \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#ProtectHome=}% + {ProtectHome=read-only}. В результате служба теряет возможность + что-либо писать практически во все каталоги системы, за + исключением специальных файловых систем (+/dev+, +/proc+ и + +/sys+) и временных каталогов (+/tmp+ и +/var/tmp+). (Кстати, + включать эти опции имеет смысл даже для служб, которые + не~используют режим +DynamicUser=yes+, так как это сильно + снижает возможный ущерб при взломе службы.) + \item Установка +DynamicUser=yes+ автоматически применяет директиву + \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#PrivateTmp=}% + {PrivateTmp=yes}. В результате для этой службы создаются свои + собственные, изолированные от других служб экземпляры каталогов + +/tmp+ и +/var/tmp+\footnote{Прим. перев.: Технически это + реализовано следующим образом: в системных +/tmp+ и +/var/tmp+ + создаются подкаталоги со специальными именами (построенными на + основе имени службы и (псевдо)случайных последовательностей + символов), принадлежащие руту и имеющие права доступа 0700 (это + граничные каталоги~--- принцип их работы описан в данной статье + ниже), а внутри них создаются подкаталоги +tmp+ с правами 0777. + Для самой службы создается пространство имен монтирования (mount + namespace), в котором эти подкаталоги bind-монтируются в +/tmp+ + и +/var/tmp+ соответственно. См. также раздел + \ref{sec:privatetmp}.}, причем их жизненный цикл привязан к + жизненному циклу службы: при остановке службы удаляется + не~только ее пользователь, но и ее временные каталоги. (Опять же + замечу, что эту директиву имеет смысл применять и без + +DynamicUser=yes+, так как она тоже повышает безопасность вашей + системы.) + \item Установка +DynamicUser=yes+ автоматически применяет директиву + \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#RemoveIPC=}% + {RemoveIPC=yes}, которая обеспечивает автоматическое удаление + объектов межпроцессного взаимодействия (IPC) SysV и POSIX (общая + память, очереди сообщений, семафоры), принадлежащих службе, при + ее остановке. Как следствие, жизненный цикл IPC-объектов также + привязан к жизненному циклу службы и ее пользователя. (И снова: + эту директиву имеет смысл применять и для обычных служб!) +\end{enumerate} + +Использование этих четырех опций позволяет практически полностью изолировать +службу с динамическим пользователем от основной системы. Она не~может создавать +файлы или каталоги где-либо, кроме +/tmp+ и +/var/tmp+, где они будут удалены +при ее остановке, как и созданные ею объекты IPC. Таким образом, проблема +владения файлами, каталогами и объектами IPC, успешно решена. + +Если вам нужно немного приоткрыть <<железный занавес>> и дать службе возможность +взаимодействовать с другими программами, это можно сделать при помощи параметра +\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#RuntimeDirectory=}% +{RuntimeDirectory=}. Подкаталог с указанным в этом параметре именем создается в +каталоге +/run+ при запуске службы, принадлежит ее пользователю, и тоже +автоматически удаляется при ее остановке. Однако, при этом он открыт для доступа +других программ, так что служба может размещать в нем различные интерфейсные +объекты (например, UNIX-сокеты), жизненный цикл которых также будет привязан к +жизненному циклу службы. Например, если вы зададите для своей службы ++RuntimeDirectory=foobar+, то увидите, что при ее запуске создается каталог ++/run/foobar+, а при ее остановке он удаляется. (Как и другие описанные здесь +директивы, +RuntimeDirectory=+ прекрасно может работать и без +DynamicUser=+, +предоставляя для вашей службы автоматически создаваемый и удаляемый каталог с +правильным владельцем.) + +\subsectiona{Долговременное хранение данных} + +Вышеописанный способ изоляции службы, хотя и может оказаться полезен в некоторых +случаях, имеет одно существенное ограничение: служба не~может сохранить +какие-либо данные так, чтобы получить к ним доступ при последующих запусках. +Почти все системное дерево каталогов доступно ей только для чтения, а все +доступные на запись каталоги удаляются при остановке и перезапуске. + +В выпуске systemd 235 это ограничение было снято: мы добавили три новых +опции~--- +\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#RuntimeDirectory=}% +{StateDirectory=}, +LogsDirectory=+ и +CacheDirectory=+. По большей части они +действуют аналогично вышеописанной опции +RuntimeDirectory=+, но создают +подкаталоги не~в +/run+, а в +/var/lib+, +/var/log+ и +/var/cache+ +соответственно. Но самое главное их отличие состоит в том, что эти подкаталоги +\emph{не~удаляются} при остановке службы, что позволяет использовать их для +долговременного хранения данных. + +Очевидный вопрос: как при этом решается вышеописанная проблема с динамическими +идентификаторами пользователей? + +Для решения этой задачи мы обратились к опыту систем контейнерной изоляции. +При работе с контейнерами возникает схожая проблема: контейнеры и хост +используют частично или полностью перекрывающиеся наборы числовых идентификаторов +пользователей, а значит, пользователи хоста могут получить доступ к +файлам контейнера, которые принадлежат пользователям с таким же UID~--- а ведь +это может быть совсем другой пользователь, никак не~связанный со своим хостовым +<<двойником>> (кроме случаев, когда используются пространства имен +пользователей\footnote{Прим. перев.: В таких случаях используется механизм +отображения UID (user-remap), когда пользователям, работающим с user +namespaces (например, +dockremap+ в Docker), выдаются диапазоны UID хоста (см. +\hreftt{http://man7.org/linux/man-pages/man5/subuid.5.html}{/etc/subuid}), на +которые отображаются UID гостевой системы. Это решает проблему пересечения UID +хоста и гостя. Без присвоения такого диапазона гость будет иметь только один +UID (в пространстве гостя он может быть любым, даже нулевым), который для хоста +(и его файловой системы) будет виден как UID пользователя контейнера.}). +Особенно неприятно, если это происходит с исполняемым файлом, имеющим ++suid+-бит~--- тогда речь идет уже не~просто о доступе к чужим данным, а о +потенциальной возможности повышения привилегий. Чтобы предотвратить подобные +ситуации, системы управления контейнерами обычно помещают корневые системы +гостей в специальный \emph{граничный} каталог, имеющий ограниченные права +доступа (обычно: права 0700, владелец +root:root+). При этом, +непривилигерованные пользователи хоста уже не~могут добраться до файлов, +принадлежащих их контейнерным <<двойникам>>, просто потому, что не~имеют доступа +ко всему, что лежит в граничном каталоге. В UNIX, чтобы получить доступ к файлу, +вы должны иметь доступ ко всем каталогам по пути к нему, начиная с корневого. + +Как это работает для наших служб с динамическими пользователями? Предположим, +что вы указали +StateDirectory=foobar+, но \emph{не~включали} режим ++DynamicUser=+. В момент запуска такой службы, для нее будет создан каталог ++/var/lib/foobar+, принадлежащий пользователю, от которого запускается +служба\footnote{Прим. перев.: Если режим +DynamicUser=+ отключен, пользователь +службы определяется директивой +\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#User=}% +{User=}. Более подробно ее роль при включенном и выключенном режиме +динамических пользователей рассмотрена \hyperref[itm:setuser]{ниже}.}. Этот +каталог и его содержимое \emph{не~удаляются} после завершения службы. Если же мы +включим для нашей службы режим +DynamicUser=+, алгоритм станет немного +сложнее~--- теперь +/var/lib/foobar+ уже не~каталог, а принадлежащая руту +символьная ссылка на каталог +/var/lib/private/foobar+, который принадлежит +динамическому пользователю, выделенному для службы. Каталог +/var/lib/private+ +играет роль граничного: он принадлежит +root:root+, и имеет права 0700. При +остановке службы, и символьная ссылка, и каталог, на который она указывает, +не~удаляются. Каталог продолжает принадлежать теперь уже освобожденному +идентификатору пользователя, однако граничный каталог не~позволит получить к +нему доступ ни пользователям хоста, ни другим службам, получившим тот же UID. + +Следующий вопрос: если граничный каталог столь успешно защищает подкаталог с +данными службы, то как сама служба сможет получить к ним доступ? Для этого, +служба запускается в специально модифицированном пространстве имен точек +монтирования (mount namespace): помимо уже упомянутых хитростей с монтированием ++/tmp+ и +/var/tmp+\footnote{Прим. перев.: Строго говоря, <<хитростей>> там +гораздо больше~--- упомянутые выше директивы +ProtectSystem=strict+ и ++ProtectHome=read-only+ предполагают перемонтирование целого ряда каталогов, +включая корневой. Но к теме это действительно не~относится.}, добавляется ++tmpfs+, смонтированная в +/var/lib/private+ и доступная только на чтение, с +правами, разрешающими чтение из этого каталога (0755). Внутри этой файловой +системы создан каталог +foobar+, в который bind-монтируется каталог ++/var/lib/private/foobar+ с хоста. В результате и для хоста, и для службы +содержимое этого каталога находится по одному и тому же пути, но при этом <<с +точки зрения>> службы каталог +/var/lib/private+ уже не~является +ограничительным, и в нем отсутствуют подкаталоги, принадлежащие другим службам, +что обеспечивает полную изоляцию служб друг от друга. Символьная сслыка ++/var/lib/foobar+ позволяет не~задумываться о том, используется граничный +каталог или нет: и при включенном, и при выключенном режиме динамических +пользователей, каталог с данными программы доступен по указанному пути. + +Назревает очередной вопрос. Предположим, что мы запустили службу от динамического +пользователя и с настроенным каталогом для хранения данных. Она получила +некоторый UID (назовем его $X$), которому принадлежит этот каталог. Потом мы +перезапустили службу, и она получила новый UID $Y\neq X$. Что тогда произойдет? +Неужели каталог и его содержимое будут все еще принадлжать UID $X$, и наша +служба уже не~сможет получить к ним доступ? Конечно же, нет~--- systemd +рекурсивно поменяет владельца для каталога и его содержимого. + +Разумеется, операция рекурсивной смены владельца (+chown()+) для дерева +каталогов может оказаться весьма затратной (хотя, по моему личному опыту, для +большинства служб это не~так критично, как кажется на первый взгляд), и поэтому +мы ввели две оптимизации, сводящие к минимуму вероятность такой операции. +Во-первых, systemd начинает подбор подходящего UID с некоторого значения, +полученного путем хеширования имени службы. Таким образом, если имя службы +не~поменялось, то при следующем запуске она, скорее всего, получит тот же UID. В +результате, необходимость в +chown()+ отпадает (разумеется, после +соответствующих проверок). Во-вторых, если указанный каталог уже существует, и +его владельцем является неиспользуемый UID из динамического диапазона, то службе +присваивается именно этот UID, что также увеличивает шансы избежать +chown()+. +(На самом деле, выделенный сейчас диапазон на четыре с лишним тысячи UID не~так +уж и велик, и при активном применении динамических пользователей рано или поздно +появятся ситуации, когда обойтись без +chown()+ уже не~удастся.) + +Директивы +CacheDirectory=+ и +LogsDirectory=+ работают по аналогии со ++StateDirectory=+. Единственное их отличие состоит в том, что они управляют +подкаталогами в +/var/cache+ и +/var/log+, и используют граничные каталоги ++/var/cache/private+ и +/var/log/private+ соответственно. + +\subsectiona{Примеры} + +Итак, мы ознакомились с теорией. Попробуем теперь посмотреть, как все это +работает на практике. Простой пример: +\begin{Verbatim} +# cat > /etc/systemd/system/dynamic-user-test.service <>, без каких-либо конфигурационных файлов~--- например, с помощью программы +\hreftt{https://www.freedesktop.org/software/systemd/man/systemd-run.html}% +{systemd-run}, которую можно запустить непосредственно из командной оболочки. +Короче: запуск службы без предварительного создания юнит-файла. +\begin{Verbatim} +# systemd-run --pty --property=DynamicUser=yes --property=StateDirectory=wuff /bin/sh +Running as unit: run-u15750.service +Press ^] three times within 1s to disconnect TTY. +sh-4.4$ id +uid=63122(run-u15750) gid=63122(run-u15750) groups=63122(run-u15750) context=system_u:system_r:initrc_t:s0 +sh-4.4$ ls -al /var/lib/private/ +total 0 +drwxr-xr-x. 3 root root 60 6. Okt 13:21 . +drwxr-xr-x. 1 root root 852 6. Okt 13:21 .. +drwxr-xr-x. 1 run-u15750 run-u15750 8 6. Okt 13:22 wuff +sh-4.4$ ls -ld /var/lib/wuff +lrwxrwxrwx. 1 root root 12 6. Okt 13:21 /var/lib/wuff -> private/wuff +sh-4.4$ ls -ld /var/lib/wuff/ +drwxr-xr-x. 1 run-u15750 run-u15750 0 6. Okt 13:21 /var/lib/wuff/ +sh-4.4$ echo hello > /var/lib/wuff/test +sh-4.4$ exit +exit +# id run-u15750 +id: ‘run-u15750’: no such user +# ls -al /var/lib/private +total 0 +drwx------. 1 root root 66 6. Okt 13:21 . +drwxr-xr-x. 1 root root 852 6. Okt 13:21 .. +drwxr-xr-x. 1 63122 63122 8 6. Okt 13:22 wuff +# ls -ld /var/lib/wuff +lrwxrwxrwx. 1 root root 12 6. Okt 13:21 /var/lib/wuff -> private/wuff +# ls -ld /var/lib/wuff/ +drwxr-xr-x. 1 63122 63122 8 6. Okt 13:22 /var/lib/wuff/ +# cat /var/lib/wuff/test +hello +\end{Verbatim} + +В приведенном примере, мы запускаем интерактивную оболочку +/bin/sh+ как +одноразовую службу +run-u15750.service+ (+systemd-run+ выбрал это имя +автоматически, так как мы не~указали имя службы явно\footnote{Прим. перев.: Это +можно было бы сделать параметром +systemd-run+ +--unit=+.}) под динамическим +пользователем, имя которого, как и в предыдущем примере, унаследовано от имени +службы. Так как мы задали +StateDirectory=wuff+, то каталог для долговременного +хранения данных нашей службы должен быть доступен под именем +/var/lib/wuff+. В +интерактивной оболочке, запущенной в рамках службы, команда +ls+ показывает +граничный каталог +/var/lib/private+ и его содержимое, а также символьную ссылку ++/var/lib/wuff+, указывающую на его подкаталог +wuff+. Наконец, перед +завершением оболочки, мы создаем там тестовый файл. Вернувшись в нашу исходную +оболочку, мы проверяем, существует ли еще пользователь, выделенный для нашей +службы~--- нет, он автоматически удален в момент завершения службы (оболочки). +При помощи аналогичных команд +ls+ мы снова проверяем каталог долговременного +хранения данных службы, на этот раз с хоста. Видим мы почти то же самое, с +двумя исключениями: во-первых, пользователь и группа, владеющие каталогом и его +содержимым, отображаются уже в виде числовых идентификатов, а не~имен. Это +обусловлено тем, что пользователь (то есть ассоциация числового идентификатора с +некоторым именем) был удален в момент завершения службы. Во-вторых, отличаются +права доступа к граничному каталогу: 0755 (читать могут все) изнутри службы, и +0700 (читать может только владелец, т.е. рут)~--- с хоста. + +А теперь попробуем запустить еще одну одноразовую службу, указав ей тот же +каталог с данными: +\begin{Verbatim} +# systemd-run --pty --property=DynamicUser=yes --property=StateDirectory=wuff /bin/sh +Running as unit: run-u16087.service +Press ^] three times within 1s to disconnect TTY. +sh-4.4$ cat /var/lib/wuff/test +hello +sh-4.4$ ls -al /var/lib/wuff/ +total 4 +drwxr-xr-x. 1 run-u16087 run-u16087 8 6. Okt 13:22 . +drwxr-xr-x. 3 root root 60 6. Okt 15:42 .. +-rw-r--r--. 1 run-u16087 run-u16087 6 6. Okt 13:22 test +sh-4.4$ id +uid=63122(run-u16087) gid=63122(run-u16087) groups=63122(run-u16087) context=system_u:system_r:initrc_t:s0 +sh-4.4$ exit +exit +\end{Verbatim} + +Как видим, +systemd-run+ сгенерировал для этой службы уже другое имя, которое +перешло и к ее пользователю, однако числовой идентификатор пользователя остался +прежним~--- systemd подхватил UID владельца каталога с данными (предварительно +убедившись, что он больше никем не~используется). Этим иллюстрируется +вышеописанная оптимизация алгоритма выбора UID (цикл выбора идентификатора +начинается с UID владельца существующего каталога с данными): рекурсивного +выполнения +chown()+ удалось избежать. + +Надеюсь, вышеприведенные примеры помогли вам разобраться и в самой идее, и в +особенностях ее реализации. + +\subsectiona{Практическое применение} + +Итак, мы рассмотрели, как включить механизм динамических пользователей для +юнита, и разобрались, как он реализован. Самое время попытаться понять, где и +для чего он может применяться. + +\begin{itemize} + \item Одно из главных достоинств технологии динамического выделения + UID~--- возможность запускать службы с урезаннмыми привилегиями + (то есть, от отдельного пользователя), не~оставляя в системе + никаких артефактов. Системный пользователь создается и + используется, но после применения автоматически удаляется, и + повторное применение его UID не~несет никаких рисков для + безопасности. Мы можем одной командой запускать одноразовые + службы для выполнения каких-либо операций, изолируя их под + отдельным идентификатором пользователя, без необходимости + вручную создавать и удалять учетную запись, и не~расходуя + доступные UID попусту. + \item Во многих случаях, запуск службы уже не~требует предварительной + подготовки со стороны пакетного менджера. Другими словами, + большинство операций +useradd+/+mkdir+/+chown+/+chmod+, + выполняемых пост-инсталляционными скриптами пакетов, а также + дополнительные конфигурационные файлы в + \hreftt{https://www.freedesktop.org/software/systemd/man/sysusers.d.html}% + {sysusers.d} и + \hreftt{https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html}% + {tmpfiles.d} становятся необязательными, так как эти операции + выполняются автоматически благодаря директивам +DynamicUser=+ и + +StateDirectory=+/+CacheDirectory=+/+LogsDirectory=+, причем + не~при установке/удалении пакета, а непосредственно при + запуске/остановке службы. + \item Сочетание технологий динамических пользователей и одноразовых + служб предоставляет простой механизм изоляции приложений. + Например, предположим, что мы не~доверяем бинарнику +sort+. + Мы можем поместить его в простую и надежную песочницу + динамического пользователя при помощи +systemd-run+, и при этом + сохранить возможность подключать ее через каналы (pipelines) к + другим программам. Простенький пример конвейера, второй элемент + которого запущен от динамического пользователя (который + уничтожается после завершения конвейера): + \begin{Verbatim} +# cat some-file.txt | systemd-run --pipe --property=DynamicUser=1 sort -u | \ + grep -i foobar > some-other-file.txt + \end{Verbatim} + \item Сочетая технологию динамических пользователей и экземпляров + служб\footnote{Прим перев.: Более подробно работа с экземплярами + служб рассмотрена в главе~\ref{sec:instances}.}, можно получить + гибкой и полностью автоматический механизм управления + идентификаторами пользователей. Допустим, мы создаем шаблон + службы +/etc/systemd/system/foobard@.service+: + \begin{Verbatim} +[Service] +ExecStart=/usr/bin/myfoobarserviced +DynamicUser=1 +StateDirectory=foobar/%i + \end{Verbatim} + Теперь предположим, что вы запускаете экземпляр этой службы + для одного из своих клиентов: + \begin{Verbatim} +# systemctl enable foobard@customerxyz.service --now + \end{Verbatim} + Готово! (Надеюсь, понятно, что эту операцию можно повторять + многократно, подставляя каждый раз вместо +customerxyz+ + идентификаторы различных клиентов.) + \item Сочетая технологии динамических пользователей и сокет-активации, + вы легко можете получить систему, где каждое входящее соединение + обслуживается экземпляром процесса, работающим в песочнице + динамически выделенного UID\footnote{Прим перев.: Здесь идет + речь о сокет-активации <<в стиле inetd>>, когда на каждое + соединение создается экземпляр службы. Более подробно она + обсуждается в главе~\ref{sec:inetd}.}. Пример конфигурации + сокета +waldo.socket+: + \begin{Verbatim} +[Socket] +ListenStream=2048 +Accept=yes + \end{Verbatim} + И соответствующего ему шаблона службы +waldo@.service+: + \begin{Verbatim} +[Service] +ExecStart=-/usr/bin/myservicebinary +DynamicUser=yes + \end{Verbatim} + В результате, systemd будет слушать TCP-порт 2048, и на каждое + входящее соединение создавать новый экземпляр +waldo@.service+, + каждый раз с новым идентификатором пользователя, обеспечивающим + его изоляцию от остальных экземпляров. + \item Динамическое выделение пользователей хорошо сочетается с + <<системами без состояния>> (state-less systems), то есть + системами, которые запускаются с пустыми каталогами +/etc+ и + +/var+. Динамическим выделение UID и директивы + +StateDirectory=+, +CacheDirectory=+, +LogsDirectory=+ и + +RuntimeDirectory=+ позволяет автоматических создавать + пользователя и необходимые службе каталоги непосредственно перед + ее запуском. +\end{itemize} + +Динамическое выделение пользователей~--- масштабная и глобальная концепция, и ее +применение, разумеется, не~ограничивается приведенным списком. Этот список~--- +всего лишь попытка разбудить ваше воображение, дать начальный импульс к +размышлениям. + +\subsectiona{Рекомендации сопровождающим пакетов} + +Я уверен, что для значительной доли служб, поставляемых в современных +дистрибутивах, опции +DynamicUser=+, +StateDirectory=+ и т.д., могут оказаться +весьма полезны. Во многих случаях они позволят вообще отказаться от +post-inst+ +скриптов, а также конфигурационных файлов в +sysusers.d+ и +tmpfiles.d+, +объединив все необходимые настройки непосредственно в юнит-файле. Так что, если +вы сопровождаете какой-либо пакет со службой, пожалуйста, рассмотрите +возможность использования этих директив. Тем не~менее, существует ряд ситуаций, +когда данные директивы неэффективны или неприменимы: +\begin{enumerate} + \item Службы, которым нужно писать куда-либо за пределами разрешенного + списка каталогов (+/run/+, +/var/lib/+, + +/var/cache/+, +/var/log/+, +/var/tmp+, +/tmp+, + +/dev/shm+) не~совместимы с описанным подходом. Например, демон, + обновляющий систему~--- ему, как минимум, необходим доступ на + запись в +/usr+. + \item Службы, которые управляют набором процессов, запущенных от + различных пользователей, например, некоторые SMTP-серверы. Если + ваша служба построена по принципу \emph{суперсервера}, то + управление идентификаторами пользователей для своих процессов + она должна осуществлять сама~--- systemd не~должен в это + вмешиваться. + \item Службы, запускаемые от рута, и вообще требующие расширенных + привилегий. + \item Службы, котороые должны запускаться в пространстве имен + монтирования хоста (например, если служба должна создавать точки + монтирования, видимые для всей системы). Как уже упоминалось + выше, +DynamicUser=+ задействует механизмы +ProtectSystem=+, + +PrivateTmp=+ и т.д., которые основаны на запуске службы в + отдельном пространстве имен монтирования. + \item В вашем дистрибутиве пока нет свежих версий systemd: 232 + (поддержка +DynamicUser=+) или 235 (поддержка +StateDirectory=+ + и аналогичных ей опций). + \item Правила создания пакетов для вашего дистрибутива не~разрешают + подобный подход. Уточните эти моменты в правилах и, при + необходимости, обсудите данный вопрос в рассылке вашего + дистрибутива. +\end{enumerate} + +\subsectiona{Дополнительные замечания} + +Еще несколько замечаний, непосредственно относящихся к обсуждаемой теме: +\begin{enumerate} + \item Обратите внимание, что процесс выделения и удаления динамических + пользователей никак не~затрагивает +/etc/passwd+. Добавление + пользователя в базу данных оусществляется при помощи NSS-модуля + glibc + \hreftt{https://www.freedesktop.org/software/systemd/man/nss-systemd.html}% + {nss-systemd}\footnote{Прим. перев.: Разумеется, чтобы + преобразование <<имя-идентификатор>> для пользователя и его + группы работало корректно, это модуль должен быть указан в + строках +passwd:+ и +group:+ файла +/etc/nsswitch.conf+. Примеры + приведены на странице руководства модуля.}, и эта информация + никогда не~попадает на диск. + \item В традиционных UNIX-системах, демоны сбрасывают привилегии с рута + до обычного пользователя самостоятельно, в то время как механизм + динамических пользователей предполагает, что этим должен + заниматься systemd. В версии systemd 235 добавлена возможность + совместить механизм динамических пользователей с самостоятельным + сбросом привилегий процессом службы. Для этого, включите опцию + +DynamicUser=+, а в опции + \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#User=}% + {User=} укажите имя пользователя, в которого ваша служба + перевоплотится (+setuid()+) после инициализации. В результате, в + момент запуска службы systemd создаст динамического пользователя + с указанным именем. Далее, в директиве + \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.service.html\#ExecStart=}% + {ExecStart=} непосредственно перед командой запуска укажите + символ <<+!+>>. После этого, пользователь для службы будет + создаваться, но запускаться она будет от рута~--- systemd + будет считать, что служба сама сбросит полномочия в ходе + инициализации. Например: +ExecStart=!/usr/bin/mydaemond+. При + этом, регистрация соответствия имени и идентификатора + пользователя в базе данных производится, как и прежде, при + запуске службы, и поэтому процесс демона сможет без проблем + преобразововать имя пользователя в UID. + \item У вас может возникнуть вопрос: почему для динамического выделения + UID выбран именно диапазон 61184--65519 (в шестнадцатеричной + записи 0xEF00--0xFFEF)? Он был выбран потому, что большинство + дистрибутивов (например, Fedora) используют для обычных + пользователей идентификаторы ниже 60000, и мы не~хотим + переступать эту границу. Также мы делаем небольшой отступ от + 65535, так как некоторые UID вблизи этого значения имеют + специальное значение (65535 часто трактуется как + <<некорректный>> или <<отсутствующий>> UID, так как является + представлением числа $-1$ в 16-битном целом типе; 65534 обычно + соответствует пользователю +nobody+, и некоторые подсистемы ядра + отображают в это значение <<посторонние>> + идентификаторы\footnote{Прим. перев.: Например, изнутри + пространства имен пользователей, все пользователи хоста, кроме + создателя пространства, выглядят как +nobody+.}). И наконец, мы + не~хотим выходить за пределы 16-битного целого типа. Даже с + распространением технологии пространств имен пользователей, + контейнерам все равно не~нужен весь диапазон значений, + предоставляемый 32-битным целым типом, который используется в + ядре Linux для UID. Там не~менее, очевидно, что контейнеры + должны поддерживать весь 16-битный диапазон~--- как минимум, + из-за +nobody+. (Если честно, я считаю выделение 64 тысяч + идентификаторов на контейнер оптимальным вариантом: верхние 16 + бит из 32-битного UID можно использовать как идентификатор + контейнера, в том время как нижние будут соответствовать + идентификатору пользователя в этом контейнере\ldots{} Надеюсь, + вы не~потеряли нить рассуждений.) И, не~дожидаясь вашего + вопроса: пока нет никакого способа изменить этот диапазон~--- + его границы заданы в исходном коде. Но когда-нибудь мы + обязательно добавим соответствующие настройки. + \item Вы можете поинтересоваться, что произойдет, если вы уже + используете идентификаторы из диапазона 61184--65519 для других + целей? systemd должен обработать такую ситуацию корректно, если + эти идентификаторы зарегистрированы в базе даных пользователей: + выбрав UID, systemd проверят, не~используется ли он кем-то, и + если он занят, выбирает другой~--- до тех пор, пока не~найдет + свободный. Проверка производится прежде всего при помощи + функций NSS. Также просматриваются списки объектов IPC, и + их владельцы проверяются на совпадение с нашим кандидатом. Таким + образом, systemd избегает использования UID, занятых кем-то еще. + Тем не~менее, это сокращает набор доступных идентификаторов, и в + худшем случае выделение пользователя может завершиться ошибкой + из-за отсутствия свободных UID в рабочем диапазоне. + \item Если имя для выделяемого пользователя не~указано явно, systemd + пытается вывести его из имени службы. Однако, далеко не~каждое + корректное имя службы является также корректным именем + пользователя, и чтобы обойти это, используется случайное имя. + Возможно, вам будет удобнее задать имя пользователя вручную~--- + используйте для этого директиву +User=+. + \item \label{itm:setuser} + Если вы используете +User=+ в сочетании с +DynamicUser=on+, но + пользователь с указанным именем уже существует, то для службы + будет использован именно он, а механизм динамического выделения + пользователя для этой службы автоматически отключится. Таким + образом, упрощается переход между статическими и динамическими + пользователями: вы указываете нужное вам имя в +User=+, и пока + этот пользователь существует в системной базе, система будет + использовать его, и лишь при отутствии такого пользователя он + будет создаваться в динамическом режиме. Также это может быть + полезно в других ситуациях, например, чтобы подготовить службы, + использующие динамических пользователей, к возможности перехода + на статические UID, скажем, чтобы применить к ним квоты файловой + системы. + \item systemd всегда выделяет вместе с пользователем еще и группу, с + тем же самым значением идентификатора (UID = GID). + \item Если бы ядро Linux имело механизм наподобие +shiftfs+, то есть + способ смонтировать существующий каталог куда-либо с подменой + UID/GID по некоторому правилу, задаваемому при монтировании, это + значительно упростило бы реализацию работы +StateDirectory=+ в + сочетании с +DynamicUser=+, в частности, позволив отказаться от + рекурсивной смены владельца, и просто монтировать каталог с + хоста в пространство имен гостя, подменив владельца каталога на + UID/GID службы. Однако я не~питаю больших надежд на подобный + вариант, так как все работы в этой области сейчас завязаны на + пространство имен пользователей~--- механизм, который + \emph{никак не~используется} в обсуждаемой технологии (есть + мнение, что он создает гораздо больше проблем, чем решает, хотя + вы можете с этим и не~согласиться). +\end{enumerate} + +На сегодня все! + \appendix \section{FAQ (часто задаваемые вопросы)\sfnote{Перевод статьи