ПАВЕЛ КАНн
Обзор
утилит безопасности Docker
Продолжаем тему анализа безопасности Docker.
Обзор open-source утилит, которые помогают
в этом сложном деле.
Продолжая тему анализа безопасности Docker сделаю небольшой обзор open-source утилит, которые помогают в этом сложном деле. Сразу стоит отметить, что инструменты сами по себе – вещь хорошая, но в них не очень много проку, если в команде нет понимания, чего этими инструментами хотят добиться, а также нет построенного процесса работы с ними и их результатами.

Возвращаясь к картинке из прошлого поста рассмотрим следующие области анализа:
TL;DR

Персонально для выделенной зоны покрытия мне понравился такой набор (Lynis) + (DockerBenchSecurity) + (Hadolint) + (Dockle) + (Trivy или Anchore). Им мы закрываем почти все слои, касающиеся специфики Docker, начиная от хоста, где он крутится и заканчивая проблемами конкретного образа.


Docker Host Security

Cамый базовый уровень безопасности всей инфраструктуры Docker – защита хоста, на котором установлен Docker и на котором запускаются контейнеры (отдельно обращу внимание, что защита распределенных систем управления контейнерами вынесена в отдельную тему). Обеспечение безопасности хоста как такового – практика довольно обширная и выходит за рамки темы безопасности Docker, однако в первом приближении можно использовать следующую утилиту для того, чтобы понять, на каком уровне защиты мы находимся в данный момент.


Lynis

URL: https://cisofy.com/lynis/

Утилита для базовой быстрой проверки основных параметров безопасности Unix-based систем, таких как конфигурация пользователей ОС, работа подсистем аудита, неиспользуемые аккаунты и файлы и даже пара проверок для Docker, таких как, например, наличие неиспользуемых контейнеров и т.д. Инструкция по установке и запуску достаточно простая.


Docker Daemon Security / Docker Containers Runtime

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

URL: https://github.com/docker/docker-bench-security

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

Основной набор проверок строится на базе документа CIS Benchmarks for Docker (CIS_Docker_Community_Edition_Benchmark_v1.1.0.pdf) – документ 2017 года и пока обновлений не было, но информации и best practices достаточно много. Сам инструмент DBS тоже тех же лет, но фиксы периодически коммитятся в репозиторий: https://github.com/docker/docker-bench-security

DBS включает в себя 7 категорий проверок:

1. Конфигурация хоста (отдельная партиция под контейнеры, аудит процессов, …);

2. Конфигурация Docker daemon (поддержка USR namespace, конфигурация сетевого трафика);

3. Проверка конфигурационных файлов Docker daemon (пермиссии к docker.socket, иным файлам и директориям Docker);

4. Проверка образов контейнеров и build-файлов (использование инструкции ADD, включенный Healthcheck, пользователь, под которым работают контейнеры,…);

5. Runtime проверки (AppArmor, SELinux расширения, политика перезапусков, открытые порты и используемые разделяемые пространства имен, …);

6. Общая работа с образами (сколько контейнеров/образов имеется и сколько реально используется);

7. Конфигурация Docker Swarm (настройки менеджеров контейнеров, управление секретами на них, отделение трафика между контейнерами от трафика между менеджерами);

Часть этих проверок выполняется автоматически и выдает результат пройдена/не пройдена, а часть требует ручного ревью после выполнения скрипта. В этом один из минусов DBS, что из отчёта сложно понять, что нужно делать и приходится обращаться к документу CIS Benchmark (выше) за деталями, как конкретно подтвердить и исправить ту или иную проблему. Также результат отчёта напрямую не может быть завязан в CI/CD, ввиду отсутствия какого-либо машинно-приемлемого формата вывода – придется пилить какой-нибудь коннектор/парсер.


Установка Docker Bench Security

Можно клонировать репозиторий и запустить ./docker-bench-security.sh который просканирует все имеющиеся образы и контейнеры.

