Доклады, выступления, видео и электронные публикации

Программные механизмы изоляции контейнеров DOCKER

1. Введение

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

Технология контейнерной виртуализации набирает популярность: по прогнозам Gartner, к 2022 году более 75% организаций всего мира будут использовать контейнеризированные приложения в рабочей среде, в то время как сейчас это число оценивается менее чем 30% [1]. Важную роль в популяризации данной технологии сыграл появившийся в 2013 году инструмент Docker. Контейнеризация имеет ряд преимуществ: кроссплатформенность, эффективность использования ресурсов компьютера, высокая скорость доставки обновлений [2]. Однако несмотря на достоинства технологии контейнерной виртуализации, ее внедрение осложняется проблемами, связанными с безопасностью [3]. При решении этих проблем большое внимание уделяется вопросу изоляции контейнеров, потому что высокий уровень изоляции не только ограничивает вектор атаки на систему, но и минимизирует последствия компрометации контейнера. Рассмотрим программные средства изоляции контейнеров в Linux-системах на материале инструмента Docker.

2. Обзор технологии Docker

Рассмотрим основы технологии Docker. Главным функциональным компонентом Docker является Docker Engine. Он включает в себя [4]:

  1. Docker-демон — сервис, запущенный с правами суперпользователя в хостовой ОС, который управляет Docker объектами (образами, контейнерами, сетью).
  2. Docker Engine API — интерфейс, который Docker-клиент и Docker-демоном используют для взаимодействия.
  3. Docker-клиент — консольная утилита, через которую пользователь производит управление Docker-демоном.

Экземпляр контейнера строится на основе образа — шаблона, который содержит как само приложение, так и все необходимые для его работы компоненты: среду исполнения, библиотеки, переменные среды, файлы конфигурации. Сборка образа производится согласно инструкциям конфигурационного файла Dockerfile. В Docker для хранения данных образа используется технология каскадно-объединённого монтирования файловых систем, благодаря чему данные образа представлены в виде слоев данных [5]. Каждый слой данных является набором отличий от слоя, находящегося перед ним. Каждая последующая команда Dockerfile, вносящая изменения в содержимое уже имеющихся слоев, при сборке образа добавляет новый слой [5]. В Docker доступны разные драйверы, реализующие каскадно-объединённое монтирование: Aufs, OverlayFS, Btrfs, ZFS.

Слои данных образа находятся в режиме «только для чтения». При инициализации контейнера поверх слоев данных образа накладывается слой, для которого разрешена запись. Все изменения, произведенные в процессе работы контейнера, такие как запись новых файлов, изменение и удаление существующих файлов, записываются на этот слой данных [5]. Так как слои данных образа не модифицируются, один образ может использоваться сразу несколькими контейнерами, что позволяет избежать дублирования данных и экономит время при запуске контейнеров. 

3. Необходимость в изоляции контейнеров Docker

При создании образа в качестве отправной точки можно использовать готовый образ, в таком случае он называется родительским. Слои данных нового образа накладываются поверх слоев родительского. В открытом хранилище DockerHub можно найти образ практически с любым известным приложением, настроенным и готовым к запуску. Однако в ходе анализа около 4 миллиона контейнеров, хранящихся в DockerHub, было обнаружено, что 51% из них содержит критические уязвимости [6]. Все уязвимости родительского образа наследуются дочерними, поэтому выбор родительского образа очень важен для вопроса безопасности.

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

4. Программные механизмы изоляции контейнеров

В данном разделе рассмотрены программные механизмы, используемые Docker для поддержания изоляции контейнеров в Linux-системах. Каждый пункт начинается с обзора конкретного механизма, после чего рассматривается, как он используется в Docker. Также дается оценка эффективности применения механизма и соображения по оптимизации.

4.1. Модули безопасности Linux

Модули безопасности Linux (англ. Linux Security Modules, LSM) позволяют реализовать нестандартные для Linux-систем модели безопасности [7]. Большинство готовых решений, построенных на модулях безопасности Linux, реализуют механизм мандатного разграничения доступа (англ. Mandatory Access Control, MAC). 

В рамках MAC каждому объекту системы присваивается метка конфиденциальности, определяющая ценность содержащейся в нем информации — его уровень секретности. А каждому субъекту системы присваивается уровень допуска, определяющий уровень доверия к нему — максимальное значение метки конфиденциальности объектов, к которым субъект имеет доступ. Механизм разграничения доступа в MAC основан на проверке соответствия метки конфиденциальности объекта и уровня допуска субъекта [8]. Управление доступом в MAC производится администратором безопасности; у пользователей нет возможности изменять правила доступа к объектам.

