Павел Канн
Способы и примеры внедрения утилит для проверки безопасности Docker
Способы и примеры внедрения утилит для проверки безопасности Docker

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

Основная идея состоит в том, чтобы продемонстрировать, как можно внедрить автоматическую проверку Dockerfile и Docker образов, которые создаются в процессе разработки.
Проверка осуществляется следующими способами:
  1. Проверка корректности и безопасности инструкций Dockerfile - утилитой линтером Hadolint;
  2. Проверка корректности и безопасности конечного и промежуточных образов - утилитой Dockle;
  3. Проверка наличия общеизвестных уязвимостей (CVE) в базовом образе и ряде зависимостей - утилитой Trivy;
Ниже описаны три варианта настройки окружения. Один - с описанием поднятия тестового инстанса GitLab, второй, с использованием shell-скрипта и последний, с построением Docker-образа для сканирования Docker-образов. Вы можете выбрать вариант, который удобнее и впоследствии перенести его на свою инфраструктуру.

Все необходимые файлы и дополнительные инструкции находятся в репозитории: https://github.com/Swordfish-Security/docker_cicd
GitLab

Полноценная установка с нуля GitLab-а и запуск утилит для проверки тестового Dockerfile и случайным образом выбранного образа - приложения JuiceShop.

1. Ставим Docker
sudo apt-get update && sudo apt-get install docker.io
2. Добавляем текущего пользователя в группу docker, чтобы можно было работать с докером не через sudo
sudo addgroup <username> docker
3. Находим свой IP
ip addr
4. Ставим и запускаем GitLab в контейнере (заменяя IP адрес в hostname на свой)
docker run --detach \
-- hostname 192.168.1.112 \
--publish 443:443 --publish 80:80 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config : /etc/gitlab \
--volume /srv/gitlab/logs : /var/log/gitlab \
--volume /srv/gitlab/data : /var/opt/gitlab \
gitlab /gitlab-ce :latest
Ждём, пока GitLab выполнит все необходимые процедуры по установке (можно следить за процессом через логи: docker logs -f gitlab)

5. Открываем в браузере свой локальный IP и должны увидеть страницу с предложением поменять пароль для пользователя root:
Задаём новый пароль и заходим в GitLab

6. Создаём новый проект, например cicd-test и инициализируем его стартовым файлом README.md
7. Теперь нам необходимо установить Runner, который будет запускать все необходимые операции.
Скачиваем последнюю версию (в данном случае - под Linux 64-bit)
sudo curl -L --output /usr/local/bin/gitlab-runner https: //gitlab-runner-downloads .s3.amazonaws.com /latest/binaries/gitlab-runner-linux-amd64
8. Делаем его исполняемым
sudo chmod +x /usr/local/bin/gitlab-runner
9. Добавляем пользователя для Runner-а и стартуем сервис
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
sudo gitlab-runner install --user=gitlab-runner --working-directory= /home/gitlab-runner
sudo gitlab-runner start
должно получиться примерно так:
local @osboxes:~$ sudo gitlab-runner install --user=gitlab-runner --working-directory= /home/gitlab-runner
Runtime platform arch=amd64 os=linux pid=8438 revision=0e5417a3 version=12.0.1
local @osboxes:~$ sudo gitlab-runner start
Runtime platform arch=amd64 os=linux pid=8518 revision=0e5417a3 version=12.0.1
10. Теперь регистрируем Runner, чтобы он мог взаимодействовать с нашим инстансом GitLab.
Для этого открываем страницу http://<OUR IP ADDRESS>/root/cicd-test/-/settings/ci_cd и на закладке Runners находим URL и Registration token
11. Регистрируем Runner, подставляя URL и Registration token.
Стоит учитывать, что в этом случае передается флаг --docker-privileged для того, чтобы была возможность запускать контейнеры из-под докер-контейнера.
sudo gitlab-runner register \
--non-interactive \
--url "http://<URL>/" \
--registration-token "<Registration Token>" \
--executor "docker" \
--docker-privileged \
--docker-image alpine:latest \
--description "docker-runner" \
--tag-list "docker,privileged" \
--run-untagged= "true" \
--locked= "false" \
--access-level= "not_protected"
12. Теперь необходимо добавить в репозиторий файлы mydockerfile.df (тестовый Dockerfile, который мы будем проверять) и конфигурационный файл GitLab CI/CD процесса .gitlab-cicd.yml (который перечисляет инструкции для сканеров) - обратите внимание на точку в названии.
YAML файл конфигурации содержит инструкции по запуску трех утилит - Hadolint, Dockle и Trivy, которые проанализируют выбранный Dockerfile и образ, заданный в переменной DOCKERFILE.
Также можно сканировать сохраненные образы в виде .tar-архива (однако потребуется в YAML файле изменить входные параметры для утилит).

