Почему процессы в контейнерах Docker не должны запускаться с правами root
Процессы в контейнере Docker не должны запускаться с правами root. Безопаснее запускать приложения от имени пользователя без полномочий root, указанного вами в файле Dockerfile, или при использовании docker run
. Это минимизирует риск, предоставляя уменьшенную поверхность атаки для любых угроз в вашем контейнере.
В этой статье вы узнаете об опасностях запуска контейнерных приложений с правами root. Вы также увидите, как создать пользователя без полномочий root и настроить пространство имен в ситуациях, когда это невозможно.
Почему запуск с правами root опасен?
По умолчанию контейнеры запускаются от имени root. Демон Docker выполняется от имени пользователя root на вашем хосте, и запущенные контейнеры также будут иметь права root.
Хотя может показаться, что root внутри контейнера является независимым пользователем, на самом деле это то же самое, что и учетная запись root на вашем хосте. Разделение обеспечивается только механизмами изоляции контейнеров Docker. Нет четкой физической границы; другой процесс вашего контейнера, запущенный пользователем root в ядре вашего хоста. Это означает, что уязвимость в вашем приложении, среде выполнения Docker или ядре Linux может позволить злоумышленникам выйти из контейнера и выполнить операции с привилегиями root на вашем компьютере.
Есть некоторые встроенные средства защиты, которые уменьшают риск этого. Корень внутри контейнера является непривилегированным и имеет ограниченные возможности. Это предотвращает использование контейнером команд системного администрирования, если только вы не добавите возможности вручную или не используете привилегированный режим при запуске своих контейнеров.
Несмотря на это смягчение, запуск приложений от имени пользователя root остается опасным. Точно так же, как вы ограничиваете использование root в традиционной среде, неразумно использовать его без необходимости в ваших контейнерах. Вы предоставляете сверхпривилегированную среду, которая дает злоумышленникам больше плацдарма в случае взлома.
Запуск контейнерных приложений от имени пользователя без полномочий root
Контейнерные приложения лучше всего запускать от имени обычного пользователя. Большинству программ не требуется root-доступ, поэтому смена пользователя обеспечивает немедленный уровень защиты от взлома контейнера.
Вы должны создать новую учетную запись пользователя на одном из последних этапов в вашем Dockerfile. Этого можно добиться с помощью инструкции USER
:
FROM base-image:latest RUN apt install demo-package USER demo-user:demo-group ENTRYPOINT ["demo-binary"]
Контейнеры, запущенные из этого образа, будут работать как demo-user
. Пользователь будет членом группы demo-group
. Вы можете опустить имя группы, если вам не нужно, чтобы пользователь был в группе:
USER demo-user
Вы можете указать идентификатор пользователя (UID) и идентификатор группы (GID) вместо имен:
USER 950:950
Выделение известных UID и GID обычно является самым безопасным способом продолжения. Это предотвращает сопоставление пользователя в контейнере с учетной записью хоста с чрезмерными привилегиями.
USER
часто указывается как предпоследний этап в Dockerfile. Это означает, что вы по-прежнему можете запускать операции, требующие root, на более ранних этапах сборки образа. Инструкция apt install
в приведенном выше примере имеет законную потребность в root. Если бы над ней была размещена инструкция USER
, apt
запускался бы как demo-user
, у которого не было бы необходимых разрешений. Поскольку инструкции Dockerfile применимы только к сборкам образов, а не к запуску контейнеров, безопасно отложить изменение пользователя до более позднего времени в вашем Dockerfile.
Изменение пользователя, под которым работает ваш контейнер, может потребовать обновления разрешений для файлов и папок, к которым он обращается. Установите права собственности на любые пути, которые будут использоваться вашим приложением:
COPY initial-config.yaml /data/config.yaml USER demo-user:demo-group RUN chown demo-user:demo-group /data
В этом примере каталог /data
должен принадлежать demo-user
, чтобы приложение могло вносить изменения в свой файл конфигурации. Предыдущий оператор COPY
скопировал файл как root. Сокращение доступно при использовании флага --chown
с copy
:
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:latest $ docker run -d --user 950:950 demo-image:latest
Флаг --user
запускает процесс контейнера от имени указанного пользователя. Это менее безопасно, чем инструкция Dockerfile USER
, потому что вам нужно применять ее отдельно к каждой команде docker run
. Лучшим вариантом для регулярно используемых изображений является создание собственного производного изображения, которое может установить новую учетную запись пользователя:
FROM image-that-runs-as-root:latest USER demo-user
$ docker build . -t image-that-now-runs-as-non-root:latest
Смена пользователя стороннего образа может вызвать проблемы: если контейнер должен запускаться от имени пользователя root или ему требуется доступ к путям файловой системы, принадлежащим пользователю root, при использовании приложения вы увидите ошибки. Вы можете попробовать вручную изменить разрешения на пути, которые вызывают проблемы. В качестве альтернативы проверьте, поддерживает ли поставщик метод запуска приложения с непривилегированной учетной записью пользователя.
Работа с приложениями, которые должны запускаться от имени root
Пространство имен пользователей — это метод работы с приложениями, которым требуются некоторые привилегии root. Он позволяет сопоставить root внутри контейнера с пользователем без полномочий root на вашем хосте. Смоделированный корень внутри контейнера имеет необходимые ему привилегии, но прорыв не предоставит корневой доступ к хосту.
Переназначение пространства имен активируется добавлением поля userns-remap
в файл /etc/docker/daemon.json
:
{ "userns-remap": "default" }
Использование default
в качестве значения для userns-remap
указывает Docker автоматически создать нового пользователя на вашем хосте с именем dockremap
. Корень в контейнерах будет отображаться обратно в dockremap
на вашем хосте. Вместо этого вы можете дополнительно указать существующего пользователя и группу, используя комбинацию UID/GID или имя пользователя/имя группы:
{ "userns-remap": "demo-user" }
Перезапустите демон Docker после применения изменений:
$ sudo service docker restart
Если вы используете nsuser-remap: default
, пользователь dockremap
теперь должен существовать на вашем хосте:
$ id dockremap
uid=140(dockremap) gid=119(dockremap) groups=119(dockremap)
Пользователь также должен появиться в подчиненных ID-файлах /etc/subuid
и /etc/subgid
:
$ dockremap: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
total 14
drwx------ 5 231500 231500 13 Jul 22 19:00 aufs
drwx------ 3 231500 231500 13 Jul 22 19:00 containers
...
Пространство имен пользователей — это эффективный способ повысить изоляцию контейнера, избежать разрывов и сохранить совместимость с приложениями, которым требуются привилегии root. Однако есть некоторые компромиссы: эта функция лучше всего работает на новом экземпляре Docker, для томов, подключенных с хоста, должны быть настроены их разрешения, а некоторые внешние драйверы хранилища вообще не поддерживают сопоставление пользователей. Вы должны просмотреть документацию, прежде чем принять этот вариант.
Краткое содержание
Запуск контейнерных приложений с правами root представляет угрозу безопасности. Хотя легко упустить из виду, изоляция, обеспечиваемая контейнерами, недостаточно сильна, чтобы полностью отделить пользователей ядра от пользователей контейнеров. Корень в контейнере совпадает с корнем на вашем хосте, поэтому успешная компрометация может обеспечить контроль над вашей машиной.
Как автор изображения, вы должны включить инструкцию USER
в свой Dockerfile, чтобы ваше приложение работало без root. Пользователи образа могут переопределить это с помощью docker run --user
, чтобы назначить определенный UID и GID. Это помогает смягчить случаи, когда образ обычно использует root.
Вы можете усилить безопасность, удалив все возможности из контейнера с помощью --cap-drop=ALL
, а затем добавив необходимые в белый список с помощью флагов --cap-add
. Комбинация этих методов запустит ваше приложение от имени пользователя без полномочий root с минимальным набором необходимых ему привилегий, что улучшит вашу безопасность.