Перед каждой презентацией одна и та же история: собираешь инфраструктуру вручную, молишься богам DevOps и придумываешь отговорки на случай внезапных багов. С DevSecOps особенно весело: мало установить сам продукт, нужен целый зоопарк из GitLab, Kubernetes, сканеров безопасности и систем управления уязвимостями.

Поэтому мы собрали DevSecOps-песочницу, которая разворачивается одной кнопкой: подождал 15 минут — готово. Всё крутится на одной виртуальной машине с K3s, управляется через Terraform и Cloud-init, а главное — воспроизводится с гарантированным результатом.

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

Презентация со скриншотами — прошлый век. Клиенту нужна живая демонстрация того, как продукт решает проблему у него на глазах. Но чтобы показать интеграцию DevSecOps-подходов, нужен целый пайплайн: GitLab для контроля версий, CI/CD для сборки и тестирования, связка сканеров безопасности вроде SAST, DAST и анализатора зависимостей. Плюс DefectDojo для управления уязвимостями и Kubernetes для оркестрации. Собирать этот зоопарк вручную перед каждой демонстрацией — долго и непредсказуемо. Все знают, как это бывает: «Вот сейчас покажу!» — а оно не работает.

Над проектом работала команда из К2 Кибербезопасность: я, Максим Гусев, инженер по защите ИТ-инфраструктуры, и мои коллеги — Максим Виноградов и Александр Лысенко.

Архитектура. Что у нас под капотом?

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

В итоге получилась архитектура из четырёх логических слоёв:

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

  • Сетевой уровень, который отвечает за то, чтобы все компоненты могли общаться друг с другом безопасно и эффективно (ingress controller, CNI, DNS).

  • Уровень приложений с ключевыми сервисами — GitLab, DefectDojo и тестовое бизнес-приложение.

  • Уровень безопасности — слой, реализуемый с помощью Kaspersky Container Security, пронизывающий все остальные и обеспечивающий контроль за поведением приложений.

Рассмотрим их более детально.

Базовые компоненты инфраструктуры

Фундамент стенда — K3s, облегчённая версия Kubernetes. Она хорошо подходит для компактного стенда из одной ноды. K3s упаковывает необходимые зависимости для простого создания кластера по принципу «всё включено» (batteries‑included):

  • CRI  — containerd;

  • CNI — Flannel;

  • DNS кластера — CoreDNS;

  • Ingress controller  — Traefik;

  • Load‑Balancer — ServiceLB;

  • Persistent Volume Controller  — Local‑path‑provisioner;

  • утилиты для хоста (iptables, socat и др.).

А вот с GitLab получилось интереснее: их у нас два, и каждый решает свои задачи.

  • External GitLab располагается за пределами стенда и играет роль командного центра. В нём лежат конфигурации Terraform и Cloud-init, которые разворачивают виртуальную машину. Запустил пайплайн — стенд поднялся с нуля.

  • Internal GitLab разворачивается автоматически внутри стенда — его видит клиент на демонстрации. Тут лежит исходный код тестового приложения, настроены CI/CD-пайплайны для сборки, тестирования и сканирования. Отсюда же идёт интеграция с KCS и DefectDojo.

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

Дело в том, что внутренний GitLab не должен быть пустым. Для демонстрации нужны готовые тестовые репозитории с кодом, настроенные пайплайны и история коммитов. Но как поместить это всё внутрь при автоматическом развёртывании? Просто скопировать данные из одного инстанса в другой не получится — это сложнее, чем кажется.

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

Решение нашли гибридное. Заранее подготовленные tar-архивы пушили в репозиторий. Чтобы не пушить в пустоту, так как GitLab был только что поднят, написали скрипт, который подключается к gitlab-rails console и создаёт пустые репозитории, после чего идёт пуш самих файлов в свежесозданные репозитории.

DefectDojo

Кто внедрял DevSecOps, знает: SAST выдаёт один отчёт, DAST — другой, сканер зависимостей — третий, KCS — четвёртый. Форматы и интерфейсы у всех разные, и сводить их воедино вручную — сплошное мучение.

ASOC-платформа (в нашем случае DefectDojo) собирает все данные в одном месте. Semgrep, Trivy, OWASP ZAP, KCS — сканеры отправляют результаты через API, и вместо разных отчётов получается один дашборд с полной картиной.

Kaspersky Container Security (KCS)

