Linux namespace что это

Механизмы контейнеризации: namespaces

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Последние несколько лет отмечены ростом популярности «контейнерных» решений для ОС Linux. О том, как и для каких целей можно использовать контейнеры, сегодня много говорят и пишут. А вот механизмам, лежащим в основе контейнеризации, уделяется гораздо меньше внимания.

Все инструменты контейнеризации — будь то Docker, LXC или systemd-nspawn,— основываются на двух подсистемах ядра Linux: namespaces и cgroups. Механизм namespaces (пространств имён) мы хотели бы подробно рассмотреть в этой статье.

Начнём несколько издалека. Идеи, лежащие в основе механизма пространств имён, не новы. Ещё в 1979 году в UNIX был добавлен системный вызов chroot() — как раз с целью обеспечить изоляцию и предоставить разработчикам отдельную от основной системы площадку для тестирования. Нелишним будет вспомнить, как он работает. Затем мы рассмотрим особенности функционирования механизма пространств имён в современных Linux-системах.

Chroot(): первая попытка изоляции

Название chroot представляет собой сокращение от change root, что дословно переводится как «изменить корень». С помощью системного вызова chroot() и соответствующей команды можно изменить корневой каталог. Программе, запущенной с изменённым корневым каталогом, будут доступны только файлы, находящиеся в этом каталоге.

Файловая система UNIX представляет собой древовидную иерархию:

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Вершиной этой иерархии является каталог /, он же root. Все остальные каталоги — usr, local, bin и другие, — связаны с ним.

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

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Файловая система разделена на две части, и они никак не влияют друг на друга. Как работает chroot? Сначала обратимся к исходному коду. В качестве примера рассмотрим реализацию chroot в OC 4.4 BSD-Lite.

Системный вызов chroot описан в файле vfs_syscall.c:

Самое главное происходит в предпоследней строке приведённого нами фрагмента: текущая директория становится корневой.
В ядре Linux системный вызов chroot реализован несколько сложнее (фрагмент кода взят отсюда):

Рассмотрим особенности работы chroot в Linux на практических примерах. Выполним следующие команды:

В результате выполнения второй команды мы получим сообщение об ошибке:

Ошибка заключается в следующем: не была найдена командная оболочка. Обратим внимание на этот важный момент: с помощью chroot мы создаём новую, изолированную файловую систему, которая не имеет никакого доступа к текущей. Попробуем снова:

Опять ошибка — несмотря на идентичное сообщение, совсем не такая, как в прошлый раз. Прошлое сообщение было выдал шелл, так как не нашёл нужного исполняемого файла. В примере выше об ошибке сообщил динамический линковщик: он не нашёл необходимых библиотек. Чтобы получить к ним доступ, их тоже нужно копировать в chroot. Посмотреть, какие именно динамические библиотеки требуется скопировать, можно так:

После этого выполним следующие команды:

Теперь получилось! Попробуем выполнить в новой файловой системе, например, команду ls:

В ответ мы получим сообщение об ошибке:

Причина понятна: в новой файловой системе команда ls отсутствует. Нужно опять копировать исполняемый файл и динамические библиотеки, как это уже было показано выше. В этом и заключается серьёзный недостаток chroot: все необходимые файлы нужно дублировать. Есть у chroot и ряд недостатков с точки зрения безопасности.

Попытки усовершенствовать механизм chroot и обеспечить более надёжную изоляцию предпринимались неоднократно: так, в частности, появились такие известные технологии, как FreeBSD Jail и Solaris Zones.
В ядре Linux изоляция процессов была усовершенствована благодаря добавлению новых подсистем и новых системных вызовов. Некоторые из них мы разберём ниже.

Механизм пространств имён

Пространство имён (англ. namespace) — это механизм ядра Linux, обеспечивающий изоляцию процессов друг от друга. Работа по его реализации была начата в версии ядра 2.4.19. На текущий момент в Linux поддерживается шесть типов пространств имён:

Пространство имёнЧто изолирует
PIDPID процессов
NETWORKСетевые устройства, стеки, порты и т.п.
USERID пользователей и групп
MOUNTТочки монтирования
IPCSystemV IPC, очереди сообщений POSIX
UTSИмя хоста и доменное имя NIS

