- PVSM.RU - https://www.pvsm.ru -

Повышаем безопасность контейнеров Docker

Повышаем безопасность контейнеров Docker - 1

— Сударь, каким образом вас взломали?
— Не образом, а контейнером.
Старинный анекдот

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

Тот, кто хоть немного работал с Docker, наверняка слышал про образ alpine [1]. Он создан на основе дистрибутива Alpine Linux [2], который по сравнению, например, с Debian или Ubuntu при размере базового образа в 5 Мб оставляет взломщикам гораздо меньше возможностей для атаки. Если приложение сможет работать в alpine, это будет отличным способом оптимизации.

А что насчет бинарных файлов? Может ли приложение работать автономно? Если да, то есть основания рассчитывать на дополнительное уменьшение размера. В качестве базового для таких образов, как Debian и Ubuntu, обычно используется scratch, но в нем также может заработать приложение на golang. Gianluca Arbezzano создал репозиторий [3] с готовыми бинарными файлами минимального размера. Давайте попробуем linux_386.

Повышаем безопасность контейнеров Docker - 2

curl -SsL https://github.com/gianarb/micro/releases/download/1.0.0/micro_1.0.0_linux_386 > micro

Мы можем включить этот бинарный файл в scratch-образ с помощью вот такого Dockerfile:

FROM scratch

ADD ./micro /micro
EXPOSE 8000

CMD ["/micro"]

docker build -t micro-scratch .
docker run -p 8000:8000 micro-scratch

В итоге нам удалось запустить http-приложение в образе размером всего 5 Мб, то есть мы уменьшили его более чем в два раза по сравнению 12 Мб, которые занимает образ, созданный на основе alpine.

Scratch-образ невозможно использовать с любыми приложениями, но ради снижения накладных расходов стоит попытаться.

Для Ruby в качестве базового можно использовать ruby:2.3-alpine [4]. Ruby в нем устанавливается из исходников, а не из пакета Alpine. С учетом семантического версионирования [5] релиз 2.3 будет получать обновления безопасности напрямую от разработчиков.

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

Уведомления и веб-хуки

Если для базового образа выпущено обновление безопасности, необходимо обновить те образы, которые на нем основаны. Здесь могут помочь уведомления MicroBadger [6], которые можно отправлять, например, в Slack, как это делают ребята из Microscaling для официальных образов alpine и ruby.

Повышаем безопасность контейнеров Docker - 3

Они также используют оповещения для автоматического запуска процедуры сборки/пересборки в случае изменения базовых образов. Такая функциональность есть и в Docker Hub, но Microscaling утверждает, что MicroBadger лучше, так как может использоваться с любой системой, поддерживающей веб-хуки (например, CI или сканером безопасности).

Обычный пользователь

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

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

# Создаем рабочий каталог
WORKDIR /app
# Копируем код Rails-приложения в образ
COPY . ./
# Создаем обычного пользователя, устанавливаем права и меняем юзера
RUN addgroup rails && adduser -D -G rails rails 
 && chown -R rails:rails /app
USER rails

Сканирование безопасности

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

Для сканирования безопасности образов реестр Quay.io использует Clair [8] — продукт с открытым исходным кодом от CoreOS. Совсем недавно в Clair была добавлена [9] поддержка Alpine, что на самом деле очень здорово. Будем надеяться, что эта функциональность скоро будет доступна и в Quay. Помимо Clair существуют сканеры TwistLock [10] и Aqua [11], но в большинстве случаев за их использование надо платить.

Clair — это приложение на Golang, которое реализует набор HTTP API для выгрузки, загрузки и анализа образов. Данные об уязвимостях загружаются из различных источников, таких как Debian Security Tracker [12] или RedHat Security Data [13], и сохраняются в Postgres. Clair работает по принципу статического анализатора, поэтому, чтобы просканировать контейнер, его не надо запускать — проверяется лишь файловая система образа.

docker run -it -p 5000:5000 registry

С помощью этой команды мы запустили собственный реестр, чтобы использовать его в качестве источника образов для сканирования. Давайте попробуем загрузить в него образ micro от Gianluca Arbezzano [3]:

docker pull gianarb/micro:1.0.0
docker tag gianarb/micro:1.0.0 localhost:5000/gianarb/micro:1.0.0
docker push localhost:5000/gianarb/micro:1.0.0

Далее установим Clair.

mkdir $HOME/clair-test/clair_config
cd $HOME/clair-test
curl -L https://raw.githubusercontent.com/coreos/clair/v1.2.2/config.example.yaml -o clair_config/config.yaml
curl -L https://raw.githubusercontent.com/coreos/clair/v1.2.2/docker-compose.yml -o docker-compose.yml

Пропишите в $HOME/clair_config/config.yml ваши настройки подключения к базе данных postgresql://postgres:password@postgres:5432?sslmode=disable

Для запуска Postgres и Clair нужно выполнить следующую команду:

docker-compose up