Либо можно использовать собранный контейнер:
$ docker run -it --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc \
-v /usr/bin/docker-containerd:/usr/bin/docker-containerd \
-v /usr/bin/docker-runc:/usr/bin/docker-runc \
-v /usr/lib/systemd:/usr/lib/systemd \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
--label docker_bench_security \
docker/docker-bench-security


Пример отчёта Docker Bench Security:
Docker Bench Test

URL: https://github.com/gaia-adm/docker-bench-test

Также есть развитие идеи DBS – Docker Bench Test. Это переписанный DBS на фреймворке BATs (который предназначен для тестирования на bash) с использованием проверок (assertions). Цель была в том, чтобы упростить автоматизацию проверок (сделав вывод в машинно-читаемом виде) и иметь возможность проводить только выбранные проверки. Иными словами мы сами можем задать условия при которых те или иные проверки будут проходить или не проходить.

Статья автора: https://medium.com/@alexeiled/docker-security-testing-3545e7493843

Установка аналогична.

Можно склонировать репозиторий, либо запустить в контейнере:
$ docker run -it --net host --pid host --cap-add audit_control \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/lib/systemd:/usr/lib/systemd \
-v ~/docker-bench-test:/var/docker-bench-test \
-v /etc:/host/etc \
--label docker_bench_test \
gaiaadm/docker-bench-test


Результаты выполнения скрипта будут помещены в /var/docker-bench-test/results , а сгенерированные для вашего хоста скрипты проверок (для переиспользования в дальнейшем) – var/docker-bench-test/tests

Каждая проверка может быть кастомизирована под конкретные требования:

# 5.25
@test "5.25 - Restrict container from acquiring additional
privileges: {{c}}" {
result=$(docker inspect --format 'SecurityOpt=
{{.HostConfig.SecurityOpt }}' "{{c}}")
run grep "no-new-privileges" <<< "$result"
if [ $status -ne 0 ]; then
fail "Privileges not restricted: {{c}}" fi }


Результаты выполнения проверок выглядят таким образом:
Docker Images Security (system and dependencies vulnerabilities)

Самый большой раздел с точки зрения количества утилит, которые анализируют Docker образы на предмет известных уязвимостей (CVE) в системных пакетах и зависимостях. Здесь речь идёт уже о проблемах именно конкретных сборок, используемых в образе и того, какой набор ПО и с какими проблемами в эти сборки попадает.

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


Dockle

URL: https://github.com/goodwithtech/dockle

Самый первый инструмент не столько ищет CVE в системном ПО образа, сколько проверяет корректность и безопасность конкретного образа как такового, анализируя его слои и конфигурацию – какие пользователи созданы, какие инструкции используются, какие тома подключены, присутствие пустого пароля и т.д. Таким образом функционал частично пересекается с DBS, но позволяет сканировать конкретный образ и за счёт простоты – можно легко внедрить этот инструмент в CI/CD. Пока количество проверок не очень большое и базируется на рекомендациях CIS Benchmark для образов и нескольких собственных проверках. Однако проект развивается и расширяется.

Dockle работает как с образом в репозитории, так и с сохранённым .tar файлом образа.

Ставится и запускается достаточно просто:

$ wget https://github.com/goodwithtech/dockle/releases/download/v0.1.13/dockle_0.1.13_Linux-64bit.tar.gz && tar zxf dockle_0.1.13_Linux-64bit.tar.gz
$ ./dockle httpd:latest


В результате инструмент покажет результат выполнения пары десятков проверок и рекомендаций по образу:
Clair

Представляет собой API и бэкенд для проверки слоёв заданного образа на те или иные CVE, список которых он берёт из общедоступных баз. Стоит заметить, что никакого фронтенда у данного инструмента нет и необходимо будет использовать сторонние утилиты для доступа к АПИ.