4.1.1. SELinux

SELinux представляет собой инструмент для осуществления мандатного разграничения доступа, построенный на LSM. Политика безопасности SELinux не заменяет стандартную модель управления доступом в Linux-системах — в первую очередь проверяются правила доступа, установленные политикой безопасности ОС, а затем уже SELinux.

Разграничение доступа в SELinux производится путем присваивания объектам и субъектам, которые необходимо контролировать, меток. В качестве субъектов рассматриваются процессы, а объектами, доступ к которым контролируется, являются элементы файловой системы, такие как файл, директория, ссылка, сокет. В качестве метки SELinux использует контекст безопасности, который представляет собой вспомогательную сущность, позволяющую абстрагироваться от системных свойств [9]. При попытке доступа субъекта к объекту SELinux принимает решение о допустимости производимого действия, основываясь на контекстах безопасности объекта и субъекта, и разрешает или блокирует выполняемое действие [10].

4.1.2. AppArmor

AppArmor — еще одна технология, реализующая механизм мандатного разграничения доступа. В отличие от SELinux, в AppArmor не используются сложные абстракции над сущностями ОС. Доступ определяется на основе профилей, которые привязаны к пути ресурса [11]. Хотя такой подход и позволяет производить более простую настройку политики безопасности, но в то же он имеет ряд уязвимостей. Политику безопасности можно обойти, если создать жесткую ссылку на охраняемый ресурс в другом месте [12]. Для AppArmor ресурс и ссылка на него будут идентифицированы как 2 разных объекта, так как они имеют разные пути, в то время как в SELinux за ресурсом сохраняется контекст безопасности, определяющий правила обращения к нему.

Еще одно отличие AppArmor от SELinux состоит в том, что для изменения политики SELinux требуется перезагрузка системы, а политики AppArmor могут загружаться/выгружаться по мере необходимости во время работы компьютера [11].

Docker совместим с каждым из рассмотренных инструментов мандатного контроля доступа. По умолчанию используется AppArmor, если он установлен ​​в хостовой ОС [13]. В отличие от SELinux, для AppArmor в Docker имеется стандартный конфигурационный профиль. Профиль SELinux может быть создан администратором безопасности вручную. Стандартный профиль AppArmor  используется по умолчанию при запуске контейнеров. Одним из правил стандартного профиля является запрет перехода по ссылкам на файлы, находящиеся в директориях хостовой ОС /etc, /dev, /sys и /proc, во время инициализации контейнера [14]. Таким образом предотвращается утечка информации, которую может спровоцировать вредоносный  Dockerfile или образ путем монтирования связанных с хостовой ОС файлов в контейнере.

Стандартный профиль AppArmor в Docker настроен таким образом, чтобы обеспечивалась совместимость с широким спектром приложений. Особенности работы конкретного контейнеризированного приложения с объектами ОС могут требовать дополнительного контроля доступа к ним, который не предусмотрен стандартным профилем AppArmor. Поэтому для обеспечения более высокого уровня защиты, необходимо настроить профиль AppArmor под особенности конкретного контейнеризированного приложения.

4.2. Контрольные группы

Контрольные группы (англ. control groups, сокр. cgroups) — функция ядра Linux, позволяющий контролировать потребление системных ресурсов процессами [15].

Управление процессами производится не напрямую, а через контрольные группы. Далее ограничение потребления ресурсов для контрольных групп достигается путем встраивания их в контроллеры (resource controllers), которые также называются подсистемами (subsystem) [15]. Каждый контроллер представляет собой отдельный тип системного ресурса, например, процессорное время или память [15]. Управление cgroups производится через виртуальную файловую систему. При этом контрольные группы для разных контроллеров организованы в иерархии, дочерние группы наследуют атрибуты родительских [15].

В приложении к Docker контрольные группы применяются для поддержания желаемого распределения системных ресурсов хостовой ОС между запущенными на ней контейнерами. Сgroups не препятствует воздействию одного контейнера на процессы и данные другого контейнера, однако применение cgroups необходимо для отражения некоторых атак типа «отказ в обслуживании» [16]. По умолчанию контейнеры не имеют ограничений на потребление ресурсов хостовой ОС [17]. Однако их необходимо задать для повышения отказоустойчивости хостовой ОС.

