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

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

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

1. Подлинность образов

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

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

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

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

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

Учитывая вышеизложенное, я бы посоветовал сделать три вещи.

Используйте частные (private) и доверенные (trusted) репозитории

В частности, я рекомендую официальные репозитории на Docker Hub.

Там есть базовые образы для:

  • операционных систем, таких как Ubuntu;
  • языков программирования, таких как PHP и Ruby;
  • серверов, таких как MySQL, PostgreSQL и Redis.

Помимо всего прочего, Docker Hub выделяется тем, что образы в нем всегда сканируются с помощью службы проверки безопасности (Security Scanning Service) Docker.

Для тех, кто ранее не слышал об этом сервисе, привожу выдержку из документации:

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

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

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

Вы также можете создать частный (private) репозиторий для использования внутри организации.

Используйте Docker Content Trust

Еще одним полезным инструментом является Docker Content Trust. Эта функция была представлена сравнительно недавно — в Docker Engine 1.8. Она позволяет издателям заверять созданные ими Docker-образы.

Процитирую статью, посвященную выпуску соответствующего релиза, за авторством Diogo Mónica — ведущего специалиста по безопасности компании Docker:

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

В итоге этот сервис защищает от подделывания образов, атак повторного воспроизведения (replay attacks) и компрометации ключей. Я настоятельно рекомендую прочитать соответствующую статью и официальную документацию.

Docker Bench Security

Не так давно я начал использовать инструмент под названием Docker Bench Security, который:

Проводит проверку на основе десятков зарекомендовавших себя методов, относящихся к развертыванию Docker-контейнеров в production.

Этот инструмент основан на рекомендациях CIS Docker 1.13 Benchmarkи проводит проверки в следующих областях:
1) конфигурация хоста;
2) конфигурация демона Docker;
3) конфигурационные файлы демона Docker;
4) образы и сборочные файлы контейнеров;
5) среда исполнения контейнеров;
6) безопасность Docker.

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

git clone [email protected]:docker/docker-bench-security.git

Затем сделайте cd docker-bench-secutity и выполните следующую команду:

docker run -it --net host --pid host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc:/etc --label docker_bench_security \
docker/docker-bench-security

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

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

Особенно мне приглянулась возможность автоматизации. То есть проверки можно включить в процесс непрерывной интеграции и таким образом постоянно контролировать уровень безопасности контейнеров.

2. Избыточные привилегии

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

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

В плане Docker я обращаю особое внимание на 2 вещи:

  • контейнеры, работающие в привилегированном режиме;
  • избыточные привилегии для контейнеров.

Начнем с первого пункта. Контейнер в Docker может быть запущен с ключом privileged, который дает ему дополнительные привилегии.

Процитируем документацию:

предоставляет контейнеру все разрешения и снимает все ограничения, наложенные cgroup-контроллером устройства. Другими словами, контейнер может делать практически все то же, что и хост. Этот флаг сделали для особых случаев, например, для запуска Docker внутри Docker.

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

Процитирую Armin Braun:

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

Но даже если вы не используете контейнеры с опцией --privileged, у некоторых из них могут быть избыточные привилегии. По умолчанию Docker запускает контейнеры с довольно ограниченным набором возможностей. Это, однако, можно изменить путем редактирования профиля. В зависимости от вашего хостинг-провайдера, включая DigitalOcean, sloppy.io, dotCloud и Quay.io, эти настройки могут отличаться от идущих по умолчанию. Если же вы развернули Docker самостоятельно, проверка привилегий, с которыми запускаются ваши контейнеры, не менее важна.

Отключите ненужные привилегии и разрешения (capabilities)

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

Пользователям рекомендуется отключить все разрешения (capabilities), за исключением тех, которые точно необходимы их процессам.

Ответьте на следующие вопросы:

  • Какое сетевое соединение нужно вашему приложению?
  • Нужен ли ему доступ к raw-сокетам?
  • Будет ли оно работать по UDP?

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

Для управления разрешениями контейнеров используются опции --cap-drop и --cap-add.

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

Соответствующие разрешения можно настроить следующим образом:

docker run \
--cap-drop SETPCAP \
--cap-drop NET_BIND_SERVICE \
--cap-add SYS_MODULE \
-ti /bin/sh

Более подробную информацию об этих опциях вы можете найти в разделе документации «Runtime privilege and Linux capabilities».

3. Безопасность системы

Итак, у вас есть удостоверенный издателем образ и уменьшенный набор привилегий контейнера. Но насколько этот образ безопасен? Например, какие разрешения получит злоумышленник, завладев доступом к контейнеру? Или другими словами: насколько вы обезопасили свои контейнеры? Если атакующему удастся взломать контейнер, сможет ли он проникнуть куда-то еще? Если да, то с этим надо что-то делать.

Благодаря пространствам имен (namespaces) и контрольным группам (cgroups) Docker даже с настройками по умолчанию становится с каждым релизом все более безопасным, но вам необязательно полагаться только на эти функции. Можно сделать шаг вперед и воспользоваться дополнительными механизмами обеспечения безопасности Linux, такими как AppArmor, SELinux, grsecurity и Seccomp.