Мы используем Open Source для CI/CD-конвейера, но за самое критичное — безопасность контейнеров — отвечает коммерческое решение, Kaspersky Container Security. Получается баланс: Open Source даёт гибкость и возможность настроить всё под себя, а коммерческий продукт — предсказуемость и техподдержку там, где они особенно важны.

  • На этапе сборки KCS встраивается в CI-пайплайн GitLab и сканирует каждый новый Docker-образ: ищет уязвимости, вредоносный код, открытые пароли, неправильные настройки.

  • На этапе выполнения Container Security проверяет целостность контейнеров, отслеживает их поведение в реальном времени, сканирует файловые операции антивирусом и блокирует подозрительные процессы прямо внутри контейнера.

Получается двойная защита: сначала фильтруем код до попадания в прод, потом контролируем то, что уже запущено.

auth & bff

Наконец, нужно было демонстрационное приложение — сканировать «Hello, World!» как-то несерьёзно. Мы развернули связку микросервисов: auth для аутентификации и bff (backend for frontend) для работы с клиентской частью. Простое би��нес-приложение, достаточно реалистичное для демонстраций.

На нём показываем, как сканеры находят уязвимости в коде, как KCS проверяет Docker-образы и как защита работает в рантайме.

Получилась система из пяти слоёв, где каждый делает свою работу: K3s даёт платформу для оркестрации, External GitLab управляет инфраструктурой, Internal — кодом приложения, DefectDojo собирает отчёты о безопасности, KCS контролирует контейнеры от сборки до продакшена, а тестовое приложение показывает, как всё работает вместе.

Архитектура готова, осталось главное: как развернуть всю эту конструкцию быстро и без ошибок?

Магия автоматизации

Главная ценность нашего решения не в самой архитектуре, а в скорости развёртывания. Стенд от пустого облака до готового DevSecOps-окружения с GitLab, KCS, DefectDojo и тестовым приложением поднимается за 15 минут. Всё это — одной кнопкой в интерфейсе GitLab благодаря Terraform и Cloud-init.

Представьте, что нужно построить и полностью оборудовать современный дом. Тогда Terraform — это генеральный подрядчик. Он возводит коробку и подводит коммуникации: создаёт виртуальную сеть, разворачивает виртуальную машину с нужными параметрами, настраивает сетевые правила и IP-адреса. Cloud-init — бригада отделочников. Заходит внутрь с инструкциями и превращает пустые бетонные блоки в жилой дом: устанавливает пакеты, разворачивает K3s, деплоит GitLab, KCS, DefectDojo, заливает конфигурации и секреты.

Слаженная работа этих двух инструментов даёт полную автоматизацию. Разберём каждый подробнее.

Terraform: пишем инфраструктуру как код

В основе Terraform лежит концепция Infrastructure as Code (IaC). Вместо того чтобы вручную накликивать виртуальные машины в веб-интерфейсе, мы описываем желаемое состояние инфраструктуры в виде текстовых файлов. Технически это набор .tf-файлов с декларативным описанием.

Фрагмент нашего Terraform-кода:
resource "aws_instance" "vm_ans" {
# ID образа для создания экземпляра ВМ
ami 	= var.vm_template_ans
 
# Тип экземпляра (сколько CPU и RAM)
instance_type = var.vm_instance_type_ans
 
# Подключаем группу безопасности
vpc_security_group_ids = [aws_security_group.from_management_subnets.id]
 
# Добавляем публичный SSH-ключ
key_name= var.pubkey_name_ans
 
# Передаём скрипт для Cloud-init
user_data = data.cloudinit_config.cloud_config.rendered
}

Здесь мы объявляем ресурс aws_instance, задаём ему шаблон (ami), размер (instance_type) и правила доступа. Самая важная строка — последняя: user_data. Сюда Terraform передаёт инструкции для Cloud-init.

Главное преимущество такого подхода — повторяемость. Запускаешь скрипт сто раз — сто раз получаешь одинаковый результат.

Ещё важный момент: репозиторий становится достоверным источником информации о том, как выглядит инфраструктура. Конфигурации хранятся в Git, поэтому можем отслеживать изменения, откатываться к предыдущим версиям и работать с инфраструктурой как с обычным кодом.

Cloud-init: сценарий первого запуска

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