В Docker каталог для управления cgroups монтируется в виртуальной файловой системе хостовой ОС. Таким образом, внутри самих контейнеров нет ссылок на другие контейнеры или процессы хостовой ОС, что положительно сказывается на вопросе безопасности.

4.3. Пространства имен

Пространства имён (англ. namespaces) — механизм ядра Linux, позволяющий изолировать ресурсы ОС для экземпляров процессов [18]. Помещая процесс в пространство имен, можно ограничить ресурсы, видимые данному процессу. Namespaces впервые появились в 2002 году. В актуальной на сегодняшний день версии ядра Linux 5.11 существует восемь типов пространств имён:

  • Cgroup
  • IPC
  • Network
  • Mount
  • PID
  • Time
  • User
  • UTS

При старте ОС создается пространство имен каждого типа, они называются инициализирующими (initial) или корневыми (root). Далее в них можно создавать дочерние пространства имен и помещать в них процессы. Процесс всегда находится ровно в одном пространстве имен каждого типа.

Пространства имен IPC управляют ресурсами межпроцессного взаимодействия, а именно, объектами IPC System V и очередями сообщений POSIX. Каждое пространство имен IPC имеет свой собственный набор ресурсов. Любой объект, созданный в пространстве имен IPC, виден только процессам, находящимся в том же пространстве имен. Когда пространство имен IPC уничтожается (т. е. когда последний процесс, являющийся членом пространства имен, завершается), все связанные с ним IPC объекты также уничтожаются.

Пространства имен Network позволяют изолировать ресурсы, связанные с сетью. Каждое отдельное пространство имен Network имеет свой набор сетевых ресурсов, к ним относятся список IP-адресов, список сокетов, таблица IP-маршрутизации, брандмауэр. Каждый физический или виртуальный сетевой интерфейс может находиться только в одном пространстве имен Network. Когда пространство имен Network уничтожается (т. е. когда последний процесс, являющийся членом пространства имен, завершается), его виртуальные сетевые интерфейсы уничтожаются, а физические помещаются в корневое пространство имен Network (не в пространство имен родителя последнего процесса).

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

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

Пространства имен User изолируют идентификаторы пользователей и групп, корневой каталог, ключи и привилегии. Как и пространства имен PID, пространства имен User являются вложенными. Процесс может создать пространство имен User с помощью системных вызовов unshare() или clone() с флагом CLONE_NEWUSER. Родительским для нового пространства имен User будет считаться пространство имен процесса, его создавшего. Дочерних пространств может быть несколько.

Процесс может иметь разные идентификаторы пользователя и группы в разных пространствах имен User. Например, непривилегированный в корневом пространстве имен процесс может иметь права суперпользователя в одном из вложенных пространств. Это используется для создания непривилегированных контейнеров. Таким образом достигается, что суперпользователь внутри контейнера не имеет тех же прав в хостовой ОС.

Пространства имен UTS обеспечивают изоляцию хостовых и доменных имен. Они устанавливаются с помощью системных вызовов sethostname() и setdomainname(). Изменения этих идентификаторов видны всем процессам, находящимся в одном и том же пространстве имен UTS, но невидимы для процессов, принадлежащих разным пространствам. Значения имени хоста и имени домена NIS для нового пространства имен utc равны соответствующим полям пространства имен процесса, его создавшего.

Пространства имен Time позволяют процессам видеть разное системное время.

Пространства имен cgroup изолируют информацию, связанную с контрольными группами процессов. Этот тип пространств имен позволяет, например, скрыть структуру виртуальной файловой системы хостовой ОС для контейнеров.

С точки зрения хостовой ОС контейнер представляет собой некий процесс. Благодаря пространствам имен он имеет ограниченное представление о ресурсах хостовой ОС. Docker-демон при запуске контейнера создает для него индивидуальный набор пространств имен 6 типов: IPC, Network, Mount, PID, UTS, cgroup. В область видимости контейнера попадают только те объекты ядра хостовой ОС, которые были ему выделены или им же порождены. Так, например, контейнер видит только те процессы, которые были запущены им самим. При этом хостовая ОС видит их тоже, потому что, как отмечалось, пространства имен PID являются вложенными.

По умолчанию контейнеры запускаются с правами суперпользователя, что неизбежно приводит к увеличению поверхности атаки. Если права суперпользователя не нужны для работы контейнеризированного приложения, экземпляр контейнера имеет смысл запустить в непривилегированном режиме userns-remap [19]. При этом создается новое пространство имен User, в которое помещается контейнер, там он имеет права суперпользователя, а за его пределами в хостовой ОС — права непривилегированного пользователя.

