DevSecOps
Big Data
Cloud Migration
High Load
Infrastructure audit
Performance Testing
Web 3.0
DevSecOps
Big Data
Cloud Migration
High Load
Infrastructure audit
Performance Testing
Web 3.0
DevSecOps
Big Data
Cloud Migration
High Load
Infrastructure audit
Performance Testing
Web 3.0
DevSecOps
Big Data
Cloud Migration
High Load
Infrastructure audit
Performance Testing
Web 3.0
Red Hat
Kubernetes
Sonatype
GitLab
SonarSource
Jfrog
Atlassian
Suse
Red Hat
Kubernetes
Sonatype
GitLab
SonarSource
Jfrog
Atlassian
Suse
Red Hat
Kubernetes
Sonatype
GitLab
SonarSource
Jfrog
Atlassian
Suse
Red Hat
Kubernetes
Sonatype
GitLab
SonarSource
Jfrog
Atlassian
Suse
DevOpsDays Almaty
DevOpsDays Almaty
DevOpsDays Almaty
DevOpsDays Almaty
О нас
Контакты
Блог
Инвестиции для МСБ
Старт карьеры
О нас
Контакты
Блог
Инвестиции для МСБ
Старт карьеры
Contacts and details
About us
Contacts and details
About us

Основы безопасности контейнеров или восприятие контейнеров как процессы

Благодаря контейнерам не нужно думать, что происходит под капотом. Docker и Kubernetes отлично скрывают сложности от своих пользователей.

Однако, когда нужно отладить и обезопасить контейнеризированные среды, полезно взаимодействовать с контейнерами на низком уровне. Вы можете использовать инструменты Linux для взаимодействия с ними и их отладки.

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


Здесь приведены некоторые практические идеи по безопасности контейнерных сред и устранению неполадок. Сосредоточимся на стандартных контейнерах в стиле Docker, но это также применимо к другим средам, таким как Podman, containerd и CRI-O.

Контейнеры — всего лишь процессы

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


Давайте начнем с проверки, есть ли на нашей виртуальной машине какие-либо активные процессы с именем nginx:


ps -fC nginx

Это должно вернуть пустой список, так как на данный момент у нас не запущено ни одного веб-сервера NGINX.


Теперь давайте запустим контейнер Docker, используя образ nginx из Docker Hub.


docker run -d nginx:1.23.1

Как только контейнер будет загружен, мы снова запустим нашу команду ps:


ps -fC nginx

На этот раз мы получаем обратно что-то вроде примера ниже. Это показывает нам, что на компьютере сейчас запущено несколько процессов NGINX. Что касается нашей Linux-машины, то кто-то только что запустил NGINX на хосте.

Одним из первых вопросов может быть: как вы определяете разницу между сервером NGINX из образа Docker, и сервером, который только что был установлен на виртуальной машине? Есть несколько способов сделать это, но самый простой — проверить наличие запущенных контейнеров с помощью docker ps:

В качестве альтернативы мы могли бы использовать инструменты обработки Linux, чтобы определить, запущен ли веб-сервер как контейнер.

Параметр ---forest в ps (например, ps -ef --forest) позволяет нам увидеть иерархию процессов. В этом случае наши процессы NGINX имеют родительский процесс containerd-shim-runc-v2. Вы должны увидеть процесс shim для каждого контейнера, запущенного на хосте. Этот процесс shim является частью контейнера и используется Docker для управления содержащимися в нем процессами. Цель этого процесса shim состоит в том, чтобы разрешить перезапуск containerd или Docker без необходимости перезапускать все контейнерына хосте.

Взаимодействие с контейнером как с процессом

Теперь мы знаем, что контейнеры — это всего лишь процессы, но что это означает с точки зрения того, как мы можем с ними взаимодействовать?


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


Файловая система /proc в Linux — это виртуальная или псевдофайловая система. Она не содержит реальных файлов — вместо этого заполнена информацией о запущенной системе. Пока у пользователя соответствующие привилегии на хосте, на котором запущен Docker, он может использовать /proc для доступа к информации о любом контейнере.


Давайте рассмотрим некоторую информацию о контейнере NGINX, который мы запустили ранее. В тестовой системе мы видим, что идентификатор процесса nginx равен 2336. Если мы перечислим файлы в /proc, то увидим пронумерованный каталог для каждого процесса на хосте, включая наш процесс NGINX. Каждый из этих каталогов содержит множество файлов и директорий с информацией об этом процессе, что означает, что мы можем перейти в каталог 2336, чтобы узнать больше о нашем отдельном процессе.

Также полезно использовать инструменты Linux для работы с контейнерами, которые были защищены от удаления таких инструментов, как файловые редакторы или мониторы процессов. Усиление образов контейнеров является распространенной рекомендацией по безопасности, но это усложняет отладку. Вы можете редактировать файлы внутри контейнера, обратившись к корневой файловой системе контейнера из каталога /proc на хосте. Переход к /proc/[PID]/root предоставит вам список каталогов содержащегося процесса, который имеет этот PID.


В этом случае запуск sudo ls /proc/2336/root покажет нам список, который выглядит так:

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

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


В качестве примера давайте уничтожим контейнер NGINX с помощью команды sudo kill 2336. Затем мы можем запустить docker ps, чтобы подтвердить, что контейнера больше нет.

Что это значит для безопасности?

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


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


Пользователь с доступом к базовому хосту может прочитать содержимое файла environ внутри /proc, чтобы просмотреть эту информацию, как показано ниже — в примере, где контейнер запущен с переменной "TOP_SECRET=API_KEY".

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

Заключение

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

Если у вас есть вопросы или нужна помощь в использовании Docker — обращайтесь к нам, мы всегда готовы помочь! Можем начать, например, с аудита инфраструктуры. Ну, или выбирайте ту услугу, которая более актуальна