Чтобы облегчить процедуру тестирования, воспользуемся CLI под названием Hyperclair (это клиент для работы с Clair). Ниже приведены команды для Mac OS (если вы используете другую ОС, см. https://github.com/wemanity-belgium/hyperclair/releases [14]):

curl -SSl https://github.com/wemanity-belgium/hyperclair/releases/download/0.5.2/hyperclair-darwin-386 > ~/hyperclair
chmod 755 ~/hyperclair

Теперь у нас в ~/hyperclair есть исполняемый файл:

~/hyperclair pull localhost:5000/gianarb/micro:1.0.0
~/hyperclair push localhost:5000/gianarb/micro:1.0.0
~/hyperclair analyze localhost:5000/gianarb/micro:1.0.0
~/hyperclair report localhost:5000/gianarb/micro:1.0.0

Сгенерированный отчет выглядит вот так:

Повышаем безопасность контейнеров Docker - 4

Удаление потенциально уязвимых сборочных зависимостей Rails-приложения

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

Предположим, что сканирование выявило критические уязвимости в libxml2 и libxslt. Это buildtime-зависимости гема Nokogiri, который является XML- и JSON-парсером. С целью увеличения производительности этот гем использует написанные на Си расширения, требующие компиляции. Но после того как гем установлен, libxml2 и libxslt больше не нужны.

Давайте удалим все buildtime-зависимости:

# Кеш для установки гемов
WORKDIR /tmp
ADD Gemfile* /tmp/
# Обновляем и устанавливаем все необходимые пакеты
# В конце удаляем используемые для сборки пакеты и apk-кеш
RUN apk update && apk upgrade && 
 apk add --no-cache $RUBY_PACKAGES && 
 apk add --no-cache --virtual build-deps $BUILD_PACKAGES && 
 bundle install --jobs 20 --retry 5 && 
 apk del build-deps

За счет кеширования Gemfile и Gemfile.lock в /tmp [15] команда bundle install запустится только в случае изменения Gemfile. В противном случае будет использован кеш Docker. Такая оптимизация позволяет уменьшить время выполнения и нагрузку на сеть, которые при установке гемов могут быть достаточно велики.

Заметьте, что команда run многострочная, и поэтому в образ добавляется только один слой. Необходимые для сборки пакеты устанавливаются с ключом --virtual, и их легко удалить после завершения процесса.

Автоматизированная сборка

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

В случае Ruby ситуация упрощается, так как мы можем взять те же самые файлы Dockerfile, которые использовали в процессе создания. Для программ на Go сначала нужно скомпилировать бинарный файл, а затем уже добавлять его в образ. Локально для этого можно использовать makefile.

Альтернативным вариантом будет компиляция бинарного файла по событию в docker-контейнере. Рекомендую посмотреть на пару golang-builder-образов от CenturyLinkLabs [16] и Prometheus [17].

Для запуска процесса сборки можно использовать сборочные хуки (build hooks), которые также удобны для добавления в образы динамических метаданных [18].

Повышаем безопасность контейнеров Docker - 5

Заключение

Итак, мы коротко рассмотрели способы минимизации образов Docker для приложений на Go и Ruby, научились запускать контейнеры под обычным пользователем, настроили сканирование безопасности с помощью Clair и немного поговорили об автоматической пересборке. Надеюсь, эти простые шаги помогут повысить безопасность ваших контейнеров Docker. На этом пока все. Спасибо за внимание!

Список источников:

  1. https://medium.com/microscaling-systems/dockerfile-security-tuneup-166f1cdafea1#.a24qq9tv7 [19]
  2. http://gianarb.it/blog/about-your-images-security-tips [20]

Автор: Centos-admin.ru

Источник [21]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/sistemnoe-administrirovanie/238755

Ссылки в тексте:

[1] alpine: https://hub.docker.com/_/alpine/

[2] Alpine Linux: https://alpinelinux.org/

[3] репозиторий: https://github.com/gianarb/micro/releases/tag/1.0.0

[4] ruby:2.3-alpine: https://microbadger.com/images/ruby:2.3-alpine

[5] семантического версионирования: http://semver.org/

[6] уведомления MicroBadger: https://microbadger.com/

[7] сканирование безопасности: https://docs.docker.com/docker-cloud/builds/image-scan/

[8] Clair: https://github.com/coreos/clair

[9] добавлена: https://github.com/coreos/clair/pull/272

[10] TwistLock: https://www.twistlock.com/

[11] Aqua: https://www.aquasec.com/

[12] Debian Security Tracker: https://security-tracker.debian.org/tracker

[13] RedHat Security Data: https://www.redhat.com/security/data/metrics/

[14] https://github.com/wemanity-belgium/hyperclair/releases: https://github.com/wemanity-belgium/hyperclair/releases

[15] кеширования Gemfile и Gemfile.lock в /tmp: https://medium.com/@fbzga/how-to-cache-bundle-install-with-docker-7bed453a5800#.5vyqf8x9d

[16] CenturyLinkLabs: https://github.com/CenturyLinkLabs/golang-builder

[17] Prometheus: https://github.com/prometheus/golang-builder

[18] добавления в образы динамических метаданных: https://medium.com/microscaling-systems/labelling-automated-builds-on-docker-hub-f3d073fb8e1#.e64qpdd1q

[19] https://medium.com/microscaling-systems/dockerfile-security-tuneup-166f1cdafea1#.a24qq9tv7: https://medium.com/microscaling-systems/dockerfile-security-tuneup-166f1cdafea1#.a24qq9tv7

[20] http://gianarb.it/blog/about-your-images-security-tips: http://gianarb.it/blog/about-your-images-security-tips

[21] Источник: https://habrahabr.ru/post/320552/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best