По умолчанию ставится сервер + Postgres база, куда продолжительное время заливаются данные об имеющихся CVE в разных сборках. Потом эти данные используются для анализа образа. Заливка данных продолжается минут 15-30, но есть уже готовые докер-образы скаченной базы, которые можно подключить или можно собрать SQL скрипт со всеми данными, который уже накатить на базу.

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


Установка Clair

Есть два варианта поэкспериментировать с этим сервером:
– Использовать сэндбокс окружение с интерактивной инструкцией по использованию:
https://www.katacoda.com/courses/docker-security/image-scanning-with-clair
– Установить готовый пакет и файл docker-compose из этой статьи:
https://nullsweep.com/docker-static-analysis-with-clair/

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

$ mkdir clair_config && curl -L https: //raw.githubusercontent.com/coreos/clair/master/config.yaml.sample -o clair_config/config.yaml

2. Стартуем Postgres и сервер Clair:

$ docker run -d -e POSTGRES_PASSWORD="" -p 5432:5432 postgres:9.6
$ docker run --net=host -d -p 6060-6061:6060-6061 -v $PWD/clair_config:/config quay.io/coreos/clair:latest -config=/config/config.yaml


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

$ docker logs <id или имя контейнера> --tail 5

4. Оценить, отвечает ли сервер, можно так (должен вернуться правильный json):

$ curl -X GET http://127.0.0.1:6060/status

klar
Чтобы дальше хоть как-то работать с сервером, нам потребуется утилита для взаимодействия с АПИ. Первая из них – klarURL:
https://github.com/optiopay/klar/releases/download/v2.4.0/klar-2.4.0-linux-amd64

Скачиваем, кладем или локально или в PATH ( /usr/local/bin/) и запускаем сканирование (для примера используем образ bkimminich/juice-shop):

$ mkdir klar && cd klar && wget https://github.com/optiopay/klar/releases/download/v2.4.0/klar-2.4.0-linux-amd64 -O klar && chmod +x klar
$ CLAIR_ADDR=http: //localhost:6060 CLAIR_THRESHOLD=10 ./klar bkimminich/juice-shop



clairctl
Альтернативный вариант – поставить другой клиент, clairctl. Он умеет генерировать отчет в HTML, однако по ощущениям – более нестабильный.

1. Качаем релизную версию:
$ mkdir clairctl && cd clairctl && wget https://github.com/jgsqware/clairctl/releases/download/v1.2.8/clairctl-linux-amd64 -O clairctl && chmod +x clairctl

2. Скачиваем конфиг clairctl.yaml:
$ wget https://raw.githubusercontent.com/jgsqware/clairctl/master/clairctl.yml.default -O clairctl.yaml