4.4. Seccomp

Seccomp (сокр. от англ. secure computing mode) — механизм ядра Linux, позволяющий устанавливать для определенного процесса разрешенный набор системных вызовов [20]. В Seccomp реализована поддержка сложной фильтрации вызовов и их аргументов с помощью технологии BPF (сокр. от англ. Berkeley Packet Filters). Устанавливать ограничения в Seccomp можно с использованием черных и белых списков. Это два принципиально разных подхода. Черные списки блокируют те вызовы, которые находится в списке, а белые, напротив, — запрещают все вызовы, кроме тех, что находятся в списке. С точки зрения безопасности предпочтительнее использовать белые списки, поскольку они явно описывают все множество разрешенных действий и их правила гораздо строже. Однако, черные списки гораздо более просты в управлении.

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

В Docker Engine поддержка Seccomp была введена, начиная с версии 1.10. По умолчанию в Docker для всех контейнеров применяются настройки стандартного профиля Seccomp, фильтрация в котором реализована на основе белых списков [21]. В стандартных настройках запрещено 44 системных вызова, в то время как в современных 64-битных Linux-системах существует более трехсот системных вызовов. Например, к числу запрещенных относится системный вызов mount(), выполняющий монтирование файловых систем. Также запрещен вызов acct(), позволяющий процессам отключать собственные ограничения на потребление ресурсов.

Стандартные настройки профиля Seccomp в Docker не оптимизированы под задачи конкретного контейнеризированного приложения. Белый список содержит обширный набор разрешенных системных вызовов. Это, с одной стороны, ограничивает вектор атаки на систему, а с другой — может создать ложное ощущение защищенности. Многие разрешенные в стандартном профиле вызовы имеют уязвимости. Среди них вызов mmap() (CVE-2018-8781, CVE-2020-11282).

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

4.5. Linux привилегии

В Linux-системах суперпользователь имеет право на выполнение практически любых действий. Начиная с версии ядра 2.2, права суперпользователя в ядре Linux были разбиты на отдельные единицы — привилегии (англ. capabilities) [22]. Привилегии независимы друг от друга и могут быть активированы или отключены для любого процесса. Если процессу для работы требуется только некоторое подмножество привилегий суперпользователя, имеет смысл отключить остальные привилегии для повышения защищенности системы.

Инструменты контейнерной виртуализации используют привилегии, чтобы гарантировать, что запущенный контейнер имеет только необходимые ему для работы права в системе. Docker-демон по умолчанию запускает контейнеры с правами суперпользователя, для которого набор привилегий ограничен. Как и в случае с seccomp, Docker для управления привилегиями использует белые списки, что более надежно с точки зрения безопасности. Всего в стандартном списке содержится 14 разрешенных привилегий [23]. К числу разрешенных относится привилегия CAP_NET_RAW, дающая право на создание сырых (англ. raw) и пакетных (англ. packet) сокетов. Данная привилегия необходима, например, для получения и отправки ICMP пакетов. Однако согласно найденной уязвимости CVE-2020-13401, злоумышленник, получивший контроль над контейнером с привилегией CAP_NET_RAW способен успешно реализовать спуфинг-атаку или атаку типа «отказ в обслуживании». Это еще раз доказывает, что стандартные настройки Docker не могут гарантировать полной изоляции контейнеров.

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

5. Заключение

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

Для поддержания изоляции контейнеров в Linux-системах Docker использует комплекс программных средств: пространства имен, контрольные группы, Seccomp, Linux привилегии, модули безопасности Linux. Стандартные настройки, для каждого из перечисленных программных средств, заданы таким образом, чтобы обеспечивалась совместимость с широким спектром приложений. Для повышения уровня защищенности, необходимо ограничить права контейнера в хостовой ОС до минимума. Однако стоит отметить, что и в таком случае полная изоляция не гарантируется, ведь для работы контейнеризированного приложения могут требоваться права доступа, которые потенциально могут быть использованы злоумышленником. Существующие проблемы безопасности создают необходимость в подборе инструментов защиты и их настройке индивидуально под задачи конкретных контейнеризированных приложений.