Все эти типы используются современными системами контейнеризации (Docker, LXC и другими) при запуске программ.

PID: изоляция PID процессов

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

С появлением механизма namespaces стала возможной поддержка нескольких деревьев процессов, полностью изолированных друг от друга.

При загрузке в Linux сначала запускается процесс с идентификационным номером (PID) 1. В дереве процессов он является корневым. Он запускает другие процессы и службы. Механизм namespaces позволяет создавать отдельное ответвление дерева процессов с собственным PID 1. Процесс, который создаёт такое ответвление, являются частью основного дерева, но его дочерний процесс уже будет корневым в новом дереве.

Процессы в новом дереве никак не взаимодействуют с родительским процессом и даже не «видят» его. В то же время процессам в основном дереве доступны все процессы дочернего дерева. Наглядно это показано на следующей схеме:

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Можно создавать несколько вложенных пространств имён PID: один процесс запускает дочерний процесс в новом пространстве имён PID, a тот в свою очередь порождает новый процесс в новом пространстве и т.п.

Один и тот же процесс может иметь несколько идентификаторов PID (отдельный идентификатор для отдельного пространства имён).

Для создания новых пространств имён PID используется системный вызов clone() c флагом CLONE_NEWPID. С помощью этого флага можно запускать новый процесс в новом пространстве имён и в новом дереве. Рассмотрим в качестве примере небольшую программу на языке C (здесь и далее примеры кода взяты отсюда и незначительно нами изменены):

Скомпилируем и запустим эту программу. По завершении её выполнения мы увидим следующий вывод:

Во время выполнения такой маленькой программы в системе произошло много интересного. Функция clone() создала новый процесс, клонировав текущий, и начала его выполнение. При этом она отделила новый процесс от основного дерева и создала для него отдельное дерево процессов.

Попробуeм теперь изменить код программы и узнать родительский PID с точки зрения изолированного процесса:

Вывод изменённой программы будет выглядет так:

Строка «Родительский PID: 0» означает, что у рассматриваемого нами процесса родительского процесса нет. Внесём в программу ещё одно изменение и уберём флаг CLONE_NEWPID из вызова clone():

Системный вызов clone в этом случае сработал практически так же, как fork() и просто создал новый процесс. Между fork() и clone(), однако, есть существенное отличие, которое следует разобрать детально.

Fork() создаёт дочерний процесс, который представляет копию родительского. Родительский процесс копируется вместе со всем контекстом исполнения: выделенной памятью, открытыми файлами и т.п.

В отличие от fork() вызов clone() не просто создаёт копию, но позволяет разделять элементы контекста выполнения между дочерним и родительским процессами. В приведённом выше примере кода с функцией clone используется аргумент child_stack, который задаёт положение стека для дочернего процесса. Как только дочерний и родительский процессы могут разделять память, дочерний процесс не может выполняться в том же стеке, что и родительский. Поэтому родительский процесс должен установить пространство памяти для дочернего и передать указатель на него в вызове clone(). Ещё один аргумент, используемый с функцией clone() — это флаги, которые указывают, что именно нужно разделять между родительским и дочерним процессами. В приведённом нами примере использован флаг CLONE_NEWPID, который указывает, что дочерний процесс должен быть создан в новом пространстве имён PID. Примеры использования других флагов будут приведены ниже.

Итак, изоляцию на уровне процессов мы рассмотрели. Но это — всего лишь первый шаг. Запущенный в отдельном пространстве имён процесс все равно будет иметь доступ ко всем системным ресурсам. Если такой процесс будет слушать, например, 80-й порт, это этот порт будет заблокирован для всех остальных процессов. Избежать таких ситуаций помогают другие пространства имён.

NET: изоляция сетей

Благодаря пространству имён NET мы можем выделять для изолированных процессов собственные сeтевые интерфейсы. Даже loopback-интерфейс для каждого пространства имён будет отдельным.

Сетевые пространства имён можно создавать с помощью системного вызова clone() с флагом CLONE_NEWNET. Также это можно сделать с помощью iproute2:

Воспользуемся strace и посмотрим, что произошло в системе во время приведённой команды:

Обратим внимание: здесь для создания нового пространстве имён использован системный вызов unshare(), а не уже знакомый нам clone. Unshare() позволяет процессу или треду отделять части контекста исполнения, общие с другими процессами (или тредами).

Как можно помещать процессы в новое сетевое пространство имён?

Во-первых, процесс, создавший новое пространство имён, может порождать другие процессы, и каждый из этих процессов будет наследовать сетевое пространство имён родителя.

Во-вторых, в ядре имеется специальный системный вызов — setns(). С его помощью можно поместить вызывающий процесс или тред в нужное пространство имён. Для этого требуется файловый дескриптор, который на это пространство имён ссылается. Он хранится в файле /proc/

/ns/net. Открыв этот файл, мы можем передать файловый дескриптор функции setns().

Можно пойти и другим путём. При создании нового пространства имён с помощью команды ip создаётся файл в директории /var/run/netns/ (см. в выводе трассировки выше). Чтобы получить файловый дескриптор, достаточно просто открыть этот файл.

Сетевое пространство имён нельзя удалить при помощи какого-либо системного вызова. Оно будет существовать, пока его использует хотя бы один процесс.

MOUNT: изоляция файловой системы

Об изоляции на уровне файловой системы мы уже упоминали выше, когда разбирали системный вызов chroot (). Мы отметили, что системный вызов chroot() не обеспечивает надёжной изоляции. С помощью же пространств имён MOUNT можно создавать полностью независимые файловые системы, ассоциируемые с различными процессами:

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Для изоляции файловой системы используется системный вызов clone() c флагом CLONE_NEWNS:

Сначала дочерний процесс «видит» те же точки монтирования, что и родительский. Как только дочерний процесс перенесён в отдельное пространство имён, к нему можно примонтировать любую файловую системы, и это никак не затронет ни родительский процесс, ни другие пространства имён.

Другие пространства имён

Изолированный процесс также может быть помещён в другие пространства имён: UID, IPC и PTS. UID позволяет процессу получать привилегии root в пределах определённого пространства имён. С помощью пространства имён IPC можно изолировать ресурсы для коммуникации между процессами.

UTS используется для изоляции системных идентификаторов: имени узла (nodename) и имени домена (domainame), возвращаемых системным вызовом uname(). Рассмотрим ещё одну небольшую программу:

Вывод этой программы будет выглядеть так:

Как видим, функция child_fn() выводит имя узла, изменяет его, а затем выводит уже новое имя. Изменение происходит только внутри нового пространства имён.

Заключение

В этой статье мы в общих чертах рассмотрели, как работает механизм namespaces. Надеемся, она поможет вам лучше понять принципы работы контейнеров. По традиции приводим ссылки на интересные дополнительные материалы:

Если вы по тем или иным причинам не можете оставлять комментарии здесь — приглашаем в наш блог.

Источник

Глубокое погружение в Linux namespaces

В этой серии постов мы внимательно рассмотрим один из главных ингредиентов в контейнере – namespaces. В процессе мы создадим более простой клон команды docker run – нашу собственную программу, которая будет принимать на входе команду (вместе с её аргументами, если таковые имеются) и разворачивать контейнер для её выполнения, изолированный от остальной системы, подобно тому, как вы бы выполнили docker run для запуска из образа.

Что такое namespace?

Linux namespace – это абстракция над ресурсами в операционной системе. Мы можем думать об namespace, как о ящике. В этом ящике находятся системные ресурсы, которые точно зависят от типа ящика (namespace). В настоящее время существует семь типов пространств имён (namespaces): Cgroups, IPC, Network, Mount, PID, User, UTS.

Пространства имён (namespaces) – не какая-то дополнительная фича или библиотека, которую вам нужно установить, например, с помощью пакетного менеджера apt. Они предоставляются самим ядром Linux и уже являются необходимостью для запуска любого процесса в системе. В любой данный момент времени любой процесс P принадлежит ровно одному экземпляру namespace каждого типа. Поэтому, когда ему требуется сказать «обнови таблицу маршрутизации в системе», Linux показывает ему копию таблицы маршрутизации namespace, к которому он принадлежит в этот момент.

