Работоспособность любого приложения может быть подвержена угрозам: от сбоев в работе до кражи персональных данных. С этими рисками следует работать через регулярные и комплексные проверки кода на уязвимости, которые должны быть полностью автоматизированными.
Несмотря на то, что тема актуальная, в интернете до сих пор сложно найти практические примеры, которые бы позволили построить независимый конвейер, не привязанный к системе контроля версий.
Меня зовут Алексей Исламов, я администратор СИБ в Точка Банк. В статье предлагаю готовый вариант реализации такой системы из open‑source инструментов, которым может воспользоваться каждый.
О чём расскажу:
Под какие условия создавался конвейер.
Как мы его собирали, какие инструменты использовали;
Как автоматизировали весь процесс.
Как работаем с результатами сканов безопасности, чтобы сделать процесс устранения уязвимостей удобным и безболезненным для всех.
Проблематика и требования к конвейеру
В идеальном мире проверки закладываются на моменте выбора системы контроля версий и выстраивания вообще всего флоу разработки. Но в реальной жизни всё совсем не так: идея внедрить проверки безопасности кода чаще всего появляется, когда процессы уже отстроены. И если вы привыкли работать без таких проверок, то их внедрение в процесс разработки — история сложная и трудозатратная. И чем команда больше, тем сложнее это реализовать без рисков остановки или перестраивания процессов разработки.
Сложно, но делать нужно. мы столкнулись именно с такой ситуацией, когда пришли к необходимости настроить автоматизированное решение.
Итак, задача № 1 — внедрить конвейер так, чтобы проверки ничего не сломали и не стали помехой для команд разработки. У нас используются две системы контроля версий (СКВ): Gitlab и Bitbucket, а это осложняет процесс встраивания и приведения проверки кода к единообразию.
Задача № 2 — найти решение, которое будет одинаково хорошо работать с обеими системами и будет от них независимо. На российском рынке есть готовые продукты, но они дают меньше контроля и возможности кастомизировать решение.
Поэтому, задача № 3 — построить именно open‑source решение, которое бы дало свободу в настройке под себя.
Нашими сервисами пользуются большое количество людей. Любой сбой, приостановка работы сервисов осложнит жизнь их пользователям, а для нас принесет репутационные и финансовые риски. Например, в приложении нашлась бага, разработчик подготовил фикс, но весь процесс выкатки встал на моменте сканирования кода — не сработал какой‑то пайплайн. Вместо того, чтобы чинить приложение и отдавать пользователям версию без бага, команда вынуждена разбираться с упавшим пайплайном.
Отсюда задача № 4 — процесс проверки должен быть выстроен изолированно, чтобы в случае ошибок на стороне конвейера выкатывание релизов на прод не останавливалось.
В общем, нам нужно минимально вмешиваться в существующие процессы разработки и не нагружать команды задачами актуализации и проверки пайплайнов в части сканирования кода на уязвимости.
Как собирали конвейер
Обеспечить команду информацией об уязвимостях в проекте нужно на самом раннем этапе, чтобы у разработчика была возможность править код на ходу. Опуская подробности, выделю такие этапы разработки:
разработчик пишет код;
отправляет его в репозиторий;
код проходит различные проверки, согласования и так далее;
если всё хорошо — код уходит в прод.