Список литературы

  1. Gartner Forecasts Strong Revenue Growth for Global Container Management Software and Services Through 2024 [Электронный ресурс]. URL: https://www.gartner.com/en/newsroom/press-releases/2020-06-25-gartner-forecasts-strong-revenue-growth-for-global-co (дата обращения: 01.05.2021).
  2. Gandhi R., Szmrecsanyi P. The Benefits of Containerization and What It Means for You [Электронный ресурс]. URL: https://www.ibm.com/cloud/blog/the-benefits-of-containerization-and-what-it-means-for-you (дата обращения: 01.05.2021).
  3. Souppaya M., Morello J., Scarfone K. Application container security guide. Gaithersburg, MD: National Institute of Standards and Technology, 2017.
  4. Docker Engine overview [Электронный ресурс]. URL: https://docs.docker.com/engine/ (дата обращения: 03.05.2021).
  5. About storage drivers [Электронный ресурс]. URL: https://docs.docker.com/storage/storagedriver/ (дата обращения: 03.05.2021).
  6. Shevchenko S. Operation «Red Kangaroo»: Industry’s First Dynamic Analysis of 4M Public Docker Container Images [Электронный ресурс]. URL: https://blog.prevasio.com/2020/12/operation-red-kangaroo-industrys-first.html (дата обращения: 26.04.2021).
  7. Linux Security Modules: General Security Hooks for Linux — The Linux Kernel documentation [Электронный ресурс]. URL: https://www.kernel.org/doc/html/latest/security/lsm.html  (дата обращения: 03.05.2021).
  8. Mandatory Access Control - an overview | ScienceDirect Topics [Электронный ресурс]. URL: https://www.sciencedirect.com/topics/computer-science/mandatory-access-control (дата обращения: 03.05.2021).
  9. SELinux User’s and Administrator’s Guide: Introduction [Электронный ресурс]. URL: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/chap-security-enhanced_linux-introduction (дата обращения: 03.05.2021).
  10. SELinux User’s and Administrator’s Guide: SELinux Contexts [Электронный ресурс]. URL: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/chap-security-enhanced_linux-selinux_contexts (дата обращения: 03.05.2021).
  11. Security - AppArmor [Электронный ресурс]. URL: https://ubuntu.com/server/docs/security-apparmor (дата обращения: 03.05.2021).
  12. CWE - CWE-62: UNIX Hard Link (4.4) [Электронный ресурс]. URL: https://cwe.mitre.org/data/definitions/62.html (дата обращения: 03.05.2021).
  13. AppArmor security profiles for Docker [Электронный ресурс]. URL: https://docs.docker.com/engine/security/apparmor/ (дата обращения: 26.04.2021).
  14. Docker contrib/apparmor/template.go [Электронный ресурс] https://raw.githubusercontent.com/docker/docker/master/contrib/apparmor/template.go. (дата обращения: 26.04.2021).
  15. cgroups(7) - Linux manual page [Электронный ресурс]. URL: https://man7.org/linux/man-pages/man7/cgroups.7.html (дата обращения: 03.05.2021).
  16. Docker security [Электронный ресурс]. URL: https://docs.docker.com/engine/security/ (дата обращения: 26.04.2021).
  17. Runtime options with Memory, CPUs, and GPUs [Электронный ресурс]. URL: https://docs.docker.com/config/containers/resource_constraints/ (дата обращения: 26.04.2021).
  18. namespaces(7) - Linux manual page [Электронный ресурс]. URL: https://man7.org/linux/man-pages/man7/namespaces.7.html (дата обращения: 26.04.2021).
  19. Isolate containers with a user namespace [Электронный ресурс]. URL: https://docs.docker.com/engine/security/userns-remap/ (дата обращения: 26.04.2021).
  20. seccomp(2) - Linux manual page [Электронный ресурс]. URL: https://man7.org/linux/man-pages/man2/seccomp.2.html (дата обращения: 03.05.2021).
  21. Seccomp security profiles for Docker [Электронный ресурс]. URL: https://docs.docker.com/engine/security/seccomp/ (дата обращения: 26.04.2021).
  22. capabilities(7) - Linux manual page [Электронный ресурс]. URL: https://man7.org/linux/man-pages/man7/capabilities.7.html (дата обращения: 03.05.2021).
  23. Docker moby/moby [Электронный ресурс]. URL: https://raw.githubusercontent.com/moby/moby/master/oci/caps/defaults.go (дата обращения: 26.04.2021).

Автор: Иванова Н. В.

Дата публикации: 19.08.2021

Выходные данные: Комплексная защита информации: материалы XXVI научно-практической конференции. Минск. 25–27 мая 2021 г. Минск: Издатель Владимир Сивчиков, 2021. С. 283–287.


Scientia potestas est
Кнопка связи