Этот сценарий превращает чистую Debian-машину в готовый стенд. Сначала он устанавливает системные пакеты: git, curl, helm и другие утилиты для дальнейшей работы. Потом скачивает и устанавливает K3s, превращая одинокую виртуальную машину в полноценный Kubernetes-кластер из одной ноды. Следом настраивает сетевые плагины — мы используем Flannel для управления сетевым взаимодействием внутри кластера. И наконец, деплоит сервисы: с помощью Helm-чартов Cloud-init последовательно разворачивает все ключевые приложения — внутренний GitLab, Kaspersky Container Security, DefectDojo и тестовое приложение auth & bff.

Вот небольшой фрагмент этого скрипта:

# Установка Helm (менеджер пакетов для Kubernetes)
echo "Устанавливаем Helm..."
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

# Установка Gitlab
helm repo add gitlab https://charts.gitlab.io/
helm install gitlab gitlab/gitlab -f /devsec-project/tf_ans/data/gitlab.yaml -n gitlab —create-namespace –debug
cd /devsec-project/tf_ans_data/gitlab_repos
chmod +x ./gitlab-init.sh
./gitlab-init-sh 

# Установка DefectDojo
helm repo update
helm dependency update .
export DJANGO_INGRESS_ENABLED=true
export DJANGO_INGRESS_ACTIVATE_TLS=true
export ALLOW_INSECURE_IMAGES=true
helm install defectdojo . \
  -f values.yaml \
  --set global.security.allowInsecureImages=${ALLOW_INSECURE_IMAGES} \
  --set django.ingress.enabled=${DJANGO_INGRESS_ENABLED} \
  --set django.ingress.activateTLS=${DJANGO_INGRESS_ACTIVATE_TLS} \
  --set createSecret=true \
  --set createRedisSecret=true \
  --set createPostgresqlSecret=true

Как это работает вместе

Запуск пайплайна
Запуск пайплайна

Теперь сложим пазл. Как выглядит этот процесс развёртывания?

  1. Нажатие кнопки. Инженер заходит во внешний (управляющий) GitLab, открывает нужный CI/CD-пайплайн и нажимает «Run Pipeline».

  2. Запускается Terraform. GitLab CI Runner запускает джобу, которая выполняет команду terraform apply.

  3. Terraform строит коробку. Он обращается к API облачного провай��ера и, согласно коду, создаёт виртуальную машину со всеми сопутствующими ресурсами. В момент создания он передаёт ей наш большой user_data-скрипт.

  4. Пробуждение Cloud-init. Виртуальная машина впервые загружается. Система видит, что ей подсунули user_data, и передаёт управление Cloud-init.

  5. Cloud-init настраивает дом. Cloud-init начинает методично, шаг за шагом выполнять наш сценарий: ставит K3s, Helm, разворачивает GitLab, KCS, DefectDojo. Этот процесс можно наблюдать в реальном времени через консоль виртуальной машины.

  6. Стенд готов. Через 10 минут Cloud-init завершает работу. Все поды в Kubernetes запущены, все сервисы доступны по своим адресам. На выходе мы получаем IP-адрес стенда и ключи доступа.

Всё работает чисто, быстро и предсказуемо. Нужно внести изменения — меняем код в Git. Стенд больше не нужен — запускаем terraform destroy, и вся инфраструктура аккуратно удаляется, не оставляя мусора.

Прогоняем код по пайплайну

Представим, что добавляем новую фичу в тестовое приложение. Пишем код, тестируем локально, делаем привычное: git add ., git commit -m "feat: add new user profile endpoint", git push origin main.

В традиционной разработке код улетает в репозиторий, а про безопасность вспоминают через пару недель, когда исправлять уже поздно и дорого. А на нашем стенде коммит попадает на внутренний GitLab — и автоматически запускается цепочка проверок.

Шаг 1: сборка и первые проверки

Как только GitLab принял коммит, запускается CI/CD-пайплайн. Сначала проходят проверки синтаксиса и линтеры. Они убеждаются, что код написан корректно и соответствует стандартам команды. Исходный код со всеми зависимостями упаковывается в Docker-образ, но перед отправкой нужно убедиться, что всё внутри безопасно. Начинается первый этап проверок безопасности — тот самый Shift Left Security.

Semgrep проводит статический анализ и ищет потенциальные уязвимости: SQL-инъекции, межсайтовый скриптинг (XSS), небезопасные библиотеки и другие типичные ошибки. Это самый ранний и дешёвый способ поймать баги, пока они ещё не попали в продакшен.