Дальше можно вместо тестового mydockerfile.df и случайного образа подставлять свои артефакты, тем самым выполняя проверки при каждом коммите в GitLab репозиторий. Либо сконфигурировать иные условия запуска.

Файлы можно взять из репозитория: https://github.com/Swordfish-Security/docker_cicd/

13. После добавления файлов на закладке CI/CD → Pipelines можно будет увидеть ход выполнения инструкций.
По умолчанию Trivy останавливает выполнение работы если были обнаружены CRITICAL уязвимости в образе или зависимостях. В то же время Hadolint всегда возвращает Success код выполнения.
Результат работы каждой утилиты можно посмотреть:
1. В логе каждой сканирующей джобы;
2. В json файлах в разделе artifacts;
3. В HTML отчёте (ниже)


15. Для представления отчётов утилит в более человекочитаемом виде используется небольшой скрипт на Python для конвертации трёх json в 1 HTML с таблицей дефектов. Этот скрипт запускается отдельной джобой Report и его артефактом является искомый html файл:
shell-скрипт

Готовый скрипт, который можно запустить на чистой виртуалке (при желании и на железной машине): Скрипт выполняет те же самые инструкции, что и вышеописанный gitlab-runner.

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

https://raw.githubusercontent.com/Swordfish-Security/docker_cicd/master/docker_sec_check.sh

Готовый HTML с отчётом будет в ./docker_tools/results.html

Docker контейнер со всеми утилитами

В качестве альтернативы есть два Dockerfile для создания образа с тремя утилитами и скачивания базы данных уязвимостей. Один Dockerfile соберет набор для сканирования образа из репозитория, второй (Dockerfile_tar) - соберет набор для сканирования tar-файла с образом.

1. Берем Docker файл и скрипты отсюда https://github.com/Swordfish-Security/docker_cicd/tree/master/Dockerfile
2. Запускаем его на сборку:
docker build -t dscan -f docker_security.df .
3. После сборки запускаем образ (передаём переменную окружения DOCKERIMAGE с названием образа и маунтим докерфайл для анализа с нашей машины на /Dockerfile)
~/docker_cicd$ docker run --rm -v $(pwd)/results:/results -v $(pwd)/Dockerfile/docker_security.df:/Dockerfile -e DOCKERIMAGE= "bkimminich/juice-shop" dscan:image
[+] Running Hadolint
[+] Running Dockle
[+] Running Trivy
2019 - 07 -01T14: 59 : 47 .122Z INFO Removing image caches...
2019 - 07 -01T14: 59 : 58 .774Z INFO Updating vulnerability database...
2019 - 07 -01T15: 00 : 07 .402Z INFO Detecting Alpine vulnerabilities...
2019 - 07 -01T15: 00 : 07 .431Z INFO Updating npm Security DB...
2019 - 07 -01T15: 00 : 10 .068Z INFO Detecting npm vulnerabilities...
2019 - 07 -01T15: 00 : 10 .074Z INFO Updating npm Security DB...
2019 - 07 -01T15: 00 : 10 .990Z INFO Detecting npm vulnerabilities...
2019 - 07 -01T15: 00 : 11 .017Z INFO Updating vulnerability database...
2019 - 07 -01T15: 00 : 12 .197Z WARN You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed
2019 - 07 -01T15: 00 : 17 .228Z INFO Detecting Alpine vulnerabilities...
2019 - 07 -01T15: 00 : 17 .267Z INFO Updating npm Security DB...
2019 - 07 -01T15: 00 : 17 .563Z INFO Detecting npm vulnerabilities...
2019 - 07 -01T15: 00 : 17 .569Z INFO Updating npm Security DB...
2019 - 07 -01T15: 00 : 17 .857Z INFO Detecting npm vulnerabilities...

bkimminich/juice-shop (alpine 3.9.4 )
====================================
Total: 0 (UNKNOWN: 0 , LOW: 0 , MEDIUM: 0 , HIGH: 0 , CRITICAL: 0 )


juice-shop/ package -lock.json
============================
Total: 0 (UNKNOWN: 0 , LOW: 0 , MEDIUM: 0 , HIGH: 0 , CRITICAL: 0 )


juice-shop/frontend/ package -lock.json
=====================================
Total: 0 (UNKNOWN: 0 , LOW: 0 , MEDIUM: 0 , HIGH: 0 , CRITICAL: 0 )

[+] Making the output look pretty
[+] Starting the main module ============================================================
[+] Converting JSON results
[+] Writing results HTML
[+] Clean exit ============================================================
[+] Everything is done. Find the resulting HTML report in results.html
Готовый HTML с отчётом будет в ./results/results.html на хосте
Павел Канн
Старший Эксперт по безопасности Swordfish Security