Для чего это нужно?

Обратите внимание, что содержащийся в namespace ресурс не обязательно представляет собой уникальную копию. В ряде случаев, возникших намеренно или ввиду бреши в безопасности, два и более namespaces будут содержать одну и ту же копию, например одного и того же файла. Таким образом, изменения, внесенные в этот файл в одном Mount namespace, фактически будут видны во всех других Mount namespaces, которые также ссылаются на него. Поэтому мы тут откажемся от нашей аналогии с ящиком, поскольку предмет не может одновременно находиться в двух разных ящиках.

Ограничение — это забота

Вы можете открыть другой терминал, выполнить ту же команду и это должно дать вам тот же результат. Это потому, что как мы упоминали ранее, процесс обязательно должен принадлежать некоторому пространству имён (namespace) и до тех пор, пока мы мы явно не зададим к какому, Linux добавляет его в namespaces по умолчанию.

Давайте немного вмешаемся в это. Во втором терминале мы можем выполнить что-то вроде этого:

Одним из следствий того, что мы только что проделали, является то, что теперь мы можем изменить системный hostname из нашего нового процесса bash и это не повлияет ни на какой другой процесс в системе. Вы можете проверить это, выполнив hostname в первом терминале и увидев, что имя хоста там не изменилось.

Но что, например, такое контейнер?

Надеюсь, теперь у вас есть некоторое представление о том, что может делать namespace. Вы можете предположить, что контейнеры по своей сути — обыкновенные процессы с отличающимися от других процессов namespaces, и вы будете правы. Фактически это квота. Контейнер без квот не обязан принадлежать уникальному namespace каждого типа — он может совместно использовать некоторые из них.

Изолирование

В зависимости от области применения, мы сфокусируемся на User, Mount, PID и Network namespaces. Остальные же будут относительно тривиальны для реализации после того, как мы закончим (фактически, мы добавим поддержку UTS здесь в первичной реализации программы). А рассмотрение, например, Cgroups, выходит за рамки этой серии (изучение cgroups — другого компонента контейнеров, используемого для управления тем, сколько ресурсов может использовать процесс).

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

Реализация

Эта комбинация создания-настройки namespace потребует некоторого взаимодействия между основным процессом isolate и дочерним процессом запускаемой команды. В результате часть основной работы здесь будет заключаться в настройке связующего канала между обоими процессами — в нашем случае мы будем использовать Linux pipe из-за его простоты.

Нам нужно сделать три вещи:

Вот основной процесс:

Далее процесс команды ожидает сигнала перед её запуском:

Наконец, мы может попробовать это запустить:

Сейчас isolate — это немногим больше, чем программа, которая просто форкает команду (у нас есть UTS, работающий для нас). В следующем посте мы сделаем еще один шаг, рассмотрев User namespaces заставим isolate выполнять команду в собственном User namespace. Там мы увидим, что на самом деле надо проделать некоторую работу, чтобы иметь пригодный к использованию namespace, в котором может выполняться команда.

Источник

Глубокое погружение в Linux namespaces, часть 2

В предыдущей части мы только окунули пальцы ног в воды namespace и при этом увидели, как это было просто — запустить процесс в изолированном UTS namespace. В этом посте мы осветим User namespace.

Среди прочих ресурсов, связанных с безопасностью, User namespaces изолирует идентификаторы пользователей и групп в системе. В этом посте мы сосредоточимся исключительно на ресурсах user и group ID (UID и GID соответственно), поскольку они играют фундаментальную роль в проведении проверок разрешений и других действий во всей системе, связанных с безопасностью.

В Linux эти ID — просто целые числа, которые идентифицируют пользователей и группы в системе. И каждому процессу назначаются какие-то из них, чтобы задать к каким операциями/ресурсам этот процесс может и не может получить доступ. Способность процесса нанести ущерб зависит от разрешений, связанных с назначенными ID.

User Namespaces

