Pull to refresh
360.46
FirstVDS
Виртуальные серверы в ДЦ в Москве

Методы обеспечения безопасности контейнеров Docker

Reading time 5 min
Views 13K

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

Метод 1. Использование непривилегированных  пользователей внутри контейнера

По умолчанию Docker запускает контейнеры от имени root пользователя. Чем грозит использование root пользователя в запущенных контейнерах? 

Если в приложении, которое работает в  контейнере, будет обнаружена уязвимость, то это может позволить злоумышленникам выйти из контейнера и выполнить различные действия с правами root на хостовой ОС. Тем самым вы создаете слишком привилегированную среду, которая дает злоумышленникам больше возможностей в случае взлома. Чтобы убедиться в этом, запустим пару разных контейнеров. И первый — образ с веб-сервером Nginx. Для этого используем команду:

docker run -d --name nginx nginx

Далее осуществим вход в оболочку контейнера при помощи команды:

docker exec -it nginx /bin/bash

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

id

Далее запустим контейнер с образом легковесной операционной системы Alpine:

docker run -d -it --name alpine alpine

Осуществим вход в оболочку Bourne shell (sh):

docker exec -it alpine /bin/sh

И выполним команду id:

Как и в случае с контейнером Nginx, можно увидеть, что мы подключились к контейнеру как root.

В качестве решения при запуске контейнеров всегда указывайте имя обычного (непривилегированного пользователя). Для этого при запуске контейнеров необходимо использовать ключ -u (или длинную версию --user) в котором можно задать необходимого пользователя. Рассмотрим пример с запуском контейнера с Nginx который мы запускали ранее:

docker run -d --name nginx nginx

Но теперь подключимся к созданному контейнеру при помощи exec и зададим пользователя nobody:

docker exec -it --user nobody nginx /bin/bash

Как видно на скриншоте выше, приглашение к вводу содержит имя пользователя nobody и символ $. Это означает, что сессия запущена от имени обычного непривилегированного пользователя. Также указать конкретного пользователя можно и в Dockefile. Для этого используется инструкция USER:

FROM ubuntu:16.04
RUN useradd -u 8877 nonroot
USER nonroot

Соберем образ при помощи команды:

docker build -t nonrootuser .

И запустим:

docker run -it nonrootuser /bin/bash

Выполним команду id:

Как можно заметить, мы подключились к контейнеру от имени ранее созданного пользователя — nonroot.

Метод 2. Ограничение на использование capabilities (привилегий)

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

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

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

--cap-drop — запрет на выполнение capabilities

--cap-add — разрешение на выполнение capabilities

Обе эти опции можно использовать сразу для одного контейнера. Рассмотрим на примере запуска контейнера с alpine:

docker run -d -it --cap-drop all --cap-add CAP_SYS_ADMIN alpine /bin/sh

В примере выше мы сначала создали запрет на выполнение любых привилегий в контейнере (опция --cap-drop со значением all), а потом разрешили выполнять привилегию CAP_SYS_ADMIN (--cap-add). Привилегия CAP_SYS_ADMIN, в частности, позволяет монтировать и размонтировать файловые системы.

Метод 3. Использование опции no-new-privileges

Использование опции no-new-privileges позволит предотвратить повышения привилегий в контейнере путем использования setuid и setgid

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

setgid — флаги с правами доступа, которые разрешают пользователям запускать исполняемые файлы с правами группы исполняемого файла. 

Ознакомиться подробнее с данными правами доступа можно по ссылкам setuid и setgid.

Перейдем к демонстрации. Соберем образ из следующего Dockerfile:

FROM ubuntu
RUN cp /bin/bash /bin/setuidbash && chmod 4755 /bin/setuidbash
RUN useradd -ms /bin/bash newuser
USER newuser
CMD ["/bin/bash"]

В собранном образе создадим еще одну оболочку bash с именем setuidbash и назначим ей права root пользователя при помощи setuid. Это означает, что созданную оболочку будем использовать для получения root прав. Соберем образ:

docker build -t newpriv .

Далее запустим контейнер:

docker run -it newpriv

Так как в Dockerfile мы указали пользователя, то сессия запустилась от имени обычного непривилегированного пользователя с именем newuser. Теперь повысим наши привилегии до root, выполнив команду:

/bin/setuidbash -p

Ключ -p (privileged) означает, что запуск будет произведен как suid. Как можно увидеть на скриншоте выше, приглашение к вводу изменилось с символа $ (обычного пользователя) на символ # (root пользователь). Это означает, что получение root прав выполнилось успешно.

Чтобы предотвратить повышение привилегий, контейнер необходимо запустить с опцией no-new-privileges:

docker run -it --security-opt=no-new-privileges:true newpriv

Теперь выполним команду /bin/setuidbash -p еще раз:

Повышений привилегий не сработало.

Метод 4. Использование файловых систем в режиме «только для чтения»

Если в контейнере нет надобности в записи и создании каких-либо файлов, то его стоит запускать в режиме «только для чтения» (read-only). При включенном режиме в контейнере нельзя будет создавать новые файлы и редактировать существующие. В плане безопасности это приведет к тому, что контейнер не сможет записывать или изменять какие-либо данные внутри себя. Проверим данный способ на практике. Для начала запустим контейнер с ОС ubuntu:

docker run -it -d --name ubuntu_shell ubuntu

Перейдем в контейнер и создадим файл test.txt:

docker exec -it ubuntu_shell /bin/bash
touch test.txt

Теперь запустим контейнер с опцией -- read-only:

docker run --read-only -it -d --name ubuntu_read_only ubuntu

Подключимся к нему и попытаемся создать файл test.txt — чтобы проверить, как сработает опция:

docker exec -it ubuntu_read_only /bin/bash

Возникла ошибка touch: cannot touch 'test.txt': Read-only file system, которая сообщает, что файловая система запущена в режиме только для чтения.

Метод 5. Не использовать сетевой интерфейс docker0

По умолчанию для всех создаваемых контейнеров Docker использует свой bridge интерфейс под названием docker0, который всегда создается в системе вместе с установкой Docker:

Отдельно стоит выделить, что при использовании Docker-compose контейнеры подключаются к единой создаваемой сети, и все контейнеры доступны друг для друга. Все контейнеры, подключенные к интерфейсу docker0 будут взаимодействовать друг с другом. Однако для каждого контейнера необходимо создавать свою отдельную сеть и подключать к этой сети только те контейнеры, которые будут использоваться. Тем самым можно изолировать нужные контейнеры или вовсе отключить их от сети. Для создания сетей в Docker используется команда:

docker network create --driver <тип_сети> <имя_сети>

Где параметр driver может принимать одно из следующих значений:

bridge;

host;

overlay;

ipvlan;

macvlan.

Чтобы подключить контейнер к сети, необходимо использовать параметр --network и в качестве значения указать имя требуемой сети.

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


НЛО прилетело и оставило здесь промокод для читателей нашего блога:

— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.

Tags:
Hubs:
+8
Comments 13
Comments Comments 13

Articles

Information

Website
firstvds.ru
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
FirstJohn