Зачастую вариантов для реализации инструментальной проверки кода на безопасность два:
Базовая реализация модели конвейера, в которой используется Gitlab CI и встроенный функционал (или тоже самое, но в другой системе контроля версий). Такая схема начинает работать на этапе 2 или 3.
Запуск отдельных сканеров в ручном режиме. Такой вариант возможно внедрить и на 1-м этапе, но это трудозатратно и нормально поддерживать не получится.
В общем, ни один из этих вариантов нам не подходил.
Из‑за использования двух систем контроля версий мы изначально задумывали систему не как набор разрозненных компонентов отдельно под каждую СКВ, а как полноценное и общее решение.
Нам были нужны:
Сами сканеры, которые проверяют код.
Система, которая умеет принимать отчёты от этих сканеров, отображать их в человекочитаемом виде и позволяет каким‑то образом работать с результатами.
Решение, которое позволит объединить, связать все компоненты вместе и заставит всё это работать.
Как я писал в самом начале, в интернете готовых решений для такой задачи особо никто не описывает, поэтому пришлось анализировать. Мы ориентировались на нужную нам функциональность, распространённость использования и популярность. В результате пришли к удовлетворяющему наши требования набору инструментов и схеме работы.
В качестве системы автоматизации решили использовать Jenkins, который одинаково хорошо работает с обеими системами контроля версий. Jenkins, развёрнутый на железе, скачивает заданный репозиторий и запускает контейнеры со сканерами: поиск секретов Gitleaks, сканер для определения уязвимых библиотек (SCA сканер) Trivy в паре с Cdxgen и статический анализатор Semgrep.
Подробно про сканеры можно посмотреть по ссылкам:
После того, как сканеры сформировали отчёты, они отправляются в систему анализа уязвимостей DefectDOJO — она умеет принимать отчёты от большого количества различных инструментов и имеет в арсенале другие плюшки для работы с уязвимостями.
Изначально мы строили систему, чтобы протестировать сам подход и посмотреть, что из этого получится. Поэтому мы выбрали небольшую группу сервисов и в ручном режиме выстроили для них конвейер: хардкодили какие‑то переменные, ссылки, относящиеся к конкретному проекту, руками создавали проекты в DefectDOJO. В общем, просто проверяли, как всё работает, плюс, имея доступ к репозиториям, сами же смотрели, насколько «чистыми» были результаты сканеров. Сервисы выбирали по количеству коммитов (примерно 1–2 в месяц), чтобы можно было внимательно проверить результаты сканов и обсудить с разработчиками.
В результате, в процесс разработки встроилась целая система, о которой команды на момент этого тестового периода даже не знали. С учётом флоу проверки кода добавился этап 3, и процесс разработки стал выглядеть так:
Разработчик пишет код.
Отправляет его в репозиторий.
Получает результаты проверки своего кода на наличие уязвимостей. Здесь можно посмотреть результаты отчётов сканеров и поработать с ними, чтобы на следующие этапы перешёл уже исправленный код.
Код проходит различные проверки, согласования и так далее
Если всё хорошо — код уходит в прод.
Разработка первого подхода заняла около 3–4 месяцев, так как со всем пришлось разбираться с нуля. Сначала я развернул систему на своем домашнем стенде с использованием общедоступных репозиториев для тестов. После того, как убедился, что конвейер рабочий, развернул на контуре в тестовой среде.
Автоматизируем конвейер
Первые результаты были ожидаемы: мы знали, что делают сканеры, и что по итогу мы получим отчёты, в которых реальные находки перемешаны с большим количеством фолзов (false positive), как и у любого сканера без написанных правил. Начали понемногу работать с результатами, налаживать процесс взаимодействия с выбранными командами и в итоге окончательно решили внедрять конвейер.
Количество сервисов у нас около 1000, масштабироваться в ручном режиме было бы невозможно. Так что вопрос об автоматизации процесса встал перед нами сразу. Первое, что мы сделали, — начали постепенно добавлять триггеры к каждому репозиторию в Gitlab и Bitbucket, чтобы они запускали сканы по заданному действию в репозитории. Так мы автоматизировали запуск и получение результатов сканирования, но ссылку под каждый репозиторий, которая запускает пайплайны проверок, а также проекты в DefectDOJO, всё равно приходилось создавать руками.
Начали решать проблемы по очереди:
Внимательно прочитали описание метода, который принимает запросы на импорт результатов сканирования в DefectDOJO, и обнаружили пару параметров, которые позволяют создавать проекты автоматически. В первый раз мы эту важную информацию пропустили.
Занялись автоматизацией создания пайплайна в Jenkins. Тут без полного изменения структуры пайплайнов и написания скриптов не обошлось:
Выстроили новую структуру пайплайнов: написали универсальные шаблоны под каждый сканер, которые будут копироваться в Jenkins‑каталог определённого репозитория, и по одному универсальному стартовому пайплайну для Gitlab и Bitbucket, которые будут запускать цепочку сканов в нужном каталоге Jenkins.
Использовали возможности плагинов Jenkins, которые позволяют запускать пайплайны по триггеру. В Gitlab и Bitbucket нашли возможность задать общий триггер для всех репозиториев — системный вебхук, который посылает запрос с данными о репозитории на указанный адрес.
В этом запросе Gitlab и Bitbucket отправляют информацию о репозитории и в Jenkins она записывается в переменные окружения. Учитывая это, мы написали скрипт, который на основании данных о репозитории создаёт нужную Jenkins для корректной работы структуру каталогов, которая уже содержит в себе шаблоны сканеров.
Объясню, как это работает, на примере.
Разработчик пушит код в Gitlab, срабатывает триггер, и в Jenkins отправляется запрос.
Далее Jenkins парсит полученные данные и запускает скрипт, который создаёт нужную структуру для запуска пайплайнов сканов проекта, если её ещё нет.
После Jenkins по известному пути каталога триггерит запуск первого пайплайна в цепочке, передавая ему значения, полученные из Gitlab, для того, чтобы отчёты попали в нужный проект в DefectDOJO, который тоже создастся автоматически.
В итоге, имея всего пять пайплайнов и парочку скриптов, нам удалось полностью автоматизировать процесс инструментальной проверки кода.
На всю автоматизацию ушёл примерно месяц — занимался я этим по вечерам, так как помимо конвейера были основные задачи. Сказалась и нехватка знаний: много чего приходилось гуглить, анализировать, учиться. Сейчас, с учётом приобретенных знаний и опыта, на развертывание и автоматизацию такой системы уйдёт примерно неделя.
Плюсы и минусы системы
Главным плюсом нашей реализации стала её универсальность и независимость от системы контроля версий, что особенно актуально с учётом импортозамещения. Мы не привязываемся к какой‑либо платформе, конвейер работает независимо и всё, что нужно для запуска сканов, — это пост‑запрос с минимумом параметров.
Наш конвейер неожиданно оказался полезным ещё и юридической службе. После демонстрации на одном из внутренних митапов коллеги узнали, что сканеры могут собирать информацию о лицензиях используемых компонентов.
Так как реализация конвейера была изолированной, мы смогли безболезненно вставить несколько шагов в пайплайн проверки и сканирования кода: собирать инфу о лицензиях компонентов и отправлять полученные сведения в специально написанный для этого сервис.
Однако, несмотря на все плюсы есть и минусы.
Первый минус — ложные срабатывания. Мы используем open source решения, от этого никуда не деться.
Второй минус — неполный охват. Базы сканеров могут не содержать каких‑то инструкций, шаблонов, чтобы охватывать все возможные уязвимости.
Оба минуса нивелируются гибкостью: чтобы решить обе проблемы достаточно написать свои шаблоны и инструкции и добавить их в схему проверок.
Что дальше делать с результатами проверок и как общаться с разработчиками
То, как вы в дальнейшем работаете с результатами проверок, зависит от того, как строятся взаимоотношения команд безопасности и разработки.
Есть варианты, при которых безопасность достаточно жёстко выставляет приоритеты на исправление уязвимостей команде разработки и может блокировать выкатку релизов. Но наша структура управления такие истории исключает. Поэтому наш вариант — договариваться и выстраивать качественную коммуникацию с разработкой.
Что мы для этого делаем:
На внутренних митапах рассказываем о том, к чему могут привести уязвимости, как работает наш конвейер, демонстрируем, что он не мешает процессу разработки.
На конкретных примерах найденных уязвимостей демонстрируем возможные последствия их реализации, показывая разработчикам необходимость и важность их исправления.
Регулярно собираем ОС с команд, интересуемся, что им удобно, что нет, какая информация им ещё требуется и дорабатываем систему для максимального удобства.
Такая коммуникация позволяет найти взаимопонимание — команды видят, что мы не ставим палки в колеса, а реально помогаем улучшить их код и сделать продукт ещё круче и надёжнее. Ну и другая сторона — наше решение тоже улучшается. Благодаря собранной ОС от команд у нас появились идеи полезных фич:
Ручной запуск сканов для тех, кто хочет запускать сканирование ещё на предстейджевых ветках.
Информирование в рабочем мессенджере или почте о завершении сканирования сразу со ссылкой на результаты.
Получать ссылку на результаты или даже сами отчёты в комментариях к коммиту, чтобы обрабатывать результаты не покидая рабочего пространства.
Бонусная часть: как настроить отправку результатов сканов докер-образов встроенного в Harbor сканера Trivy в DefectDOJO
Harbor из коробки разворачивается вместе со встроенным в него сканером Trivy и позволяет проводить сканирование каждого загружаемого образа. Результаты красиво отображаются в веб интерфейсе, но отправить их куда‑то ещё или просто выгрузить возможности нет. Но мы нашли решение.
Залезли в контейнер Trivy, посмотрели, как он работает, куда пишет файлики отчётов, что с ними происходит во время сканирования и во время окончания, получили инфу и написали несколько скриптов.
Во время старта контейнера в entrypoint.sh добавили наши скрипты для работы в фоне.
C помощью inotifywait.sh следим за каталогом и создаём ссылки на репорты: когда создаётся новый отчёт, этот скрипт его подхватывает и создает ссылку уже в другом каталоге, который мы сами создали.
Когда файл записался полностью и отправился в Harbor, он удаляется и на это действие смотрит второй inotifywaitdel.sh — запускает скрипт парсер, который парсит созданный файлик и отправляет данные в DefectDOJO.
После отправки файл удаляется, чтобы не занимать место.
В качестве заключения
Собственная реализация такого проекта, безусловно, сложная и трудозатратная история — нужно много анализировать, тестировать, адаптировать инструменты. Мы этот путь прошли и будем рады, если кто‑то воспользуется нашим решением и реализует его у себя гораздо быстрее и проще.
Все материалы для развёртывания конвейера вы можете найти по ссылке.
Действуйте и всё обязательно получится! И не бойтесь экспериментировать, смотреть на работу сканеров под разными углами и использовать новые инструменты.