Все они являются зрелыми и хорошо протестированными инструментами, которые в состоянии усилить безопасность вашей системы. Если вы с ними еще не знакомы, вот их краткое описание.

AppArmor

AppArmor — программный инструмент упреждающей защиты, основанный на политиках безопасности (известных также как профили (англ. profiles)), которые определяют, к каким системным ресурсам и с какими привилегиями может получить доступ то или иное приложение. В AppArmor включен набор стандартных профилей, а также инструменты статического анализа и инструменты, основанные на обучении, позволяющие ускорить и упростить построение новых профилей. — Источник: Wikipedia.

SELinux

SELinux (англ. Security-Enhanced Linux — Linux с улучшенной безопасностью) — реализация системы принудительного контроля доступа, которая может работать параллельно с классической избирательной системой контроля доступа. — Источник: Wikipedia.

grsecurity

Grsecurity — это проект для Linux, который включает в себя некоторые связанные с безопасностью улучшения, включая принудительный контроль доступа, рандомизацию ключевых локальных и сетевых информативных данных, ограничения /proc и chroot() jail, контроль сетевых сокетов, контроль возможностей и добавочные функции аудита. Типичной областью применения являются web-серверы и системы, которые принимают удаленные соединения из сомнительных мест, такие как серверы, которые обеспечивают shell-доступ для пользователей. — Источник: Wikipedia.

seccomp

seccomp (сокращение от secure computing mode) — это механизм обеспечения безопасности в ядре Linux. Он был помещен в основную ветку ядра Linux начиная с версии 2.6.12, которая была выпущена 8 марта 2005. Seccomp позволяет процессу перейти в «безопасное» состояние, из которого он не может выйти и в котором способен выполнить лишь ограниченный набор системных вызовов: exit(), sigreturn(), а также read() и write() к уже открытым файловым дескрипторам. При попытке выполнения других системных вызовов процесс будет завершен ядром с помощью SIGKILL. Таким образом, этот механизм не виртуализирует системные ресурсы, а практически полностью изолирует от них запущенный процесс. — Источник: Wikipedia

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

4. Ограничение потребления ресурсов

Каковы потребности вашего приложения? Может быть, ему нужно не более 50 МБ памяти? Тогда зачем позволять больше? Выполняет ли ваше приложение интенсивную обработку данных, для которой требуется 4 и более процессорных ядер? Тогда выделите необходимые ресурсы, но не более того.

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

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

  • -m / — memory: устанавливает ограничения использования памяти;
  • — memory-reservation: устанавливает «мягкое» ограничение использования памяти;
  • — kernel-memory: ограничение использования памяти ядра;
  • — cpus: ограничение использования процессора;
  • — device-read-bps: ограничение скорости чтения данных с устройства.

Пример ниже демонстрирует использование некоторых из этих параметров в конфигурационном файле Docker compose, взятом из официальной документации:

version: '3'
services:
redis:
image: redis:alpine
deploy:
resources:
limits:
cpus: '0.001'
memory: 50M
reservations:
memory: 20M

Более подробную информацию можно получить, воспользовавшись интерактивной справкой Docker или обратившись к разделу документации “Runtime constraints on resources”.

5. Поверхность атаки

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

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

Не уверены по поводу статистики развертывания в организации? Задайте себе следующие вопросы:

  • Какие приложения развернуты в организации в данный момент?
  • Кто их развернул?
  • Когда они были развернуты?
  • Почему они были развернуты?
  • Как долго они будут развернуты?
  • Кто за них отвечает?
  • Когда в последний раз была проведена проверка их безопасности?

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

Внедрите журнал аудита и соответствующее логирование

Помимо журналов аудита (audit trail) приложений, учитывающих время создания и активации учетной записи пользователя, последнего обновления пароля и т. д., рассмотрите внедрение журнала аудита контейнеров, создаваемых и разворачиваемых в организации.

Эта система не обязана быть очень сложной, но она должна фиксировать следующие события:

  • когда приложение было развернуто;
  • кто его развернул;
  • почему оно было развернуто;
  • каково его назначение;
  • когда оно устареет и должно быть выведено из строя.

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

В дополнение рассмотрите создание оповещений, которые должны отправляться на email или в IRC, Slack, HipChat и т. п. Благодаря этому дополнительному уровню безопасности сотрудники получают информацию о выполненных развертываниях, что делает процесс более прозрачным. Теперь то, что не должно было произойти, уже не сможет пройти незамеченным.

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

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

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

Заключение

Итак, мы рассмотрели 5 вопросов, связанных с безопасностью Docker, а также поговорили о возможных решениях. Надеюсь, что если вы переходите на Docker, раздумываете о таком шаге или уже это сделали, то предложенный материал поможет в усилении безопасности вашей контейнерной инфраструктуры.

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