3. В конфиг-файле правим url: на тот, где у нас стоит clair (обычно http://127.0.0.1)

4. Проверяем на каком-нибудь локальном образе:
$ ./clairctl --config ./clairctl.yml analyze -l postgres
Image: /postgres:latest
Unknown: 4
Negligible: 38
Low: 21
Medium: 36
High: 9
Critical: 0
Defcon1: 0

Источники данных

Перечень баз, откуда берутся уязвимости:

  • Debian Security Bug Tracker
  • Ubuntu CVE Tracker
  • Red Hat Security Data
  • Oracle Linux Security Data
  • Amazon Linux Security Advisories
  • SUSE OVAL Descriptions
  • Alpine SecDB
  • NIST NVD


Проблемы

В случае с clair+klar/clairctl сложность в том, чтобы даже после успешной установки понять почему для одних образов эта связка показывает 700 уязвимостей, а для других (гарантированно уязвимых) – 0 уязвимостей.

Вторая сложность в том, чтобы иметь возможность сканировать образы не только из Docker Hub, а еще и из собственных репозиториев или из локальных файлов. Как это делается – пока непонятно.

Третья проблема – все работает очень нестабильно. Скан образов размером в 750 Мб и выше в некоторых конфигурациях с clairctl падает с out of memory на машине с 4 Гб памяти.

Некоторые образы принципиально не сканируются из-за 404, либо 400 ошибки при вытягивании промежуточных слоёв из образа (почему – непонятно).

К тому же проекты выглядят не очень активно поддерживаемыми. Однако они продолжают использоваться во многих других проектах (например некоторые Docker репозитории сразу предлагают сканирование образов в Clair при их размещении).
Trivy

URL: https://github.com/knqyf263/trivy

Trivy находит уязвимости двух типов – проблемы сборок ОС (поддерживаются Alpine, RedHat (EL), CentOS, Debian GNU, Ubuntu) и проблемы в зависимостях (Gemfile.lock, Pipfile.lock, composer.lock, package-lock.json, yarn.lock, Cargo.lock)

В отличие от Clair умеет сканировать как в репозитории, так и локально, так и вообще на основании переданного .tar файла с Docker образом.

Умеет опционально показывать только те CVE, для которых были фиксы, а также скрывать CVE, добавленные в локальный whitelist ( .trivyignore)

Вывод осуществляется как на экран, так и в json. При этом есть возможность фильтрации уязвимостей по критичности, а также Trivy достаточно просто встраивается в CI/CD процессы.
Установка

В отличие от многих других сканеров для установки можно просто скачать готовый бинарник или deb-пакет и запустить, например:
$ wget https: //github.com/knqyf263/trivy/releases/download/v0.1.3/trivy_0.1.3_Linux-64bit.deb
$ dpkg -i ./trivy_0. 1 .3_Linux-64bit.deb
$ trivy vulnerables/cve- 2014 - 6271


Запуск

Просто проверить образ:
$ trivy bkimminich/juice-shop

Проверить образ, экспортированный в tar файл:
$ trivy -i ./ my_saved_docker_image.tar


Сравнение с Clair

На одинаковых образах Trivy показывает больше уязвимостей (при этом автор заявляет, что False Positives практически нет - таблица сравнения с другими сканерами)

httpd (Clair): Found 136 vulnerabilities (Unknown: 7, Negligible: 54, Low: 27, Medium: 33, High: 15)
httpd (Trivy): Total: 243 (UNKNOWN: 2, LOW: 57, MEDIUM: 132, HIGH: 45, CRITICAL: 7)

imiell/bad-dockerfile (Clair): Found 188 vulnerabilities (Low: 27, Medium: 76, High: 71, Critical: 14)
imiell/bad-dockerfile (Trivy): Total: 1723 (UNKNOWN: 0, LOW: 183, MEDIUM: 1229, HIGH: 276, CRITICAL: 35)

Anchore

URL: https://anchore.com/opensource/

Еще одна составная система. Работать с ней можно как напрямую через REST, так и при помощи отдельной утилиты Anchore-cli (https://github.com/anchore/anchore-cli)

Развернуть систему можно из Docker-compose файла, который прилагается на сайте. При этом будут подняты контейнеры для базы, анализатора, API, внутреннего каталога, подсистемы очередей, движка полиси… В общем много контейнеров.


Источники данных

  • Alpine Sec Tracker
  • AMZN CVE Sec Tracker
  • CentOS CVE Sec Tracker
  • Debian Sec Tracker
  • Oracle Linux Sec Tracker
  • Ubuntu Sec Tracker


Anchore

В общем ничего сложного.

Создаем директорию под данные:
$ mkdir ~/aevolume
$ cd ~/aevolume


Вытягиваем образ Anchore Engine:
$ docker pull docker.io/anchore/anchore-engine:latest
$ docker create --name ae docker.io/anchore/anchore-engine:latest


Копируем docker-compose из образа:
$ docker cp ae:/docker-compose.yaml ~/aevolume/docker-compose.yaml
$ docker rm ae


Запускаем движок:
$ docker-compose pull
$ docker-compose up -d


После этого необходимо установить консоль для работы с движком (нужен python+pip):
$ git clone https: //github.com/anchore/anchore-cli
$ cd anchore-cli
$ pip install --user --upgrade .
$ export PATH="$HOME/.local/bin/:$PATH"



Запуск и пример отчёта

Добавляем образ и сканируем его (креды по умолчанию). Имейте ввиду, что Anchore требуется некоторое время на обновление базы уязвимостей и анализ образа:
$ anchore-cli --u admin --p foobar image add httpd:latest
$ anchore-cli --u admin --p foobar image vuln httpd:latest all

Недостатки

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

Из плюсов – умеет разные webhooks, чтобы уведомлять когда появятся новые CVE. Ну и в целом довольно много вспомогательных инструментов, которых нет в Trivy, таких как система событий, управление политиками проверок, поиск по загруженным образам конкретного пакета с конкретной версией или всех образов с конкретной уязвимостью и т.д. Это, в принципе, может быть дописано поверх того же Trivy, но потребует времени на разработку и поддержку.
Aqua Microscanner

URL: https://github.com/aquasecurity/microscanner

Бесплатный тул, но нужно получить токен на почту перед началом использования.
Идея в том, чтобы включить сканер в процесс сборки образа инструкцией в Docker file вида:
RUN apk add --no-cache ca-certificates && update-ca-certificates && \
wget -O /microscanner https: //get.aquasec.com/microscanner && \
chmod +x /microscanner && \
/microscanner YOURTOKEN && \
rm -rf /microscanner


Решение ну такое себе… потому что как минимум это увеличит размер образа, а как максимум – фиг знает что может вместе с этим микросканером прилететь в наш образ.


Враппер

Для простоты использования есть враппер к нему, который сам строит Dockerfile, подкачивает туда нужный образ, скачивает в образ микросканер, сканирует и потом чистит всё за собой: https://github.com/lukebond/microscanner-wrapper

Запускается так:
$ MICROSCANNER_TOKEN=YOUR_TOKEN ./scan.sh bkimminich/juice-shop


Пример отчёта

Результаты умеет выдавать в json и HTML.Находит, опять же, куда как меньше, чем тот же Trivy:

Dockerfile analysis

Для первичного анализа корректности Dockerfile можно воспользоваться различными инструментами:

1. Online Fromlatest linter: https://www.fromlatest.io/#/
Онлайновый валидатор инструкций Dockerfile


2. Наиболее вменяемый вариант – Hadolint:
./broken_3.df:107 DL3015 Avoid additional packages by specifying --no-install-recommends
./broken_3.df:114 DL4001 Either use Wget or Curl but not both
./broken_3.df:139 DL3013 Pin versions in pip. Instead of pip install <package> use pip install <package>==<version>
./broken_3.df:143 DL4001 Either use Wget or Curl but not both
./broken_3.df:143 DL4006 Set the SHELL option -o pipefail before RUN with a pipe in it
./broken_3.df:180 DL4001 Either use Wget or Curl but not both
./broken_3.df:182 DL3008 Pin versions in apt get install. Instead of apt-get install <package> use apt-get install <package>=<version>
./broken_3.df:191 SC2155 Declare and assign separately to avoid masking return values.
./broken_3.df:207 SC2028 echo may not expand escape sequences. Use printf.
./broken_3.df:207 DL3015 Avoid additional packages by specifying --no-install-recommends
./broken_3.df:231 SC2046 Quote this to prevent word splitting.
./broken_3.df:248 DL3020 Use COPY instead of ADD for files and folders
./broken_3.df:251 DL3002 Last USER should not be root


Можно склонировать:
$ git clone https: //github.com/hadolint/hadolint

Или запустить в контейнере:
$ docker run --rm -i hadolint/hadolint < Dockerfile
Павел Канн
Старший Эксперт по безопасности Swordfish Security