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

Опубликовано: 2022-08-19

Графика с логотипом Docker

Процессы в контейнере Docker не должны запускаться с правами root. Безопаснее запускать ваши приложения от имени пользователя без полномочий root, которого вы указываете как часть своего Dockerfile или при использовании docker run . Это минимизирует риск, предоставляя уменьшенную поверхность атаки для любых угроз в вашем контейнере.

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

Почему запуск с правами root опасен?

По умолчанию контейнеры запускаются от имени root. Демон Docker выполняется от имени пользователя root на вашем хосте, и запущенные контейнеры также будут иметь права root.

Хотя может показаться, что root внутри контейнера является независимым пользователем, на самом деле это то же самое, что и учетная запись root на вашем хосте. Разделение обеспечивается только механизмами изоляции контейнеров Docker. Нет сильной физической границы; другой процесс вашего контейнера, запущенный пользователем root в ядре вашего хоста. Это означает, что уязвимость в вашем приложении, среде выполнения Docker или ядре Linux может позволить злоумышленникам выйти из контейнера и выполнить операции с привилегиями root на вашем компьютере.

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

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

Запуск контейнерных приложений от имени пользователя без полномочий root

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

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

 ОТ базового образа: последний
ЗАПУСТИТЬ apt установить демонстрационный пакет
ПОЛЬЗОВАТЕЛЬ демо-пользователь:демо-группа
ENTRYPOINT ["демо-бинарник"]

Контейнеры, запущенные из этого образа, будут работать как demo-user . Пользователь будет членом группы demo-group group. Вы можете опустить имя группы, если вам не нужно, чтобы пользователь был в группе:

 USER демо-пользователь

Вы можете указать идентификатор пользователя (UID) и идентификатор группы (GID) вместо имен:

 ПОЛЬЗОВАТЕЛЬ 950:950

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

USER часто указывается как предпоследний этап в Dockerfile. Это означает, что вы по-прежнему можете запускать операции, требующие root, на более ранних этапах сборки образа. Инструкция apt install в приведенном выше примере имеет законную потребность в root. Если бы инструкция USER была размещена над ней, apt запустился бы как demo-user , у которого не было бы необходимых разрешений. Поскольку инструкции Dockerfile применимы только к сборкам образов, а не к запуску контейнеров, безопасно отложить изменение пользователя на более позднее время в вашем Dockerfile.

Изменение пользователя, под которым работает ваш контейнер, может потребовать обновления разрешений для файлов и папок, к которым он обращается. Установите права собственности на любые пути, которые будут использоваться вашим приложением:

 КОПИРОВАТЬ начальный-config.yaml /data/config.yaml

ПОЛЬЗОВАТЕЛЬ демо-пользователь:демо-группа
RUN chown demo-user:demo-group/data

В этом примере каталог /data должен принадлежать demo-user , чтобы приложение могло вносить изменения в свой файл конфигурации. Предыдущий оператор COPY скопировал файл как root. Сокращение доступно при использовании флага --chown с copy :

 КОПИРОВАТЬ --chown=demo-user:demo-group initial-config.yaml /data/config.yaml

Смена пользователя при запуске контейнера

Хотя вы можете легко изменить пользователя в своих собственных файлах Dockerfile, многие сторонние приложения продолжают работать с правами root. Вы можете уменьшить риск, связанный с их использованием, устанавливая флаг --user каждый раз, когда вы вызываете docker run . Это переопределяет пользовательский набор в Dockerfile образа.

 $ docker run -d --user demo-user:demo-group demo-image:latest
$ docker run -d --user demo-user demo-image:последний
$ docker run -d --user 950:950 демонстрационный образ:последний

Флаг --user запускает процесс контейнера от имени указанного пользователя. Это менее безопасно, чем инструкция Dockerfile USER , потому что вам нужно применять ее индивидуально к каждой команде docker run . Лучшим вариантом для регулярно используемых изображений является создание собственного производного изображения, которое может установить новую учетную запись пользователя:

 ИЗ образа, работающего от имени root: последний
USER демо-пользователь
 $ сборка докера. -t образ-который-теперь-работает-без-рута:последний

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

Работа с приложениями, которые должны запускаться от имени root

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

Переназначение пространства имен активируется добавлением userns-remap в ваш файл /etc/docker/daemon.json :

 {
    "userns-remap": "по умолчанию"
}

Использование default в качестве значения для userns-remap указывает Docker автоматически создать нового пользователя на вашем хосте с именем dockremap . Корневой каталог внутри контейнеров будет отображаться обратно в dockremap на вашем хосте. Вместо этого вы можете дополнительно указать существующего пользователя и группу, используя комбинацию UID/GID или имя пользователя/имя группы:

 {
    "userns-remap": "демо-пользователь"
}

Перезапустите демон Docker после применения изменений:

 $ sudo service docker перезапустить

Если вы используете nsuser-remap: default , пользователь dockremap теперь должен существовать на вашем хосте:

 $ ID докремап

uid=140(dockremap) gid=119(dockremap) groups=119(dockremap)

Пользователь также должен появиться в файлах идентификаторов подчиненных /etc/subuid и /etc/subgid :

 $ докремап: 231500: 65535

Пользователю был выделен диапазон из 65 535 подчиненных идентификаторов, начиная с 231500. В пространстве имен пользователя идентификатор 231500 сопоставляется с 0 , что делает его пользователем root в ваших контейнерах. Будучи UID с большим номером, 231500 не имеет привилегий на хосте, поэтому атаки с выходом из контейнера не смогут нанести такой большой ущерб.

Все контейнеры, которые вы запускаете, будут запускаться с использованием переназначенного пользовательского пространства имен, если вы не откажетесь от этого с помощью docker run --userns=host . Механизм работает путем создания каталогов с пространством имен внутри /var/lib/docker , которые принадлежат подчиненному UID и GID пользователя с пространством имен:

 $ sudo ls -l /var/lib/docker/231500.231500

всего 14
drwx------ 5 231500 231500 13 июл 22 19:00 доп.
drwx------ 3 231500 231500 13 июл 22 19:00 контейнеры
...

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

Резюме

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

Как автор изображения, вы должны включить инструкцию USER в свой Dockerfile, чтобы ваше приложение работало без root. Пользователи образа могут переопределить это с помощью docker run --user , чтобы назначить определенный UID и GID. Это помогает смягчить случаи, когда образ обычно использует root.

Вы можете еще больше усилить безопасность, удалив все возможности из контейнера, используя --cap-drop=ALL , а затем добавив в белый список те, которые требуются, с --cap-add . Сочетание этих методов запустит ваше приложение от имени пользователя без полномочий root с минимальным набором необходимых ему привилегий, что улучшит вашу безопасность.