Мы проиллюстрируем возможности user namespaces, используя только пользовательские ID. Точно такие же действия применимы к групповым ID, к которым мы обратимся далее в этому посте.

User spaces могут быть вложенными! Это означает, что экземпляр пользовательского namespace (родительский) может иметь ноль и больше дочерних пространств имён, и каждое дочернее пространство имён может, в свою очередь, иметь свои собственные дочерние пространства имён и так далее… (до достижения предела в 32 уровня вложенности). Когда создаётся новый namespace C, Linux устанавливает текущий User namespace процесса P, создающего C, как родительский для C и это не может быть изменено впоследствии. В результате все user namespaces имеют ровно одного родителя, образуя древовидную структуру пространств имён. И, как и в случае с деревьями, исключение из этого правила находится наверху, где у нас есть корневой (или начальный, дефолтный) namespace. Это, если вы еще не делаете какую-то контейнерную магию, скорее всего user namespace, к которому принадлежат все ваши процессы, поскольку это единственный user namespace с момента запуска системы.

В этом посте мы будем использовать приглашения командной строки P$ и C$ для обозначения шела, который в настоящее время работает в родительском P и дочернем C user namespace соответственно.

Маппинги User ID

User namespace, по сути, содержит набор идентификаторов и некоторую информацию, связывающую эти ID с набором ID других user namespace — этот дуэт определяет полное представление о ID процессов, доступных в системе. Давайте посмотрим, как это может выглядеть:

Информация, связывающая UID из одного user namespace с другим, называется маппингом user ID. Он представляет из себя таблицы поиска соответствия ID в текущем user namespace для ID в других namespace и каждый user namespace связан ровно одним маппингом UID (в дополнение еще к одному маппингу GID для group ID).

Map-файлы

Давайте это попробуем:

Хорошо, это было не очень захватывающе, так как это были два крайних случая, но это говорит там о нескольких вещах:

Написание UID Map файлов

Чтобы исправить наш вновь созданный user namespace C, нам просто нужно предоставить наши нужные маппинги, записав их содержимое в map-файлы для любого процесса, который принадлежит C (мы не можем обновить этот файл после записи в него). Запись в этот файл говорит Linux две вещи:

Например, если мы из родительского user namespace P запишем следующее в map-файл для дочернего пространства имён C:

мы по существу говорим Linux, что:

Владелец пространств имён и привилегии

В предыдущем посте мы упомянули, что при создании новых пространств имён требуется доступ с уровнем суперпользователя. User namespaces не налагают этого требования. На самом деле, еще одной их особенностью является то, что они могут владеть другими пространствами имён.

Владелец пространств имён важен потому, что процесс, запрашивающий выполнения привилегированного действия над ресурсом, задействованным не user namespace, будет иметь свои UID привилегии, проверенные в отношении владельца этого user namespace, а не корневого user namespace. Например, скажем, что P является родительским user namespace дочернего C, а P и C владеют собственными network namespace M и N соответственно. Процесс может не иметь привилегий для создания сетевых устройств, включенных в M, но может быть в состоянии это делать для N.

К сожалению, мы повторно применим требование прав суперпользователя в следующем посте, так как isolate нуждается в привилегиях root в корневом user namespace, чтобы корректно настроить Mount и Network namespace. Но мы обязательно отбросим привилегии командного процесса, чтобы убедиться, что команда не имеет ненужных разрешений.

Как разрешаются ID

Групповые ID

Реализация

Как вы можете видеть, есть много сложностей, связанных с управлением user namespaces, но реализация довольно проста. Всё, что нам нужно сделать, это написать кучу строк в файл — муторно было узнать, что и где писать. Без дальнейших церемоний, вот наши цели:

И вызовем его из основного процесса в родительском user namespace прямо перед тем, как мы подадим сигнал командному процессу.

И это всё! isolate теперь запускает процесс в изолированном user namespace.

В этом посте было довольно много подробностей о том, как работают User namespaces, но в конце концов настройка экземпляра была относительно безболезненной. В следующем посте мы рассмотрим возможность запуска команды в своём собственном Mount namespace с помощью isolate (раскрывая тайну, стоящую за инструкцией FROM из Dockerfile ). Там нам потребуется немного больше помочь Linux, чтобы правильно настроить инстанс.