Дальше проверяем зависимости через SCA (Software Composition Analysis). Здесь работает Trivy — сканирует все зависимости проекта из package.json, requirements.txt и подобных файлов, а потом сравнивает их версии с базой известных уязвимостей CVE.

SAST и SCA хороши, но некоторые уязвимости проявляются только в рантайме. Для этого подключается DAST — OWASP ZAP. После сборки, но перед деплоем пайплайн разворачивает приложение в изолированной тестовой среде. ZAP проводит динамический анализ — пытается найти лазейки, подобрать пароли, передать некорректные данные.

Этот тройной кордон отсекает большинство стандартных проблем безопасности.
Этот тройной кордон отсекает большинство стандартных проблем безопасности.

Шаг 2: образ отправляется в KCS

После того как код прошёл через сито опенсорсных сканеров, собранный Docker-образ отправляется на углублённую проверку в Kaspersky Container Security (KCS).

KCS подключается напрямую к CI-пайплайну и проверяет образ гораздо тщательнее стандартных SCA-инструментов. Сначала сканирует CVE в системных пакетах и библиотеках — база у него шире, механизмы обнаружения более продвинутые. Потом KCS прогоняет образ через антивирусное ядро Касперского. Дальше копается в слоях образа и вытаскивает конфиденциальные данные, которые там могли забыть: пароли, API-ключи, приватные SSH-ключи. И наконец, проверяет конфигурацию — не запускается ли контейнер под root, не открыты ли лишние порты, соответствует ли всё best practices безопасности.

Шаг 3: вердикт

Теперь пайплайн должен принять решение, годится коммит для деплоя или нет?

Здесь включается Security Gate — набор правил, определяющих порог допустимого риска, который настраивается в GitLab CI. Например, можно задать такое условие: пайплайн проходит успешно, если в образе нет критических и высоких уязвимостей, а средних — не больше десяти.

  • Первый сценарий — всё чисто. Сканеры отчитались, что образ безопасен или риски в пределах нормы. Образ публикуется в GitLab Registry и автоматически деплоится в K3s-кластер. Разработчики получают уведомление об успешной публикации.

  • Второй сценарий — провал. KCS находит в образе критическую уязвимость в одной из библиотек. Security Gate захлопывается, пайплайн останавливается. Разработчики получают сообщение: «Пайплайн остановлен на проверке безопасности, обнаружена критическая уязвимость CVE-2025-XXXX, подробности по ссылке».

Шаг 4: результаты отправляются в DefectDojo

Независимо от вердикта Security Gate результаты всех сканирований отправляются по API в DefectDojo. Там они агрегируются, дедуплицируются и выводятся на единый дашборд. Инженер по безопасности заходит и видит всю картину целиком, без переключения между десятком вкладок. Видно, какие уязвимости появляются чаще всего.

Шаг 5: жизнь после деплоя

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

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

Антивирусный контроль проверяет все файловые операции внутри контейнера — попытки записать или запустить вредоносный файл блокируются.

Поведенческий анализ и автопрофилирование записывают нормальное поведение контейнера: какие процессы запускает, в какие файлы пишет, с кем общается по сети. Из этого складывается автопрофиль — цифровой отпечаток контейнера. Любое отклонение от профиля также вызывает тревогу или блокируется на месте. Сетевой контроль следит за соединениями и позволяет настроить правила — контейнер сможет общаться только с разрешёнными сервисами.

Кому и зачем это нужно?

В процессе работы мы поняли, что решаем не одну, а сразу три важные задачи.

  • Первая и самая очевидная — живая демонстрация. Стенд показывает весь DevSecOps в действии: реальный путь от коммита разработчика до работающего приложения в Kubernetes. KCS встраивается в CI/CD, блокирует уязвимые образы ещё на сборке, анализирует поведение контейнеров в рантайме, а все отчёты стекаются в DefectDojo.

  • Вторая — создание песочницы, где наши и клиентские инженеры смогут разбираться, как работают SAST, DAST и SCA, учиться писать политики безопасности для контейнеров и спокойно экспериментировать.

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

Лучшие инструменты — те, которыми пользуются. Мы создали живой и полезный стенд, который решает реальные проблемы: экономит время инженеров, помогает бизнесу принимать решения и делает DevSecOps доступнее. Теперь на вопрос клиента «Покажете завтра?» — мы отвечаем: «Конечно. Вам с какими тестовыми уязвимостями?»

Комментарии (0)