Рекомендации по безопасности при работе с Docker +20


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


1. Достоверность образа


Начнем с проблемы, которая является, пожалуй, неотъемлемой частью самой природы Docker — достоверность образа.


Если вы хоть когда-нибудь пользовались Docker, то вам должно быть известно, что с его помощью вы можете разместить контейнеры практически на любом образе — как на образе из официального списка поддерживаемых репозиториев, таких как NGINX, Redis, Ubuntu, или Alpine Linux, так и на любом другом.


В результате у нас есть огромный выбор.


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


Если вы со мной не согласны, давайте рассмотрим этот вопрос с другой стороны.


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


Я прав?


Ну и с таким же подозрением надо относиться к Docker-контейнерам.


Если вы не знаете автора кода, как вы можете быть уверены в том, что выбранный вами контейнер не содержит бинарников другого вредоносного кода?


Верно, никакой уверенности тут быть не может.


При таких условиях я могу дать три совета.


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


Во-первых, можно использовать приватные или проверенные репозитории, вроде доверенных репозиториев Docker Hub.


В официальных репозиториях можно найти следующие образы:


  • Операционные системы (Ubuntu, например)
  • Языки программирования (PHP и Ruby)
  • Сервера (MySQL, PostgreSQL и Redis)

Что выделяет Docker Hub из других репозиториев, помимо прочего, — это то, что образы всегда сканирует и просматривает Docker’s Security Scanning Service.


Если вы не слышали об этом сервисе, то вот цитата из его документации:


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

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


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


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


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


Еще один инструмент, которым стоит воспользоваться — Docker Content Trust.


Это новая функция, доступная в Docker Engine 1.8. Она позволяет верифицировать владельца образа.


Цитата из статьи о новом релизе, автор Diogo Monica, ведущий специалист по безопасности Docker:


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

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


Docker Bench Security


Еще один инструмент, которым я недавно пользовался — это Docker Bench Security. Это большая подборка рекомендаций по развертыванию контейнеров в продакшене.


Инструмент основывается на рекомендациях из the CIS Docker 1.13 Benchmark, и применяется в 6 областях:


  • Конфигурация хоста.
  • Конфигурация демона Docker.
  • Файлы конфигурации демона Docker.
  • Образы контейнеров и build файлы.
  • Runtime контейнера.
  • Операции Docker security.

Чтобы его установить, клонируйте репозиторий при помощи


    git clone git@github.com: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

Таким образом, вы соберете контейнеры и запустите скрипт, который проверит безопасность хост машины и ее контейнеров.


Ниже пример того, что вы получите на выходе.


Docker Security Benchmark Sample


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


В моем случае необходимы некоторые исправления.


Что мне особенно нравится в этой функции, так это то, что ее можно автоматизировать.


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


2. Лишние полномочия


Теперь следующий момент. Насколько я помню, вопрос лишних полномочий всегда имел место. Что во времена установки дистрибутивов Linux на bare-metal сервера, что сейчас, когда их устанавливают в качестве гостевых операционных систем внутри виртуальных машин.


То, что теперь мы их устанавливаем внутри Docker-контейнера, не значит, что они стали существенно безопаснее.


К тому же с Docker повысился уровень сложности, т.к. теперь граница между гостем и хостом неясна. В отношении Docker я концентрируюсь на двух вещах:


  • Контейнеры, запущенные в привилегированном режиме
  • Лишние полномочия у контейнеров

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


Цитата из документации:


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

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


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


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


Цитата из Armin Braun:


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

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


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


В зависимости от того, где вы хостите ваши Docker-контейнеры, включая таких вендоров как DigitalOcean, sloppy.io, dotCloud и Quay.io, их дефолтные настройки могут отличаться от ваших.


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


Откажитесь от ненужных привилегий и возможностей


Не важно, где вы хоститесь. Как говорится в руководстве Docker по безопасности:


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

Подумайте над этими вопросами:


  • Какое сетевое подключение требуется для вашего приложения?
  • Нужен ли ему прямой доступ к сокету?
  • Надо ли ему отправлять и получать UDP-запросы?

Если нет, то отключите эти возможности.


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


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


Чтобы это сделать, используйте опции --cap-drop and --cap-add.


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


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

Более подробные инструкции можно изучить в документации Docker: “Runtime privilege and Linux capabilities


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


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


Но насколько безопасен этот образ?


Например, какие права будут у злоумышленников, если они вдруг получат доступ к вашим контейнерам? Другими словами, насколько вы обезопасили свои контейнеры?


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


Docker, безусловно, безопасен по умолчанию, благодаря namespaceам и cgroupам, но не стоит беззаветно уповать на эти функции.


Вы можете пойти дальше и воспользоваться другими инструментами безопасности на Linux, такими как AppArmor, SELinux, grsecurity и Seccomp.


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


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


AppArmor


Это модуль безопасности ядра Linux, который позволяет системному администратору ограничить возможности программы с помощью индивидуальных профилей программ. Профили могут выдавать разрешения на такие действия как read, write и execute для файлов on matching paths. AppArmor предоставляет обязательный контроль доступа (mandatory access control, MAC) и таким образом служит хорошим дополнением к традиционной модели контроля Unix (discretionary access control, DAC). AppArmor включен в основное ядро Linux, начиная с версии 2.6.36.

Источник: Википедия.


SELinux


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

Источник — Википедия.


Grsecurity


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

Источник — Википедия.


Seccomp


Это объект безопасности компьютера в ядре Linux. В версии 2.6.12, опубликованной 8 марта 2005 года, его объединили с основным ядром Linux. Seccomp позволяет перевести процесс в "безопасный" режим, из которого нельзя делать никаких системных вызовов, кроме exit(), sigreturn(), read() и write() уже открытых файловых дескрипторов. Если процесс пытается сделать какие-либо другие системные вызовы, то ядро убивает процесс с SIGKILL. Таким образом, Seccomp не виртуализирует системные ресурсы, а просто изолирует от них процесс.

Источник: Wikipedia.


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


Но все же я очень рекомендую побольше о них узнать и внедрить в свою инфраструктуру.


4. Ограничьте потребление доступных ресурсов


Что нужно вашему приложению?


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


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


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


Для этого используйте следующие команды для Docker:


    -m / --memory: # Установить лимит памяти
    --memory-reservation: # Установить мягкий лимит памяти
    --kernel-memory: # Установить лимит памяти ядра
    --cpus: # Ограничьте количество CPU
    --device-read-bps: # Ограничьте пропускную способность чтения для конкретного устройства

Вот пример конфига из официальной документации Docker:


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

Больше информации можно найти при помощи команды docker help run или же в разделе “Runtime constraints on resources” документации Docker.


5. Большая поверхность атаки


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


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


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


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


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

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


Внедрите контрольный журнал с правильным логированием


Внутри приложения обычно ведется учет действий пользователя, таких как:


  • Когда пользователь создал свой аккаунт
  • Когда он его активировал
  • Когда пользователь последний раз менял пароль и т.п.

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


Не стоит этот учет излишне усложнять. Следует вести учет таких действий, как:


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

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


Вдобавок стоит внедрить уведомления по почте или любым другим способом (IRC, Slack или HipChat). Этот прием позволит убедиться, что все могут видеть, когда что разворачивается.


Таким образом, если случилось что-то неподобающее, спрятать это не получится.


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


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


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


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


Заключение


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


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


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


Об авторе


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




Данная статья является переводом Docker Security Best Practices

-->


К сожалению, не доступен сервер mySQL