Источник

Углубленное знакомство с пространствами имен Linux. Часть 1

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

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

Что такое пространства имен?

Пространства имен – это одна из особенностей ядра Linux, введенная в версии 2.6.24 в 2008 году. Они обеспечивают процессы собственным системным представлением, тем самым изолируя независимые процессы друг от друга. Другими словами, пространства имен определяют набор ресурсов, которые может использовать процесс (нельзя взаимодействовать с тем, что не видно). На высоком уровне они позволяют тонко разделять глобальные ресурсы операционной системы, такие как точки монтирования, сетевой стек и утилиты межпроцессного взаимодействия.

Сильная сторона пространств имен в том, что они ограничивают доступ к системным ресурсам без информирования об этом выполняющегося процесса. В Linux они обычно представлены как файлы в директории /proc/

При порождении нового процесса, он наследует все пространства имен от своего родителя.

Зачем использовать пространства имен?

Если представить, что пространства имен – это ящики для процессов, содержащие некоторые абстрагированные системные ресурсы, то удобной особенностью таких ящиков будет возможность добавлять и удалять их содержимое, не влияя на содержимое других ящиков. Либо, если изолированный процесс А вдруг обезумит и решит удалить всю файловую систему или сетевой стек в своем ящике (наборе пространств имен), то подобное действие не повлияет на абстракцию этих ресурсов, предоставленную для процесса В, находящегося в другом ящике.

Более того, пространства имен могут обеспечивать еще более тонкую изоляцию, позволяя процессам А и В разделять некоторые ресурсы (например, совместно использовать точку монтирования или сетевой стек). Зачастую пространства имен используются, когда на машине нужно выполнить недоверенный код, не подвергая рискам ее ОС.

Платформы соревновательного программирования, такие как Hackerrank, Codeforces и Rootme, используют среды, помещенные в пространства имен, для выполнения нескольких пользовательских служб (например, веб-серверов или баз данных) на одном оборудовании без возможности их коллизий.

Так что, пространства имен также могут оказаться полезны для эффективной реализации общего доступа к ресурсам. Другие облачные технологии вроде Docker или LXC тоже используют пространства имен для изоляции процессов. Эти платформы помещают процессы операционной системы в изолированные среды, называемые контейнерами. Выполнение процессов, к примеру, в контейнерах Docker, подобно их выполнению на виртуальных машинах.

Разница же между VM и контейнером в том, что контейнеры разделяют и используют непосредственно ядро ОС, что делает их существенно легче виртуальных машин ввиду отсутствия аппаратной эмуляции. Такое повышение общей производительности в основном обеспечено использованием пространств имен, интегрированных непосредственно в ядро Linux. Но при этом существует и ряд реализаций облегченных виртуальных машин.

Типы пространств имен

В текущей стабильной версии ядра 5.7 есть семь пространств имен:

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

Пространство имен PID

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

При загрузке системы в большинстве современных ОС Linux изначально запускается systemd (системный демон), расположенный в корневом узле дерева. Его родителем выступает PID=0 – по сути, не существующий процесс ОС. После старта демон отвечает за запуск других служб/демонов, которые представлены в качестве его потомков и необходимы для нормального функционирования операционной системы. Эти процессы будут иметь PID > 1, при этом сами PID в структуре дерева являются уникальными.

С введением пространства имен PID появилась возможность создавать вложенные деревья процессов. В результате не только systemd (PID=1), но и другие процессы могут воспринимать себя как корневые, перемещаясь в вершину поддерева и получая в нем PID=1.

Все процессы этого же поддерева тоже будут получать ID относительно данного пространства имен PID. Это также означает, что некоторые процессы могут в итоге иметь несколько ID в зависимости от количества пространств имен процессов, в которых они находятся. Тем не менее в каждом пространстве имен только один процесс может иметь конкретное значение PID (уникальное значение узла в дереве процессов становится свойством, относящимся к конкретному пространству имен).

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

Из этого следует, что процесс в пространстве имен вложенного процесса не может взаимодействовать со своим родителем или любым другим процессом в пространстве имен вышестоящего процесса. Причина в том, что, находясь на вершине нового пространства имен PID, процесс воспринимает свой PID как 1, а до процесса с PID=1 других процессов нет.

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Схема оформлена Махмудом Ридваном из Toptal

Вместо этого нужно проинструктировать программу unshare на ответвление нового процесса после создания пространства имен. Тогда этот новый процесс получит PID=1 и выполнит нашу программу оболочки. В таком случае, когда под-процессы /bin/bash будут завершаться, пространство имен будет по-прежнему хранить процесс с PID=1.

Избежать этого можно двумя способами:

Как уже говорилось, процесс может иметь несколько ID в зависимости от количества пространств имен, в которых он находится. Далее мы разберем эти разные PID оболочки, вложенные в два пространства имен.

Хорошо, рассмотрев виртуализацию в виде идентификаторов, теперь посмотрим, есть ли реальная изоляция в плане взаимодействия с другими процессами ОС.

Мы видим, что процесс не смог взаимодействовать с процессом вне его текущего пространства имен (нельзя взаимодействовать с тем, что не видно, помните?).

Подытожим тему пространств имен процессов:

Пространство имен NET

Сетевое пространство имен ограничивает видимость процесса внутри сети. Оно позволяет процессу располагать собственной частью сетевого стека хоста (набором сетевых интерфейсов, хуков Netfilter и правилами маршрутизации). Разберем на примере:

А теперь создадим новое сетевое пространство имен и рассмотрим его сетевой стек.

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

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

Связывание двух пространств имен

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Схема представлена Махмудом Ридваном из Toptal

Далее мы построим функциональную сеть между разными сетевыми пространствами имен. Здесь важно отметить, что они бывают двух типов – именованные и анонимные, но эту тему мы подробно разбирать не станем. Сначала мы создадим сетевое пространство имен, а затем пару виртуальных интерфейсов:

Теперь нужно протестировать соединение наших виртуальных интерфейсов.

Из фрагмента выше видно, как создается новое сетевое пространство имен и подключается к корневому пространству через соединение, подобное конвейеру. Родительское пространство имен один интерфейс оставило себе, а второй передало дочернему пространству имен. Теперь, по аналогии с реальным сетевым соединением, все, что входит в один интерфейс, передается в другой.

Linux namespace что это. Смотреть фото Linux namespace что это. Смотреть картинку Linux namespace что это. Картинка про Linux namespace что это. Фото Linux namespace что это

Вот мы и разобрались, как изолировать, виртуализировать и соединять сетевые стеки в Linux. Имея подобную возможность виртуализации, логичным следующим шагом будет создание виртуальной LAN между процессами.

Соединение нескольких пространств имен (создание LAN)

Для создания виртуальной LAN мы используем другую утилиту виртуализации – мост. Мост в Linux действует подобно реальному сетевому коммутатору второго уровня – перенаправляет пакеты между подключенными к нему интерфейсами, используя MAC-таблицу.

Перейдем к созданию нашей виртуальной LAN:

Все работает! Здесь важно то, что виртуальный интерфейс должен выдавать разрешения на перенаправление пакетов в текущем сетевом стеке. Для избежания заморочек с правилами iptables можно повторить эту процедуру в отдельном сетевом пространстве имен, где таблица правил будет пуста по умолчанию. Теперь давайте подключим нашу LAN к интернету!

Связь с внешним миром

Мы присвоили нашему мосту IP и теперь можем пинговать его из сетевых пространств имен.

Обратите внимание, что правила iptables хоста должны быть грамотно настроены, так как неудачная конфигурация может привести к проблемам. Кроме того, без маскарадинга пакеты будут покидать хост с его внутренним IP-адресом, который известен только самому хосту, шлюз его LAN не знает, как присоединиться к сетевому мосту.

Подытожим о сетевых пространствах имен:

Сделаем перерыв

В этой статье мы разобрали несколько типов пространств имен. С остальными же мы познакомимся во второй части:

Источник

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *